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