1/* SNMP support
2 * Copyright (C) 1999 Kunihiro Ishiguro <kunihiro@zebra.org>
3 *
4 * This file is part of GNU Zebra.
5 *
6 * GNU Zebra is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
9 * later version.
10 *
11 * GNU Zebra is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GNU Zebra; see the file COPYING.  If not, write to the Free
18 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 * 02111-1307, USA.
20 */
21
22#include <zebra.h>
23
24#if defined HAVE_SNMP && defined SNMP_SMUX
25#include <net-snmp/net-snmp-config.h>
26#include <net-snmp/net-snmp-includes.h>
27
28#include "log.h"
29#include "thread.h"
30#include "linklist.h"
31#include "command.h"
32#include <lib/version.h>
33#include "memory.h"
34#include "sockunion.h"
35#include "smux.h"
36
37#define SMUX_PORT_DEFAULT 199
38
39#define SMUXMAXPKTSIZE    1500
40#define SMUXMAXSTRLEN      256
41
42#define SMUX_OPEN       (ASN_APPLICATION | ASN_CONSTRUCTOR | 0)
43#define SMUX_CLOSE      (ASN_APPLICATION | ASN_PRIMITIVE | 1)
44#define SMUX_RREQ       (ASN_APPLICATION | ASN_CONSTRUCTOR | 2)
45#define SMUX_RRSP       (ASN_APPLICATION | ASN_PRIMITIVE | 3)
46#define SMUX_SOUT       (ASN_APPLICATION | ASN_PRIMITIVE | 4)
47
48#define SMUX_GET        (ASN_CONTEXT | ASN_CONSTRUCTOR | 0)
49#define SMUX_GETNEXT    (ASN_CONTEXT | ASN_CONSTRUCTOR | 1)
50#define SMUX_GETRSP     (ASN_CONTEXT | ASN_CONSTRUCTOR | 2)
51#define SMUX_SET	(ASN_CONTEXT | ASN_CONSTRUCTOR | 3)
52#define SMUX_TRAP	(ASN_CONTEXT | ASN_CONSTRUCTOR | 4)
53
54#define SMUX_MAX_FAILURE 3
55
56/* SNMP tree. */
57struct subtree
58{
59  /* Tree's oid. */
60  oid name[MAX_OID_LEN];
61  u_char name_len;
62
63  /* List of the variables. */
64  struct variable *variables;
65
66  /* Length of the variables list. */
67  int variables_num;
68
69  /* Width of the variables list. */
70  int variables_width;
71
72  /* Registered flag. */
73  int registered;
74};
75
76#define min(A,B) ((A) < (B) ? (A) : (B))
77
78enum smux_event {SMUX_SCHEDULE, SMUX_CONNECT, SMUX_READ};
79
80void smux_event (enum smux_event, int);
81
82
83/* SMUX socket. */
84int smux_sock = -1;
85
86/* SMUX subtree list. */
87struct list *treelist;
88
89/* SMUX oid. */
90oid *smux_oid = NULL;
91size_t smux_oid_len;
92
93/* SMUX password. */
94char *smux_passwd = NULL;
95
96/* SMUX read threads. */
97struct thread *smux_read_thread;
98
99/* SMUX connect thrads. */
100struct thread *smux_connect_thread;
101
102/* SMUX debug flag. */
103int debug_smux = 0;
104
105/* SMUX failure count. */
106int fail = 0;
107
108/* SMUX node. */
109static struct cmd_node smux_node =
110{
111  SMUX_NODE,
112  ""                            /* SMUX has no interface. */
113};
114
115/* thread master */
116static struct thread_master *master;
117
118static int
119oid_compare_part (oid *o1, int o1_len, oid *o2, int o2_len)
120{
121  int i;
122
123  for (i = 0; i < min (o1_len, o2_len); i++)
124    {
125      if (o1[i] < o2[i])
126	return -1;
127      else if (o1[i] > o2[i])
128	return 1;
129    }
130  if (o1_len < o2_len)
131    return -1;
132
133  return 0;
134}
135
136static void
137smux_oid_dump (const char *prefix, const oid *oid, size_t oid_len)
138{
139  unsigned int i;
140  int first = 1;
141  char buf[MAX_OID_LEN * 3];
142
143  buf[0] = '\0';
144
145  for (i = 0; i < oid_len; i++)
146    {
147      sprintf (buf + strlen (buf), "%s%d", first ? "" : ".", (int) oid[i]);
148      first = 0;
149    }
150  zlog_debug ("%s: %s", prefix, buf);
151}
152
153static int
154smux_socket (void)
155{
156  int ret;
157#ifdef HAVE_IPV6
158  struct addrinfo hints, *res0, *res;
159  int gai;
160#else
161  struct sockaddr_in serv;
162  struct servent *sp;
163#endif
164  int sock = 0;
165
166#ifdef HAVE_IPV6
167  memset(&hints, 0, sizeof(hints));
168  hints.ai_family = PF_UNSPEC;
169  hints.ai_socktype = SOCK_STREAM;
170  gai = getaddrinfo(NULL, "smux", &hints, &res0);
171  if (gai == EAI_SERVICE)
172    {
173      char servbuf[NI_MAXSERV];
174      sprintf(servbuf,"%d",SMUX_PORT_DEFAULT);
175      servbuf[sizeof (servbuf) - 1] = '\0';
176      gai = getaddrinfo(NULL, servbuf, &hints, &res0);
177    }
178  if (gai)
179    {
180      zlog_warn("Cannot locate loopback service smux");
181      return -1;
182    }
183  for(res=res0; res; res=res->ai_next)
184    {
185      if (res->ai_family != AF_INET
186#ifdef HAVE_IPV6
187	  && res->ai_family != AF_INET6
188#endif /* HAVE_IPV6 */
189	  )
190	continue;
191
192      sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
193      if (sock < 0)
194	continue;
195      sockopt_reuseaddr (sock);
196      sockopt_reuseport (sock);
197      ret = connect (sock, res->ai_addr, res->ai_addrlen);
198      if (ret < 0)
199	{
200	  close(sock);
201	  sock = -1;
202	  continue;
203	}
204      break;
205    }
206  freeaddrinfo(res0);
207  if (sock < 0)
208    zlog_warn ("Can't connect to SNMP agent with SMUX");
209#else
210  sock = socket (AF_INET, SOCK_STREAM, 0);
211  if (sock < 0)
212    {
213      zlog_warn ("Can't make socket for SNMP");
214      return -1;
215    }
216
217  memset (&serv, 0, sizeof (struct sockaddr_in));
218  serv.sin_family = AF_INET;
219#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
220  serv.sin_len = sizeof (struct sockaddr_in);
221#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
222
223  sp = getservbyname ("smux", "tcp");
224  if (sp != NULL)
225    serv.sin_port = sp->s_port;
226  else
227    serv.sin_port = htons (SMUX_PORT_DEFAULT);
228
229  serv.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
230
231  sockopt_reuseaddr (sock);
232  sockopt_reuseport (sock);
233
234  ret = connect (sock, (struct sockaddr *) &serv, sizeof (struct sockaddr_in));
235  if (ret < 0)
236    {
237      close (sock);
238      smux_sock = -1;
239      zlog_warn ("Can't connect to SNMP agent with SMUX");
240      return -1;
241    }
242#endif
243  return sock;
244}
245
246static void
247smux_getresp_send (oid objid[], size_t objid_len, long reqid, long errstat,
248		   long errindex, u_char val_type, void *arg, size_t arg_len)
249{
250  u_char buf[BUFSIZ];
251  u_char *ptr, *h1, *h1e, *h2, *h2e;
252  size_t len, length;
253
254  ptr = buf;
255  len = BUFSIZ;
256  length = len;
257
258  if (debug_smux)
259    {
260      zlog_debug ("SMUX GETRSP send");
261      zlog_debug ("SMUX GETRSP reqid: %ld", reqid);
262    }
263
264  h1 = ptr;
265  /* Place holder h1 for complete sequence */
266  ptr = asn_build_sequence (ptr, &len, (u_char) SMUX_GETRSP, 0);
267  h1e = ptr;
268
269  ptr = asn_build_int (ptr, &len,
270		       (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
271		       &reqid, sizeof (reqid));
272
273  if (debug_smux)
274    zlog_debug ("SMUX GETRSP errstat: %ld", errstat);
275
276  ptr = asn_build_int (ptr, &len,
277		       (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
278		       &errstat, sizeof (errstat));
279  if (debug_smux)
280    zlog_debug ("SMUX GETRSP errindex: %ld", errindex);
281
282  ptr = asn_build_int (ptr, &len,
283		       (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
284		       &errindex, sizeof (errindex));
285
286  h2 = ptr;
287  /* Place holder h2 for one variable */
288  ptr = asn_build_sequence (ptr, &len,
289			   (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR),
290			   0);
291  h2e = ptr;
292
293  ptr = snmp_build_var_op (ptr, objid, &objid_len,
294			   val_type, arg_len, arg, &len);
295
296  /* Now variable size is known, fill in size */
297  asn_build_sequence(h2,&length,(u_char)(ASN_SEQUENCE|ASN_CONSTRUCTOR),ptr-h2e);
298
299  /* Fill in size of whole sequence */
300  asn_build_sequence(h1,&length,(u_char)SMUX_GETRSP,ptr-h1e);
301
302  if (debug_smux)
303    zlog_debug ("SMUX getresp send: %td", (ptr - buf));
304
305  send (smux_sock, buf, (ptr - buf), 0);
306}
307
308static u_char *
309smux_var (u_char *ptr, size_t len, oid objid[], size_t *objid_len,
310          size_t *var_val_len,
311          u_char *var_val_type,
312          void **var_value)
313{
314  u_char type;
315  u_char val_type;
316  size_t val_len;
317  u_char *val;
318
319  if (debug_smux)
320    zlog_debug ("SMUX var parse: len %zd", len);
321
322  /* Parse header. */
323  ptr = asn_parse_header (ptr, &len, &type);
324
325  if (debug_smux)
326    {
327      zlog_debug ("SMUX var parse: type %d len %zd", type, len);
328      zlog_debug ("SMUX var parse: type must be %d",
329		 (ASN_SEQUENCE | ASN_CONSTRUCTOR));
330    }
331
332  /* Parse var option. */
333  *objid_len = MAX_OID_LEN;
334  ptr = snmp_parse_var_op(ptr, objid, objid_len, &val_type,
335			  &val_len, &val, &len);
336
337  if (var_val_len)
338    *var_val_len = val_len;
339
340  if (var_value)
341    *var_value = (void*) val;
342
343  if (var_val_type)
344    *var_val_type = val_type;
345
346  /* Requested object id length is objid_len. */
347  if (debug_smux)
348    smux_oid_dump ("Request OID", objid, *objid_len);
349
350  if (debug_smux)
351    zlog_debug ("SMUX val_type: %d", val_type);
352
353  /* Check request value type. */
354  if (debug_smux)
355  switch (val_type)
356    {
357    case ASN_NULL:
358      /* In case of SMUX_GET or SMUX_GET_NEXT val_type is set to
359         ASN_NULL. */
360      zlog_debug ("ASN_NULL");
361      break;
362
363    case ASN_INTEGER:
364      zlog_debug ("ASN_INTEGER");
365      break;
366    case ASN_COUNTER:
367    case ASN_GAUGE:
368    case ASN_TIMETICKS:
369    case ASN_UINTEGER:
370      zlog_debug ("ASN_COUNTER");
371      break;
372    case ASN_COUNTER64:
373      zlog_debug ("ASN_COUNTER64");
374      break;
375    case ASN_IPADDRESS:
376      zlog_debug ("ASN_IPADDRESS");
377      break;
378    case ASN_OCTET_STR:
379      zlog_debug ("ASN_OCTET_STR");
380      break;
381    case ASN_OPAQUE:
382    case ASN_NSAP:
383    case ASN_OBJECT_ID:
384      zlog_debug ("ASN_OPAQUE");
385      break;
386    case SNMP_NOSUCHOBJECT:
387      zlog_debug ("SNMP_NOSUCHOBJECT");
388      break;
389    case SNMP_NOSUCHINSTANCE:
390      zlog_debug ("SNMP_NOSUCHINSTANCE");
391      break;
392    case SNMP_ENDOFMIBVIEW:
393      zlog_debug ("SNMP_ENDOFMIBVIEW");
394      break;
395    case ASN_BIT_STR:
396      zlog_debug ("ASN_BIT_STR");
397      break;
398    default:
399      zlog_debug ("Unknown type");
400      break;
401    }
402  return ptr;
403}
404
405/* NOTE: all 3 functions (smux_set, smux_get & smux_getnext) are based on
406   ucd-snmp smux and as such suppose, that the peer receives in the message
407   only one variable. Fortunately, IBM seems to do the same in AIX. */
408
409static int
410smux_set (oid *reqid, size_t *reqid_len,
411          u_char val_type, void *val, size_t val_len, int action)
412{
413  int j;
414  struct subtree *subtree;
415  struct variable *v;
416  int subresult;
417  oid *suffix;
418  size_t suffix_len;
419  int result;
420  u_char *statP = NULL;
421  WriteMethod *write_method = NULL;
422  struct listnode *node, *nnode;
423
424  /* Check */
425  for (ALL_LIST_ELEMENTS (treelist, node, nnode, subtree))
426    {
427      subresult = oid_compare_part (reqid, *reqid_len,
428                                    subtree->name, subtree->name_len);
429
430      /* Subtree matched. */
431      if (subresult == 0)
432        {
433          /* Prepare suffix. */
434          suffix = reqid + subtree->name_len;
435          suffix_len = *reqid_len - subtree->name_len;
436          result = subresult;
437
438          /* Check variables. */
439          for (j = 0; j < subtree->variables_num; j++)
440            {
441              v = &subtree->variables[j];
442
443              /* Always check suffix */
444              result = oid_compare_part (suffix, suffix_len,
445                                         v->name, v->namelen);
446
447              /* This is exact match so result must be zero. */
448              if (result == 0)
449                {
450                  if (debug_smux)
451                    zlog_debug ("SMUX function call index is %d", v->magic);
452
453                  statP = (*v->findVar) (v, suffix, &suffix_len, 1,
454					 &val_len, &write_method);
455
456                  if (write_method)
457                    {
458                      return (*write_method)(action, val, val_type, val_len,
459					     statP, suffix, suffix_len);
460                    }
461                  else
462                    {
463                      return SNMP_ERR_READONLY;
464                    }
465                }
466
467              /* If above execution is failed or oid is small (so
468                 there is no further match). */
469              if (result < 0)
470                return SNMP_ERR_NOSUCHNAME;
471            }
472        }
473    }
474  return SNMP_ERR_NOSUCHNAME;
475}
476
477static int
478smux_get (oid *reqid, size_t *reqid_len, int exact,
479	  u_char *val_type,void **val, size_t *val_len)
480{
481  int j;
482  struct subtree *subtree;
483  struct variable *v;
484  int subresult;
485  oid *suffix;
486  size_t suffix_len;
487  int result;
488  WriteMethod *write_method=NULL;
489  struct listnode *node, *nnode;
490
491  /* Check */
492  for (ALL_LIST_ELEMENTS (treelist, node, nnode,subtree))
493    {
494      subresult = oid_compare_part (reqid, *reqid_len,
495				    subtree->name, subtree->name_len);
496
497      /* Subtree matched. */
498      if (subresult == 0)
499	{
500	  /* Prepare suffix. */
501	  suffix = reqid + subtree->name_len;
502	  suffix_len = *reqid_len - subtree->name_len;
503	  result = subresult;
504
505	  /* Check variables. */
506	  for (j = 0; j < subtree->variables_num; j++)
507	    {
508	      v = &subtree->variables[j];
509
510	      /* Always check suffix */
511	      result = oid_compare_part (suffix, suffix_len,
512					 v->name, v->namelen);
513
514	      /* This is exact match so result must be zero. */
515	      if (result == 0)
516		{
517		  if (debug_smux)
518		    zlog_debug ("SMUX function call index is %d", v->magic);
519
520		  *val = (*v->findVar) (v, suffix, &suffix_len, exact,
521					val_len, &write_method);
522
523		  /* There is no instance. */
524		  if (*val == NULL)
525		    return SNMP_NOSUCHINSTANCE;
526
527		  /* Call is suceed. */
528		  *val_type = v->type;
529
530		  return 0;
531		}
532
533	      /* If above execution is failed or oid is small (so
534                 there is no further match). */
535	      if (result < 0)
536		return SNMP_ERR_NOSUCHNAME;
537	    }
538	}
539    }
540  return SNMP_ERR_NOSUCHNAME;
541}
542
543static int
544smux_getnext (oid *reqid, size_t *reqid_len, int exact,
545	      u_char *val_type,void **val, size_t *val_len)
546{
547  int j;
548  oid save[MAX_OID_LEN];
549  int savelen = 0;
550  struct subtree *subtree;
551  struct variable *v;
552  int subresult;
553  oid *suffix;
554  size_t suffix_len;
555  int result;
556  WriteMethod *write_method=NULL;
557  struct listnode *node, *nnode;
558
559
560  /* Save incoming request. */
561  oid_copy (save, reqid, *reqid_len);
562  savelen = *reqid_len;
563
564  /* Check */
565  for (ALL_LIST_ELEMENTS (treelist, node, nnode, subtree))
566    {
567      subresult = oid_compare_part (reqid, *reqid_len,
568				    subtree->name, subtree->name_len);
569
570      /* If request is in the tree. The agent has to make sure we
571         only receive requests we have registered for. */
572      /* Unfortunately, that's not true. In fact, a SMUX subagent has to
573         behave as if it manages the whole SNMP MIB tree itself. It's the
574         duty of the master agent to collect the best answer and return it
575         to the manager. See RFC 1227 chapter 3.1.6 for the glory details
576         :-). ucd-snmp really behaves bad here as it actually might ask
577         multiple times for the same GETNEXT request as it throws away the
578         answer when it expects it in a different subtree and might come
579         back later with the very same request. --jochen */
580
581      if (subresult <= 0)
582	{
583	  /* Prepare suffix. */
584	  suffix = reqid + subtree->name_len;
585	  suffix_len = *reqid_len - subtree->name_len;
586	  if (subresult < 0)
587	    {
588	      oid_copy(reqid, subtree->name, subtree->name_len);
589	      *reqid_len = subtree->name_len;
590	    }
591	  for (j = 0; j < subtree->variables_num; j++)
592	    {
593	      result = subresult;
594	      v = &subtree->variables[j];
595
596	      /* Next then check result >= 0. */
597	      if (result == 0)
598		result = oid_compare_part (suffix, suffix_len,
599					   v->name, v->namelen);
600
601	      if (result <= 0)
602		{
603		  if (debug_smux)
604		    zlog_debug ("SMUX function call index is %d", v->magic);
605		  if(result<0)
606		    {
607		      oid_copy(suffix, v->name, v->namelen);
608		      suffix_len = v->namelen;
609		    }
610		  *val = (*v->findVar) (v, suffix, &suffix_len, exact,
611					val_len, &write_method);
612		  *reqid_len = suffix_len + subtree->name_len;
613		  if (*val)
614		    {
615		      *val_type = v->type;
616		      return 0;
617		    }
618		}
619	    }
620	}
621    }
622  memcpy (reqid, save, savelen * sizeof(oid));
623  *reqid_len = savelen;
624
625  return SNMP_ERR_NOSUCHNAME;
626}
627
628/* GET message header. */
629static u_char *
630smux_parse_get_header (u_char *ptr, size_t *len, long *reqid)
631{
632  u_char type;
633  long errstat;
634  long errindex;
635
636  /* Request ID. */
637  ptr = asn_parse_int (ptr, len, &type, reqid, sizeof (*reqid));
638
639  if (debug_smux)
640    zlog_debug ("SMUX GET reqid: %d len: %d", (int) *reqid, (int) *len);
641
642  /* Error status. */
643  ptr = asn_parse_int (ptr, len, &type, &errstat, sizeof (errstat));
644
645  if (debug_smux)
646    zlog_debug ("SMUX GET errstat %ld len: %zd", errstat, *len);
647
648  /* Error index. */
649  ptr = asn_parse_int (ptr, len, &type, &errindex, sizeof (errindex));
650
651  if (debug_smux)
652    zlog_debug ("SMUX GET errindex %ld len: %zd", errindex, *len);
653
654  return ptr;
655}
656
657static void
658smux_parse_set (u_char *ptr, size_t len, int action)
659{
660  long reqid;
661  oid oid[MAX_OID_LEN];
662  size_t oid_len;
663  u_char val_type;
664  void *val;
665  size_t val_len;
666  int ret;
667
668  if (debug_smux)
669    zlog_debug ("SMUX SET(%s) message parse: len %zd",
670               (RESERVE1 == action) ? "RESERVE1" : ((FREE == action) ? "FREE" : "COMMIT"),
671               len);
672
673  /* Parse SET message header. */
674  ptr = smux_parse_get_header (ptr, &len, &reqid);
675
676  /* Parse SET message object ID. */
677  ptr = smux_var (ptr, len, oid, &oid_len, &val_len, &val_type, &val);
678
679  ret = smux_set (oid, &oid_len, val_type, val, val_len, action);
680  if (debug_smux)
681    zlog_debug ("SMUX SET ret %d", ret);
682
683  /* Return result. */
684  if (RESERVE1 == action)
685    smux_getresp_send (oid, oid_len, reqid, ret, 3, ASN_NULL, NULL, 0);
686}
687
688static void
689smux_parse_get (u_char *ptr, size_t len, int exact)
690{
691  long reqid;
692  oid oid[MAX_OID_LEN];
693  size_t oid_len;
694  u_char val_type;
695  void *val;
696  size_t val_len;
697  int ret;
698
699  if (debug_smux)
700    zlog_debug ("SMUX GET message parse: len %zd", len);
701
702  /* Parse GET message header. */
703  ptr = smux_parse_get_header (ptr, &len, &reqid);
704
705  /* Parse GET message object ID. We needn't the value come */
706  ptr = smux_var (ptr, len, oid, &oid_len, NULL, NULL, NULL);
707
708  /* Traditional getstatptr. */
709  if (exact)
710    ret = smux_get (oid, &oid_len, exact, &val_type, &val, &val_len);
711  else
712    ret = smux_getnext (oid, &oid_len, exact, &val_type, &val, &val_len);
713
714  /* Return result. */
715  if (ret == 0)
716    smux_getresp_send (oid, oid_len, reqid, 0, 0, val_type, val, val_len);
717  else
718    smux_getresp_send (oid, oid_len, reqid, ret, 3, ASN_NULL, NULL, 0);
719}
720
721/* Parse SMUX_CLOSE message. */
722static void
723smux_parse_close (u_char *ptr, int len)
724{
725  long reason = 0;
726
727  while (len--)
728    {
729      reason = (reason << 8) | (long) *ptr;
730      ptr++;
731    }
732  zlog_info ("SMUX_CLOSE with reason: %ld", reason);
733}
734
735/* SMUX_RRSP message. */
736static void
737smux_parse_rrsp (u_char *ptr, size_t len)
738{
739  u_char val;
740  long errstat;
741
742  ptr = asn_parse_int (ptr, &len, &val, &errstat, sizeof (errstat));
743
744  if (debug_smux)
745    zlog_debug ("SMUX_RRSP value: %d errstat: %ld", val, errstat);
746}
747
748/* Parse SMUX message. */
749static int
750smux_parse (u_char *ptr, size_t len)
751{
752  /* This buffer we'll use for SOUT message. We could allocate it with
753     malloc and save only static pointer/lenght, but IMHO static
754     buffer is a faster solusion. */
755  static u_char sout_save_buff[SMUXMAXPKTSIZE];
756  static int sout_save_len = 0;
757
758  int len_income = len; /* see note below: YYY */
759  u_char type;
760  u_char rollback;
761
762  rollback = ptr[2]; /* important only for SMUX_SOUT */
763
764process_rest: /* see note below: YYY */
765
766  /* Parse SMUX message type and subsequent length. */
767  ptr = asn_parse_header (ptr, &len, &type);
768
769  if (debug_smux)
770    zlog_debug ("SMUX message received type: %d rest len: %zd", type, len);
771
772  switch (type)
773    {
774    case SMUX_OPEN:
775      /* Open must be not send from SNMP agent. */
776      zlog_warn ("SMUX_OPEN received: resetting connection.");
777      return -1;
778      break;
779    case SMUX_RREQ:
780      /* SMUX_RREQ message is invalid for us. */
781      zlog_warn ("SMUX_RREQ received: resetting connection.");
782      return -1;
783      break;
784    case SMUX_SOUT:
785      /* SMUX_SOUT message is now valied for us. */
786      if (debug_smux)
787        zlog_debug ("SMUX_SOUT(%s)", rollback ? "rollback" : "commit");
788
789      if (sout_save_len > 0)
790        {
791          smux_parse_set (sout_save_buff, sout_save_len, rollback ? FREE : COMMIT);
792          sout_save_len = 0;
793        }
794      else
795        zlog_warn ("SMUX_SOUT sout_save_len=%d - invalid", (int) sout_save_len);
796
797      if (len_income > 3)
798        {
799          /* YYY: this strange code has to solve the "slow peer"
800             problem: When agent sends SMUX_SOUT message it doesn't
801             wait any responce and may send some next message to
802             subagent. Then the peer in 'smux_read()' will recieve
803             from socket the 'concatenated' buffer, contaning both
804             SMUX_SOUT message and the next one
805             (SMUX_GET/SMUX_GETNEXT/SMUX_GET). So we should check: if
806             the buffer is longer than 3 ( length of SMUX_SOUT ), we
807             must process the rest of it.  This effect may be observed
808             if 'debug_smux' is set to '1' */
809          ptr++;
810          len = len_income - 3;
811          goto process_rest;
812        }
813      break;
814    case SMUX_GETRSP:
815      /* SMUX_GETRSP message is invalid for us. */
816      zlog_warn ("SMUX_GETRSP received: resetting connection.");
817      return -1;
818      break;
819    case SMUX_CLOSE:
820      /* Close SMUX connection. */
821      if (debug_smux)
822	zlog_debug ("SMUX_CLOSE");
823      smux_parse_close (ptr, len);
824      return -1;
825      break;
826    case SMUX_RRSP:
827      /* This is response for register message. */
828      if (debug_smux)
829	zlog_debug ("SMUX_RRSP");
830      smux_parse_rrsp (ptr, len);
831      break;
832    case SMUX_GET:
833      /* Exact request for object id. */
834      if (debug_smux)
835	zlog_debug ("SMUX_GET");
836      smux_parse_get (ptr, len, 1);
837      break;
838    case SMUX_GETNEXT:
839      /* Next request for object id. */
840      if (debug_smux)
841	zlog_debug ("SMUX_GETNEXT");
842      smux_parse_get (ptr, len, 0);
843      break;
844    case SMUX_SET:
845      /* SMUX_SET is supported with some limitations. */
846      if (debug_smux)
847	zlog_debug ("SMUX_SET");
848
849      /* save the data for future SMUX_SOUT */
850      memcpy (sout_save_buff, ptr, len);
851      sout_save_len = len;
852      smux_parse_set (ptr, len, RESERVE1);
853      break;
854    default:
855      zlog_info ("Unknown type: %d", type);
856      break;
857    }
858  return 0;
859}
860
861/* SMUX message read function. */
862static int
863smux_read (struct thread *t)
864{
865  int sock;
866  int len;
867  u_char buf[SMUXMAXPKTSIZE];
868  int ret;
869
870  /* Clear thread. */
871  sock = THREAD_FD (t);
872  smux_read_thread = NULL;
873
874  if (debug_smux)
875    zlog_debug ("SMUX read start");
876
877  /* Read message from SMUX socket. */
878  len = recv (sock, buf, SMUXMAXPKTSIZE, 0);
879
880  if (len < 0)
881    {
882      zlog_warn ("Can't read all SMUX packet: %s", safe_strerror (errno));
883      close (sock);
884      smux_sock = -1;
885      smux_event (SMUX_CONNECT, 0);
886      return -1;
887    }
888
889  if (len == 0)
890    {
891      zlog_warn ("SMUX connection closed: %d", sock);
892      close (sock);
893      smux_sock = -1;
894      smux_event (SMUX_CONNECT, 0);
895      return -1;
896    }
897
898  if (debug_smux)
899    zlog_debug ("SMUX read len: %d", len);
900
901  /* Parse the message. */
902  ret = smux_parse (buf, len);
903
904  if (ret < 0)
905    {
906      close (sock);
907      smux_sock = -1;
908      smux_event (SMUX_CONNECT, 0);
909      return -1;
910    }
911
912  /* Regiser read thread. */
913  smux_event (SMUX_READ, sock);
914
915  return 0;
916}
917
918static int
919smux_open (int sock)
920{
921  u_char buf[BUFSIZ];
922  u_char *ptr;
923  size_t len;
924  long version;
925  const char progname[] = QUAGGA_PROGNAME "-" QUAGGA_VERSION;
926
927  if (debug_smux)
928    {
929      smux_oid_dump ("SMUX open oid", smux_oid, smux_oid_len);
930      zlog_debug ("SMUX open progname: %s", progname);
931      zlog_debug ("SMUX open password: %s", smux_passwd);
932    }
933
934  ptr = buf;
935  len = BUFSIZ;
936
937  /* SMUX Header.  As placeholder. */
938  ptr = asn_build_header (ptr, &len, (u_char) SMUX_OPEN, 0);
939
940  /* SMUX Open. */
941  version = 0;
942  ptr = asn_build_int (ptr, &len,
943		       (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
944		       &version, sizeof (version));
945
946  /* SMUX connection oid. */
947  ptr = asn_build_objid (ptr, &len,
948			 (u_char)
949			 (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID),
950			 smux_oid, smux_oid_len);
951
952  /* SMUX connection description. */
953  ptr = asn_build_string (ptr, &len,
954			  (u_char)
955			  (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR),
956			  (const u_char *) progname, strlen (progname));
957
958  /* SMUX connection password. */
959  ptr = asn_build_string (ptr, &len,
960			  (u_char)
961			  (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR),
962			  (u_char *)smux_passwd, strlen (smux_passwd));
963
964  /* Fill in real SMUX header.  We exclude ASN header size (2). */
965  len = BUFSIZ;
966  asn_build_header (buf, &len, (u_char) SMUX_OPEN, (ptr - buf) - 2);
967
968  return send (sock, buf, (ptr - buf), 0);
969}
970
971/* `ename` is ignored. Instead of using the provided enterprise OID,
972   the SMUX peer is used. This keep compatibility with the previous
973   versions of Quagga.
974
975   All other fields are used as they are intended. */
976int
977smux_trap (struct variable *vp, size_t vp_len,
978	   const oid *ename, size_t enamelen,
979	   const oid *name, size_t namelen,
980	   const oid *iname, size_t inamelen,
981	   const struct trap_object *trapobj, size_t trapobjlen,
982	   u_char sptrap)
983{
984  unsigned int i;
985  u_char buf[BUFSIZ];
986  u_char *ptr;
987  size_t len, length;
988  struct in_addr addr;
989  unsigned long val;
990  u_char *h1, *h1e;
991
992  ptr = buf;
993  len = BUFSIZ;
994  length = len;
995
996  /* When SMUX connection is not established. */
997  if (smux_sock < 0)
998    return 0;
999
1000  /* SMUX header. */
1001  ptr = asn_build_header (ptr, &len, (u_char) SMUX_TRAP, 0);
1002
1003  /* Sub agent enterprise oid. */
1004  ptr = asn_build_objid (ptr, &len,
1005			 (u_char)
1006			 (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID),
1007			 smux_oid, smux_oid_len);
1008
1009  /* IP address. */
1010  addr.s_addr = 0;
1011  ptr = asn_build_string (ptr, &len,
1012			  (u_char)
1013			  (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_IPADDRESS),
1014			  (u_char *)&addr, sizeof (addr));
1015
1016  /* Generic trap integer. */
1017  val = SNMP_TRAP_ENTERPRISESPECIFIC;
1018  ptr = asn_build_int (ptr, &len,
1019		       (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
1020		       (long *)&val, sizeof (val));
1021
1022  /* Specific trap integer. */
1023  val = sptrap;
1024  ptr = asn_build_int (ptr, &len,
1025		       (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
1026		       (long *)&val, sizeof (val));
1027
1028  /* Timeticks timestamp. */
1029  val = 0;
1030  ptr = asn_build_unsigned_int (ptr, &len,
1031				(u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_TIMETICKS),
1032				&val, sizeof (val));
1033
1034  /* Variables. */
1035  h1 = ptr;
1036  ptr = asn_build_sequence (ptr, &len,
1037			    (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR),
1038			    0);
1039
1040
1041  /* Iteration for each objects. */
1042  h1e = ptr;
1043  for (i = 0; i < trapobjlen; i++)
1044    {
1045      int ret;
1046      oid oid[MAX_OID_LEN];
1047      size_t oid_len;
1048      void *val;
1049      size_t val_len;
1050      u_char val_type;
1051
1052      /* Make OID. */
1053      if (trapobj[i].namelen > 0)
1054        {
1055          oid_copy (oid, name, namelen);
1056          oid_copy (oid + namelen, trapobj[i].name, trapobj[i].namelen);
1057          oid_copy (oid + namelen + trapobj[i].namelen, iname, inamelen);
1058          oid_len = namelen + trapobj[i].namelen + inamelen;
1059        }
1060      else
1061        {
1062          oid_copy (oid, name, namelen);
1063          oid_copy (oid + namelen, trapobj[i].name, trapobj[i].namelen * (-1));
1064          oid_len = namelen + trapobj[i].namelen * (-1) ;
1065        }
1066
1067      if (debug_smux)
1068        {
1069          smux_oid_dump ("Trap", name, namelen);
1070          if (trapobj[i].namelen < 0)
1071            smux_oid_dump ("Trap",
1072                           trapobj[i].name, (- 1) * (trapobj[i].namelen));
1073          else
1074            {
1075              smux_oid_dump ("Trap", trapobj[i].name, (trapobj[i].namelen));
1076              smux_oid_dump ("Trap", iname, inamelen);
1077            }
1078          smux_oid_dump ("Trap", oid, oid_len);
1079          zlog_info ("BUFSIZ: %d // oid_len: %lu", BUFSIZ, (u_long)oid_len);
1080      }
1081
1082      ret = smux_get (oid, &oid_len, 1, &val_type, &val, &val_len);
1083
1084      if (debug_smux)
1085	zlog_debug ("smux_get result %d", ret);
1086
1087      if (ret == 0)
1088	ptr = snmp_build_var_op (ptr, oid, &oid_len,
1089				 val_type, val_len, val, &len);
1090    }
1091
1092  /* Now variable size is known, fill in size */
1093  asn_build_sequence(h1, &length,
1094		     (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR),
1095		     ptr - h1e);
1096
1097  /* Fill in size of whole sequence */
1098  len = BUFSIZ;
1099  asn_build_header (buf, &len, (u_char) SMUX_TRAP, (ptr - buf) - 2);
1100
1101  return send (smux_sock, buf, (ptr - buf), 0);
1102}
1103
1104static int
1105smux_register (int sock)
1106{
1107  u_char buf[BUFSIZ];
1108  u_char *ptr;
1109  int ret;
1110  size_t len;
1111  long priority;
1112  long operation;
1113  struct subtree *subtree;
1114  struct listnode *node, *nnode;
1115
1116  ret = 0;
1117
1118  for (ALL_LIST_ELEMENTS (treelist, node, nnode, subtree))
1119    {
1120      ptr = buf;
1121      len = BUFSIZ;
1122
1123      /* SMUX RReq Header. */
1124      ptr = asn_build_header (ptr, &len, (u_char) SMUX_RREQ, 0);
1125
1126      /* Register MIB tree. */
1127      ptr = asn_build_objid (ptr, &len,
1128			    (u_char)
1129			    (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID),
1130			    subtree->name, subtree->name_len);
1131
1132      /* Priority. */
1133      priority = -1;
1134      ptr = asn_build_int (ptr, &len,
1135		          (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
1136		          &priority, sizeof (priority));
1137
1138      /* Operation. */
1139      operation = 2; /* Register R/W */
1140      ptr = asn_build_int (ptr, &len,
1141		          (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
1142		          &operation, sizeof (operation));
1143
1144      if (debug_smux)
1145        {
1146          smux_oid_dump ("SMUX register oid", subtree->name, subtree->name_len);
1147          zlog_debug ("SMUX register priority: %ld", priority);
1148          zlog_debug ("SMUX register operation: %ld", operation);
1149        }
1150
1151      len = BUFSIZ;
1152      asn_build_header (buf, &len, (u_char) SMUX_RREQ, (ptr - buf) - 2);
1153      ret = send (sock, buf, (ptr - buf), 0);
1154      if (ret < 0)
1155        return ret;
1156    }
1157  return ret;
1158}
1159
1160/* Try to connect to SNMP agent. */
1161static int
1162smux_connect (struct thread *t)
1163{
1164  int ret;
1165
1166  if (debug_smux)
1167    zlog_debug ("SMUX connect try %d", fail + 1);
1168
1169  /* Clear thread poner of myself. */
1170  smux_connect_thread = NULL;
1171
1172  /* Make socket.  Try to connect. */
1173  smux_sock = smux_socket ();
1174  if (smux_sock < 0)
1175    {
1176      if (++fail < SMUX_MAX_FAILURE)
1177	smux_event (SMUX_CONNECT, 0);
1178      return 0;
1179    }
1180
1181  /* Send OPEN PDU. */
1182  ret = smux_open (smux_sock);
1183  if (ret < 0)
1184    {
1185      zlog_warn ("SMUX open message send failed: %s", safe_strerror (errno));
1186      close (smux_sock);
1187      smux_sock = -1;
1188      if (++fail < SMUX_MAX_FAILURE)
1189	smux_event (SMUX_CONNECT, 0);
1190      return -1;
1191    }
1192
1193  /* Send any outstanding register PDUs. */
1194  ret = smux_register (smux_sock);
1195  if (ret < 0)
1196    {
1197      zlog_warn ("SMUX register message send failed: %s", safe_strerror (errno));
1198      close (smux_sock);
1199      smux_sock = -1;
1200      if (++fail < SMUX_MAX_FAILURE)
1201	smux_event (SMUX_CONNECT, 0);
1202      return -1;
1203    }
1204
1205  /* Everything goes fine. */
1206  smux_event (SMUX_READ, smux_sock);
1207
1208  return 0;
1209}
1210
1211/* Clear all SMUX related resources. */
1212static void
1213smux_stop (void)
1214{
1215  if (smux_read_thread)
1216    {
1217      thread_cancel (smux_read_thread);
1218      smux_read_thread = NULL;
1219    }
1220
1221  if (smux_connect_thread)
1222    {
1223      thread_cancel (smux_connect_thread);
1224      smux_connect_thread = NULL;
1225    }
1226
1227  if (smux_sock >= 0)
1228    {
1229      close (smux_sock);
1230      smux_sock = -1;
1231    }
1232}
1233
1234
1235
1236void
1237smux_event (enum smux_event event, int sock)
1238{
1239  switch (event)
1240    {
1241    case SMUX_SCHEDULE:
1242      smux_connect_thread = thread_add_event (master, smux_connect, NULL, 0);
1243      break;
1244    case SMUX_CONNECT:
1245      smux_connect_thread = thread_add_timer (master, smux_connect, NULL, 10);
1246      break;
1247    case SMUX_READ:
1248      smux_read_thread = thread_add_read (master, smux_read, NULL, sock);
1249      break;
1250    default:
1251      break;
1252    }
1253}
1254
1255static int
1256smux_str2oid (const char *str, oid *oid, size_t *oid_len)
1257{
1258  int len;
1259  int val;
1260
1261  len = 0;
1262  val = 0;
1263  *oid_len = 0;
1264
1265  if (*str == '.')
1266    str++;
1267  if (*str == '\0')
1268    return 0;
1269
1270  while (1)
1271    {
1272      if (! isdigit (*str))
1273	return -1;
1274
1275      while (isdigit (*str))
1276	{
1277	  val *= 10;
1278	  val += (*str - '0');
1279	  str++;
1280	}
1281
1282      if (*str == '\0')
1283	break;
1284      if (*str != '.')
1285	return -1;
1286
1287      oid[len++] = val;
1288      val = 0;
1289      str++;
1290    }
1291
1292  oid[len++] = val;
1293  *oid_len = len;
1294
1295  return 0;
1296}
1297
1298static oid *
1299smux_oid_dup (oid *objid, size_t objid_len)
1300{
1301  oid *new;
1302
1303  new = XMALLOC (MTYPE_TMP, sizeof (oid) * objid_len);
1304  oid_copy (new, objid, objid_len);
1305
1306  return new;
1307}
1308
1309static int
1310smux_peer_oid (struct vty *vty, const char *oid_str, const char *passwd_str)
1311{
1312  int ret;
1313  oid oid[MAX_OID_LEN];
1314  size_t oid_len;
1315
1316  ret = smux_str2oid (oid_str, oid, &oid_len);
1317  if (ret != 0)
1318    {
1319      vty_out (vty, "object ID malformed%s", VTY_NEWLINE);
1320      return CMD_WARNING;
1321    }
1322
1323  if (smux_oid)
1324    {
1325      free (smux_oid);
1326      smux_oid = NULL;
1327    }
1328
1329  /* careful, smux_passwd might point to string constant */
1330  if (smux_passwd)
1331    {
1332      free (smux_passwd);
1333      smux_passwd = NULL;
1334    }
1335
1336  smux_oid = smux_oid_dup (oid, oid_len);
1337  smux_oid_len = oid_len;
1338
1339  if (passwd_str)
1340    smux_passwd = strdup (passwd_str);
1341  else
1342    smux_passwd = strdup ("");
1343
1344  return 0;
1345}
1346
1347static int
1348smux_peer_default (void)
1349{
1350  if (smux_oid)
1351    {
1352      free (smux_oid);
1353      smux_oid = NULL;
1354    }
1355
1356  /* careful, smux_passwd might be pointing at string constant */
1357  if (smux_passwd)
1358    {
1359      free (smux_passwd);
1360      smux_passwd = NULL;
1361    }
1362
1363  return CMD_SUCCESS;
1364}
1365
1366DEFUN (smux_peer,
1367       smux_peer_cmd,
1368       "smux peer OID",
1369       "SNMP MUX protocol settings\n"
1370       "SNMP MUX peer settings\n"
1371       "Object ID used in SMUX peering\n")
1372{
1373  if (smux_peer_oid (vty, argv[0], NULL) == 0)
1374    {
1375      smux_start();
1376      return CMD_SUCCESS;
1377    }
1378  else
1379    return CMD_WARNING;
1380}
1381
1382DEFUN (smux_peer_password,
1383       smux_peer_password_cmd,
1384       "smux peer OID PASSWORD",
1385       "SNMP MUX protocol settings\n"
1386       "SNMP MUX peer settings\n"
1387       "SMUX peering object ID\n"
1388       "SMUX peering password\n")
1389{
1390  if (smux_peer_oid (vty, argv[0], argv[1]) == 0)
1391    {
1392      smux_start();
1393      return CMD_SUCCESS;
1394    }
1395  else
1396    return CMD_WARNING;
1397}
1398
1399DEFUN (no_smux_peer,
1400       no_smux_peer_cmd,
1401       "no smux peer",
1402       NO_STR
1403       "SNMP MUX protocol settings\n"
1404       "SNMP MUX peer settings\n")
1405{
1406  smux_stop();
1407  return smux_peer_default ();
1408}
1409
1410ALIAS (no_smux_peer,
1411       no_smux_peer_oid_cmd,
1412       "no smux peer OID",
1413       NO_STR
1414       "SNMP MUX protocol settings\n"
1415       "SNMP MUX peer settings\n"
1416       "SMUX peering object ID\n")
1417
1418ALIAS (no_smux_peer,
1419       no_smux_peer_oid_password_cmd,
1420       "no smux peer OID PASSWORD",
1421       NO_STR
1422       "SNMP MUX protocol settings\n"
1423       "SNMP MUX peer settings\n"
1424       "SMUX peering object ID\n"
1425       "SMUX peering password\n")
1426
1427static int
1428config_write_smux (struct vty *vty)
1429{
1430  int first = 1;
1431  unsigned int i;
1432
1433  if (smux_oid)
1434    {
1435      vty_out (vty, "smux peer ");
1436      for (i = 0; i < smux_oid_len; i++)
1437	{
1438	  vty_out (vty, "%s%d", first ? "" : ".", (int) smux_oid[i]);
1439	  first = 0;
1440	}
1441      vty_out (vty, " %s%s", smux_passwd, VTY_NEWLINE);
1442    }
1443  return 0;
1444}
1445
1446/* Register subtree to smux master tree. */
1447void
1448smux_register_mib (const char *descr, struct variable *var,
1449                   size_t width, int num,
1450		   oid name[], size_t namelen)
1451{
1452  struct subtree *tree;
1453
1454  tree = (struct subtree *)malloc(sizeof(struct subtree));
1455  oid_copy (tree->name, name, namelen);
1456  tree->name_len = namelen;
1457  tree->variables = var;
1458  tree->variables_num = num;
1459  tree->variables_width = width;
1460  tree->registered = 0;
1461  listnode_add_sort(treelist, tree);
1462}
1463
1464/* Compare function to keep treelist sorted */
1465static int
1466smux_tree_cmp(struct subtree *tree1, struct subtree *tree2)
1467{
1468  return oid_compare(tree1->name, tree1->name_len,
1469		     tree2->name, tree2->name_len);
1470}
1471
1472/* Initialize some values then schedule first SMUX connection. */
1473void
1474smux_init (struct thread_master *tm)
1475{
1476  /* copy callers thread master */
1477  master = tm;
1478
1479  /* Make MIB tree. */
1480  treelist = list_new();
1481  treelist->cmp = (int (*)(void *, void *))smux_tree_cmp;
1482
1483  /* Install commands. */
1484  install_node (&smux_node, config_write_smux);
1485
1486  install_element (CONFIG_NODE, &smux_peer_cmd);
1487  install_element (CONFIG_NODE, &smux_peer_password_cmd);
1488  install_element (CONFIG_NODE, &no_smux_peer_cmd);
1489  install_element (CONFIG_NODE, &no_smux_peer_oid_cmd);
1490  install_element (CONFIG_NODE, &no_smux_peer_oid_password_cmd);
1491}
1492
1493void
1494smux_start(void)
1495{
1496  /* Close any existing connections. */
1497  smux_stop();
1498
1499  /* Schedule first connection. */
1500  smux_event (SMUX_SCHEDULE, 0);
1501}
1502#endif /* HAVE_SNMP */
1503