]> git.sommitrealweird.co.uk Git - quagga-debian.git/blob - lib/smux.c
New upstream release and new maintainer
[quagga-debian.git] / lib / smux.c
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. */
57 struct 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
78 enum smux_event {SMUX_SCHEDULE, SMUX_CONNECT, SMUX_READ};
79
80 void smux_event (enum smux_event, int);
81
82
83 /* SMUX socket. */
84 int smux_sock = -1;
85
86 /* SMUX subtree list. */
87 struct list *treelist;
88
89 /* SMUX oid. */
90 oid *smux_oid = NULL;
91 size_t smux_oid_len;
92
93 /* SMUX password. */
94 char *smux_passwd = NULL;
95
96 /* SMUX read threads. */
97 struct thread *smux_read_thread;
98
99 /* SMUX connect thrads. */
100 struct thread *smux_connect_thread;
101
102 /* SMUX debug flag. */
103 int debug_smux = 0;
104
105 /* SMUX failure count. */
106 int fail = 0;
107
108 /* SMUX node. */
109 static struct cmd_node smux_node =
110 {
111   SMUX_NODE,
112   ""                            /* SMUX has no interface. */
113 };
114
115 /* thread master */
116 static struct thread_master *smux_master;
117
118 static int
119 oid_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
136 static void
137 smux_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
153 static int
154 smux_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
246 static void
247 smux_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
308 static u_char *
309 smux_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
409 static int
410 smux_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
477 static int
478 smux_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
543 static int
544 smux_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. */
629 static u_char *
630 smux_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
657 static void
658 smux_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
688 static void
689 smux_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. */
722 static void
723 smux_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. */
736 static void
737 smux_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. */
749 static int
750 smux_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
764 process_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. */
862 static int
863 smux_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
918 static int
919 smux_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. */
976 int
977 smux_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
1104 static int
1105 smux_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. */
1161 static int
1162 smux_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. */
1212 static void
1213 smux_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
1236 void
1237 smux_event (enum smux_event event, int sock)
1238 {
1239   switch (event)
1240     {
1241     case SMUX_SCHEDULE:
1242       smux_connect_thread = thread_add_event (smux_master, smux_connect, NULL, 0);
1243       break;
1244     case SMUX_CONNECT:
1245       smux_connect_thread = thread_add_timer (smux_master, smux_connect, NULL, 10);
1246       break;
1247     case SMUX_READ:
1248       smux_read_thread = thread_add_read (smux_master, smux_read, NULL, sock);
1249       break;
1250     default:
1251       break;
1252     }
1253 }
1254
1255 static int
1256 smux_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
1298 static oid *
1299 smux_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
1309 static int
1310 smux_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
1347 static int
1348 smux_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
1366 DEFUN (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
1382 DEFUN (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
1399 DEFUN (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
1410 ALIAS (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
1418 ALIAS (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
1427 static int
1428 config_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. */
1447 void
1448 smux_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 */
1465 static int
1466 smux_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. */
1473 void
1474 smux_init (struct thread_master *tm)
1475 {
1476   /* copy callers thread master */
1477   smux_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
1493 void
1494 smux_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 */