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