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