Annotation of embedaddon/quagga/lib/smux.c, revision 1.1.1.1
1.1 misho 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: int ret;
274: u_char buf[BUFSIZ];
275: u_char *ptr, *h1, *h1e, *h2, *h2e;
276: size_t len, length;
277:
278: ptr = buf;
279: len = BUFSIZ;
280: length = len;
281:
282: if (debug_smux)
283: {
284: zlog_debug ("SMUX GETRSP send");
285: zlog_debug ("SMUX GETRSP reqid: %ld", reqid);
286: }
287:
288: h1 = ptr;
289: /* Place holder h1 for complete sequence */
290: ptr = asn_build_sequence (ptr, &len, (u_char) SMUX_GETRSP, 0);
291: h1e = ptr;
292:
293: ptr = asn_build_int (ptr, &len,
294: (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
295: &reqid, sizeof (reqid));
296:
297: if (debug_smux)
298: zlog_debug ("SMUX GETRSP errstat: %ld", errstat);
299:
300: ptr = asn_build_int (ptr, &len,
301: (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
302: &errstat, sizeof (errstat));
303: if (debug_smux)
304: zlog_debug ("SMUX GETRSP errindex: %ld", errindex);
305:
306: ptr = asn_build_int (ptr, &len,
307: (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
308: &errindex, sizeof (errindex));
309:
310: h2 = ptr;
311: /* Place holder h2 for one variable */
312: ptr = asn_build_sequence (ptr, &len,
313: (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR),
314: 0);
315: h2e = ptr;
316:
317: ptr = snmp_build_var_op (ptr, objid, &objid_len,
318: val_type, arg_len, arg, &len);
319:
320: /* Now variable size is known, fill in size */
321: asn_build_sequence(h2,&length,(u_char)(ASN_SEQUENCE|ASN_CONSTRUCTOR),ptr-h2e);
322:
323: /* Fill in size of whole sequence */
324: asn_build_sequence(h1,&length,(u_char)SMUX_GETRSP,ptr-h1e);
325:
326: if (debug_smux)
327: zlog_debug ("SMUX getresp send: %td", (ptr - buf));
328:
329: ret = send (smux_sock, buf, (ptr - buf), 0);
330: }
331:
332: static u_char *
333: smux_var (u_char *ptr, size_t len, oid objid[], size_t *objid_len,
334: size_t *var_val_len,
335: u_char *var_val_type,
336: void **var_value)
337: {
338: u_char type;
339: u_char val_type;
340: size_t val_len;
341: u_char *val;
342:
343: if (debug_smux)
344: zlog_debug ("SMUX var parse: len %zd", len);
345:
346: /* Parse header. */
347: ptr = asn_parse_header (ptr, &len, &type);
348:
349: if (debug_smux)
350: {
351: zlog_debug ("SMUX var parse: type %d len %zd", type, len);
352: zlog_debug ("SMUX var parse: type must be %d",
353: (ASN_SEQUENCE | ASN_CONSTRUCTOR));
354: }
355:
356: /* Parse var option. */
357: *objid_len = MAX_OID_LEN;
358: ptr = snmp_parse_var_op(ptr, objid, objid_len, &val_type,
359: &val_len, &val, &len);
360:
361: if (var_val_len)
362: *var_val_len = val_len;
363:
364: if (var_value)
365: *var_value = (void*) val;
366:
367: if (var_val_type)
368: *var_val_type = val_type;
369:
370: /* Requested object id length is objid_len. */
371: if (debug_smux)
372: smux_oid_dump ("Request OID", objid, *objid_len);
373:
374: if (debug_smux)
375: zlog_debug ("SMUX val_type: %d", val_type);
376:
377: /* Check request value type. */
378: if (debug_smux)
379: switch (val_type)
380: {
381: case ASN_NULL:
382: /* In case of SMUX_GET or SMUX_GET_NEXT val_type is set to
383: ASN_NULL. */
384: zlog_debug ("ASN_NULL");
385: break;
386:
387: case ASN_INTEGER:
388: zlog_debug ("ASN_INTEGER");
389: break;
390: case ASN_COUNTER:
391: case ASN_GAUGE:
392: case ASN_TIMETICKS:
393: case ASN_UINTEGER:
394: zlog_debug ("ASN_COUNTER");
395: break;
396: case ASN_COUNTER64:
397: zlog_debug ("ASN_COUNTER64");
398: break;
399: case ASN_IPADDRESS:
400: zlog_debug ("ASN_IPADDRESS");
401: break;
402: case ASN_OCTET_STR:
403: zlog_debug ("ASN_OCTET_STR");
404: break;
405: case ASN_OPAQUE:
406: case ASN_NSAP:
407: case ASN_OBJECT_ID:
408: zlog_debug ("ASN_OPAQUE");
409: break;
410: case SNMP_NOSUCHOBJECT:
411: zlog_debug ("SNMP_NOSUCHOBJECT");
412: break;
413: case SNMP_NOSUCHINSTANCE:
414: zlog_debug ("SNMP_NOSUCHINSTANCE");
415: break;
416: case SNMP_ENDOFMIBVIEW:
417: zlog_debug ("SNMP_ENDOFMIBVIEW");
418: break;
419: case ASN_BIT_STR:
420: zlog_debug ("ASN_BIT_STR");
421: break;
422: default:
423: zlog_debug ("Unknown type");
424: break;
425: }
426: return ptr;
427: }
428:
429: /* NOTE: all 3 functions (smux_set, smux_get & smux_getnext) are based on
430: ucd-snmp smux and as such suppose, that the peer receives in the message
431: only one variable. Fortunately, IBM seems to do the same in AIX. */
432:
433: static int
434: smux_set (oid *reqid, size_t *reqid_len,
435: u_char val_type, void *val, size_t val_len, int action)
436: {
437: int j;
438: struct subtree *subtree;
439: struct variable *v;
440: int subresult;
441: oid *suffix;
442: size_t suffix_len;
443: int result;
444: u_char *statP = NULL;
445: WriteMethod *write_method = NULL;
446: struct listnode *node, *nnode;
447:
448: /* Check */
449: for (ALL_LIST_ELEMENTS (treelist, node, nnode, subtree))
450: {
451: subresult = oid_compare_part (reqid, *reqid_len,
452: subtree->name, subtree->name_len);
453:
454: /* Subtree matched. */
455: if (subresult == 0)
456: {
457: /* Prepare suffix. */
458: suffix = reqid + subtree->name_len;
459: suffix_len = *reqid_len - subtree->name_len;
460: result = subresult;
461:
462: /* Check variables. */
463: for (j = 0; j < subtree->variables_num; j++)
464: {
465: v = &subtree->variables[j];
466:
467: /* Always check suffix */
468: result = oid_compare_part (suffix, suffix_len,
469: v->name, v->namelen);
470:
471: /* This is exact match so result must be zero. */
472: if (result == 0)
473: {
474: if (debug_smux)
475: zlog_debug ("SMUX function call index is %d", v->magic);
476:
477: statP = (*v->findVar) (v, suffix, &suffix_len, 1,
478: &val_len, &write_method);
479:
480: if (write_method)
481: {
482: return (*write_method)(action, val, val_type, val_len,
483: statP, suffix, suffix_len, v);
484: }
485: else
486: {
487: return SNMP_ERR_READONLY;
488: }
489: }
490:
491: /* If above execution is failed or oid is small (so
492: there is no further match). */
493: if (result < 0)
494: return SNMP_ERR_NOSUCHNAME;
495: }
496: }
497: }
498: return SNMP_ERR_NOSUCHNAME;
499: }
500:
501: static int
502: smux_get (oid *reqid, size_t *reqid_len, int exact,
503: u_char *val_type,void **val, size_t *val_len)
504: {
505: int j;
506: struct subtree *subtree;
507: struct variable *v;
508: int subresult;
509: oid *suffix;
510: size_t suffix_len;
511: int result;
512: WriteMethod *write_method=NULL;
513: struct listnode *node, *nnode;
514:
515: /* Check */
516: for (ALL_LIST_ELEMENTS (treelist, node, nnode,subtree))
517: {
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)
542: zlog_debug ("SMUX function call index is %d", v->magic);
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:
567: static int
568: smux_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;
578: size_t suffix_len;
579: int result;
580: WriteMethod *write_method=NULL;
581: struct listnode *node, *nnode;
582:
583:
584: /* Save incoming request. */
585: oid_copy (save, reqid, *reqid_len);
586: savelen = *reqid_len;
587:
588: /* Check */
589: for (ALL_LIST_ELEMENTS (treelist, node, nnode, subtree))
590: {
591: subresult = oid_compare_part (reqid, *reqid_len,
592: subtree->name, subtree->name_len);
593:
594: /* If request is in the tree. The agent has to make sure we
595: only receive requests we have registered for. */
596: /* Unfortunately, that's not true. In fact, a SMUX subagent has to
597: behave as if it manages the whole SNMP MIB tree itself. It's the
598: duty of the master agent to collect the best answer and return it
599: to the manager. See RFC 1227 chapter 3.1.6 for the glory details
600: :-). ucd-snmp really behaves bad here as it actually might ask
601: multiple times for the same GETNEXT request as it throws away the
602: answer when it expects it in a different subtree and might come
603: back later with the very same request. --jochen */
604:
605: if (subresult <= 0)
606: {
607: /* Prepare suffix. */
608: suffix = reqid + subtree->name_len;
609: suffix_len = *reqid_len - subtree->name_len;
610: if (subresult < 0)
611: {
612: oid_copy(reqid, subtree->name, subtree->name_len);
613: *reqid_len = subtree->name_len;
614: }
615: for (j = 0; j < subtree->variables_num; j++)
616: {
617: result = subresult;
618: v = &subtree->variables[j];
619:
620: /* Next then check result >= 0. */
621: if (result == 0)
622: result = oid_compare_part (suffix, suffix_len,
623: v->name, v->namelen);
624:
625: if (result <= 0)
626: {
627: if (debug_smux)
628: zlog_debug ("SMUX function call index is %d", v->magic);
629: if(result<0)
630: {
631: oid_copy(suffix, v->name, v->namelen);
632: suffix_len = v->namelen;
633: }
634: *val = (*v->findVar) (v, suffix, &suffix_len, exact,
635: val_len, &write_method);
636: *reqid_len = suffix_len + subtree->name_len;
637: if (*val)
638: {
639: *val_type = v->type;
640: return 0;
641: }
642: }
643: }
644: }
645: }
646: memcpy (reqid, save, savelen * sizeof(oid));
647: *reqid_len = savelen;
648:
649: return SNMP_ERR_NOSUCHNAME;
650: }
651:
652: /* GET message header. */
653: static u_char *
654: smux_parse_get_header (u_char *ptr, size_t *len, long *reqid)
655: {
656: u_char type;
657: long errstat;
658: long errindex;
659:
660: /* Request ID. */
661: ptr = asn_parse_int (ptr, len, &type, reqid, sizeof (*reqid));
662:
663: if (debug_smux)
664: zlog_debug ("SMUX GET reqid: %d len: %d", (int) *reqid, (int) *len);
665:
666: /* Error status. */
667: ptr = asn_parse_int (ptr, len, &type, &errstat, sizeof (errstat));
668:
669: if (debug_smux)
670: zlog_debug ("SMUX GET errstat %ld len: %zd", errstat, *len);
671:
672: /* Error index. */
673: ptr = asn_parse_int (ptr, len, &type, &errindex, sizeof (errindex));
674:
675: if (debug_smux)
676: zlog_debug ("SMUX GET errindex %ld len: %zd", errindex, *len);
677:
678: return ptr;
679: }
680:
681: static void
682: smux_parse_set (u_char *ptr, size_t len, int action)
683: {
684: long reqid;
685: oid oid[MAX_OID_LEN];
686: size_t oid_len;
687: u_char val_type;
688: void *val;
689: size_t val_len;
690: int ret;
691:
692: if (debug_smux)
693: zlog_debug ("SMUX SET(%s) message parse: len %zd",
694: (RESERVE1 == action) ? "RESERVE1" : ((FREE == action) ? "FREE" : "COMMIT"),
695: len);
696:
697: /* Parse SET message header. */
698: ptr = smux_parse_get_header (ptr, &len, &reqid);
699:
700: /* Parse SET message object ID. */
701: ptr = smux_var (ptr, len, oid, &oid_len, &val_len, &val_type, &val);
702:
703: ret = smux_set (oid, &oid_len, val_type, val, val_len, action);
704: if (debug_smux)
705: zlog_debug ("SMUX SET ret %d", ret);
706:
707: /* Return result. */
708: if (RESERVE1 == action)
709: smux_getresp_send (oid, oid_len, reqid, ret, 3, ASN_NULL, NULL, 0);
710: }
711:
712: static void
713: smux_parse_get (u_char *ptr, size_t len, int exact)
714: {
715: long reqid;
716: oid oid[MAX_OID_LEN];
717: size_t oid_len;
718: u_char val_type;
719: void *val;
720: size_t val_len;
721: int ret;
722:
723: if (debug_smux)
724: zlog_debug ("SMUX GET message parse: len %zd", len);
725:
726: /* Parse GET message header. */
727: ptr = smux_parse_get_header (ptr, &len, &reqid);
728:
729: /* Parse GET message object ID. We needn't the value come */
730: ptr = smux_var (ptr, len, oid, &oid_len, NULL, NULL, NULL);
731:
732: /* Traditional getstatptr. */
733: if (exact)
734: ret = smux_get (oid, &oid_len, exact, &val_type, &val, &val_len);
735: else
736: ret = smux_getnext (oid, &oid_len, exact, &val_type, &val, &val_len);
737:
738: /* Return result. */
739: if (ret == 0)
740: smux_getresp_send (oid, oid_len, reqid, 0, 0, val_type, val, val_len);
741: else
742: smux_getresp_send (oid, oid_len, reqid, ret, 3, ASN_NULL, NULL, 0);
743: }
744:
745: /* Parse SMUX_CLOSE message. */
746: static void
747: smux_parse_close (u_char *ptr, int len)
748: {
749: long reason = 0;
750:
751: while (len--)
752: {
753: reason = (reason << 8) | (long) *ptr;
754: ptr++;
755: }
756: zlog_info ("SMUX_CLOSE with reason: %ld", reason);
757: }
758:
759: /* SMUX_RRSP message. */
760: static void
761: smux_parse_rrsp (u_char *ptr, size_t len)
762: {
763: u_char val;
764: long errstat;
765:
766: ptr = asn_parse_int (ptr, &len, &val, &errstat, sizeof (errstat));
767:
768: if (debug_smux)
769: zlog_debug ("SMUX_RRSP value: %d errstat: %ld", val, errstat);
770: }
771:
772: /* Parse SMUX message. */
773: static int
774: smux_parse (u_char *ptr, size_t len)
775: {
776: /* This buffer we'll use for SOUT message. We could allocate it with
777: malloc and save only static pointer/lenght, but IMHO static
778: buffer is a faster solusion. */
779: static u_char sout_save_buff[SMUXMAXPKTSIZE];
780: static int sout_save_len = 0;
781:
782: int len_income = len; /* see note below: YYY */
783: u_char type;
784: u_char rollback;
785:
786: rollback = ptr[2]; /* important only for SMUX_SOUT */
787:
788: process_rest: /* see note below: YYY */
789:
790: /* Parse SMUX message type and subsequent length. */
791: ptr = asn_parse_header (ptr, &len, &type);
792:
793: if (debug_smux)
794: zlog_debug ("SMUX message received type: %d rest len: %zd", type, len);
795:
796: switch (type)
797: {
798: case SMUX_OPEN:
799: /* Open must be not send from SNMP agent. */
800: zlog_warn ("SMUX_OPEN received: resetting connection.");
801: return -1;
802: break;
803: case SMUX_RREQ:
804: /* SMUX_RREQ message is invalid for us. */
805: zlog_warn ("SMUX_RREQ received: resetting connection.");
806: return -1;
807: break;
808: case SMUX_SOUT:
809: /* SMUX_SOUT message is now valied for us. */
810: if (debug_smux)
811: zlog_debug ("SMUX_SOUT(%s)", rollback ? "rollback" : "commit");
812:
813: if (sout_save_len > 0)
814: {
815: smux_parse_set (sout_save_buff, sout_save_len, rollback ? FREE : COMMIT);
816: sout_save_len = 0;
817: }
818: else
819: zlog_warn ("SMUX_SOUT sout_save_len=%d - invalid", (int) sout_save_len);
820:
821: if (len_income > 3)
822: {
823: /* YYY: this strange code has to solve the "slow peer"
824: problem: When agent sends SMUX_SOUT message it doesn't
825: wait any responce and may send some next message to
826: subagent. Then the peer in 'smux_read()' will recieve
827: from socket the 'concatenated' buffer, contaning both
828: SMUX_SOUT message and the next one
829: (SMUX_GET/SMUX_GETNEXT/SMUX_GET). So we should check: if
830: the buffer is longer than 3 ( length of SMUX_SOUT ), we
831: must process the rest of it. This effect may be observed
832: if 'debug_smux' is set to '1' */
833: ptr++;
834: len = len_income - 3;
835: goto process_rest;
836: }
837: break;
838: case SMUX_GETRSP:
839: /* SMUX_GETRSP message is invalid for us. */
840: zlog_warn ("SMUX_GETRSP received: resetting connection.");
841: return -1;
842: break;
843: case SMUX_CLOSE:
844: /* Close SMUX connection. */
845: if (debug_smux)
846: zlog_debug ("SMUX_CLOSE");
847: smux_parse_close (ptr, len);
848: return -1;
849: break;
850: case SMUX_RRSP:
851: /* This is response for register message. */
852: if (debug_smux)
853: zlog_debug ("SMUX_RRSP");
854: smux_parse_rrsp (ptr, len);
855: break;
856: case SMUX_GET:
857: /* Exact request for object id. */
858: if (debug_smux)
859: zlog_debug ("SMUX_GET");
860: smux_parse_get (ptr, len, 1);
861: break;
862: case SMUX_GETNEXT:
863: /* Next request for object id. */
864: if (debug_smux)
865: zlog_debug ("SMUX_GETNEXT");
866: smux_parse_get (ptr, len, 0);
867: break;
868: case SMUX_SET:
869: /* SMUX_SET is supported with some limitations. */
870: if (debug_smux)
871: zlog_debug ("SMUX_SET");
872:
873: /* save the data for future SMUX_SOUT */
874: memcpy (sout_save_buff, ptr, len);
875: sout_save_len = len;
876: smux_parse_set (ptr, len, RESERVE1);
877: break;
878: default:
879: zlog_info ("Unknown type: %d", type);
880: break;
881: }
882: return 0;
883: }
884:
885: /* SMUX message read function. */
886: static int
887: smux_read (struct thread *t)
888: {
889: int sock;
890: int len;
891: u_char buf[SMUXMAXPKTSIZE];
892: int ret;
893:
894: /* Clear thread. */
895: sock = THREAD_FD (t);
896: smux_read_thread = NULL;
897:
898: if (debug_smux)
899: zlog_debug ("SMUX read start");
900:
901: /* Read message from SMUX socket. */
902: len = recv (sock, buf, SMUXMAXPKTSIZE, 0);
903:
904: if (len < 0)
905: {
906: zlog_warn ("Can't read all SMUX packet: %s", safe_strerror (errno));
907: close (sock);
908: smux_sock = -1;
909: smux_event (SMUX_CONNECT, 0);
910: return -1;
911: }
912:
913: if (len == 0)
914: {
915: zlog_warn ("SMUX connection closed: %d", sock);
916: close (sock);
917: smux_sock = -1;
918: smux_event (SMUX_CONNECT, 0);
919: return -1;
920: }
921:
922: if (debug_smux)
923: zlog_debug ("SMUX read len: %d", len);
924:
925: /* Parse the message. */
926: ret = smux_parse (buf, len);
927:
928: if (ret < 0)
929: {
930: close (sock);
931: smux_sock = -1;
932: smux_event (SMUX_CONNECT, 0);
933: return -1;
934: }
935:
936: /* Regiser read thread. */
937: smux_event (SMUX_READ, sock);
938:
939: return 0;
940: }
941:
942: static int
943: smux_open (int sock)
944: {
945: u_char buf[BUFSIZ];
946: u_char *ptr;
947: size_t len;
948: long version;
949: u_char progname[] = QUAGGA_PROGNAME "-" QUAGGA_VERSION;
950:
951: if (debug_smux)
952: {
953: smux_oid_dump ("SMUX open oid", smux_oid, smux_oid_len);
954: zlog_debug ("SMUX open progname: %s", progname);
955: zlog_debug ("SMUX open password: %s", smux_passwd);
956: }
957:
958: ptr = buf;
959: len = BUFSIZ;
960:
961: /* SMUX Header. As placeholder. */
962: ptr = asn_build_header (ptr, &len, (u_char) SMUX_OPEN, 0);
963:
964: /* SMUX Open. */
965: version = 0;
966: ptr = asn_build_int (ptr, &len,
967: (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
968: &version, sizeof (version));
969:
970: /* SMUX connection oid. */
971: ptr = asn_build_objid (ptr, &len,
972: (u_char)
973: (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID),
974: smux_oid, smux_oid_len);
975:
976: /* SMUX connection description. */
977: ptr = asn_build_string (ptr, &len,
978: (u_char)
979: (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR),
980: progname, strlen (progname));
981:
982: /* SMUX connection password. */
983: ptr = asn_build_string (ptr, &len,
984: (u_char)
985: (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR),
986: (u_char *)smux_passwd, strlen (smux_passwd));
987:
988: /* Fill in real SMUX header. We exclude ASN header size (2). */
989: len = BUFSIZ;
990: asn_build_header (buf, &len, (u_char) SMUX_OPEN, (ptr - buf) - 2);
991:
992: return send (sock, buf, (ptr - buf), 0);
993: }
994:
995: int
996: smux_trap (const oid *name, size_t namelen,
997: const oid *iname, size_t inamelen,
998: const struct trap_object *trapobj, size_t trapobjlen,
999: unsigned int tick, u_char sptrap)
1000: {
1001: unsigned int i;
1002: u_char buf[BUFSIZ];
1003: u_char *ptr;
1004: size_t len, length;
1005: struct in_addr addr;
1006: unsigned long val;
1007: u_char *h1, *h1e;
1008:
1009: ptr = buf;
1010: len = BUFSIZ;
1011: length = len;
1012:
1013: /* When SMUX connection is not established. */
1014: if (smux_sock < 0)
1015: return 0;
1016:
1017: /* SMUX header. */
1018: ptr = asn_build_header (ptr, &len, (u_char) SMUX_TRAP, 0);
1019:
1020: /* Sub agent enterprise oid. */
1021: ptr = asn_build_objid (ptr, &len,
1022: (u_char)
1023: (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID),
1024: smux_oid, smux_oid_len);
1025:
1026: /* IP address. */
1027: addr.s_addr = 0;
1028: ptr = asn_build_string (ptr, &len,
1029: (u_char)
1030: (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_IPADDRESS),
1031: (u_char *)&addr, sizeof (addr));
1032:
1033: /* Generic trap integer. */
1034: val = SNMP_TRAP_ENTERPRISESPECIFIC;
1035: ptr = asn_build_int (ptr, &len,
1036: (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
1037: (long *)&val, sizeof (val));
1038:
1039: /* Specific trap integer. */
1040: val = sptrap;
1041: ptr = asn_build_int (ptr, &len,
1042: (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
1043: (long *)&val, sizeof (val));
1044:
1045: /* Timeticks timestamp. */
1046: val = 0;
1047: ptr = asn_build_unsigned_int (ptr, &len,
1048: (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_TIMETICKS),
1049: &val, sizeof (val));
1050:
1051: /* Variables. */
1052: h1 = ptr;
1053: ptr = asn_build_sequence (ptr, &len,
1054: (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR),
1055: 0);
1056:
1057:
1058: /* Iteration for each objects. */
1059: h1e = ptr;
1060: for (i = 0; i < trapobjlen; i++)
1061: {
1062: int ret;
1063: oid oid[MAX_OID_LEN];
1064: size_t oid_len;
1065: void *val;
1066: size_t val_len;
1067: u_char val_type;
1068:
1069: /* Make OID. */
1070: if (trapobj[i].namelen > 0)
1071: {
1072: oid_copy (oid, name, namelen);
1073: oid_copy (oid + namelen, trapobj[i].name, trapobj[i].namelen);
1074: oid_copy (oid + namelen + trapobj[i].namelen, iname, inamelen);
1075: oid_len = namelen + trapobj[i].namelen + inamelen;
1076: }
1077: else
1078: {
1079: oid_copy (oid, name, namelen);
1080: oid_copy (oid + namelen, trapobj[i].name, trapobj[i].namelen * (-1));
1081: oid_len = namelen + trapobj[i].namelen * (-1) ;
1082: }
1083:
1084: if (debug_smux)
1085: {
1086: smux_oid_dump ("Trap", name, namelen);
1087: if (trapobj[i].namelen < 0)
1088: smux_oid_dump ("Trap",
1089: trapobj[i].name, (- 1) * (trapobj[i].namelen));
1090: else
1091: {
1092: smux_oid_dump ("Trap", trapobj[i].name, (trapobj[i].namelen));
1093: smux_oid_dump ("Trap", iname, inamelen);
1094: }
1095: smux_oid_dump ("Trap", oid, oid_len);
1096: zlog_info ("BUFSIZ: %d // oid_len: %lu", BUFSIZ, (u_long)oid_len);
1097: }
1098:
1099: ret = smux_get (oid, &oid_len, 1, &val_type, &val, &val_len);
1100:
1101: if (debug_smux)
1102: zlog_debug ("smux_get result %d", ret);
1103:
1104: if (ret == 0)
1105: ptr = snmp_build_var_op (ptr, oid, &oid_len,
1106: val_type, val_len, val, &len);
1107: }
1108:
1109: /* Now variable size is known, fill in size */
1110: asn_build_sequence(h1, &length,
1111: (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR),
1112: ptr - h1e);
1113:
1114: /* Fill in size of whole sequence */
1115: len = BUFSIZ;
1116: asn_build_header (buf, &len, (u_char) SMUX_TRAP, (ptr - buf) - 2);
1117:
1118: return send (smux_sock, buf, (ptr - buf), 0);
1119: }
1120:
1121: static int
1122: smux_register (int sock)
1123: {
1124: u_char buf[BUFSIZ];
1125: u_char *ptr;
1126: int ret;
1127: size_t len;
1128: long priority;
1129: long operation;
1130: struct subtree *subtree;
1131: struct listnode *node, *nnode;
1132:
1133: ret = 0;
1134:
1135: for (ALL_LIST_ELEMENTS (treelist, node, nnode, subtree))
1136: {
1137: ptr = buf;
1138: len = BUFSIZ;
1139:
1140: /* SMUX RReq Header. */
1141: ptr = asn_build_header (ptr, &len, (u_char) SMUX_RREQ, 0);
1142:
1143: /* Register MIB tree. */
1144: ptr = asn_build_objid (ptr, &len,
1145: (u_char)
1146: (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID),
1147: subtree->name, subtree->name_len);
1148:
1149: /* Priority. */
1150: priority = -1;
1151: ptr = asn_build_int (ptr, &len,
1152: (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
1153: &priority, sizeof (priority));
1154:
1155: /* Operation. */
1156: operation = 2; /* Register R/W */
1157: ptr = asn_build_int (ptr, &len,
1158: (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
1159: &operation, sizeof (operation));
1160:
1161: if (debug_smux)
1162: {
1163: smux_oid_dump ("SMUX register oid", subtree->name, subtree->name_len);
1164: zlog_debug ("SMUX register priority: %ld", priority);
1165: zlog_debug ("SMUX register operation: %ld", operation);
1166: }
1167:
1168: len = BUFSIZ;
1169: asn_build_header (buf, &len, (u_char) SMUX_RREQ, (ptr - buf) - 2);
1170: ret = send (sock, buf, (ptr - buf), 0);
1171: if (ret < 0)
1172: return ret;
1173: }
1174: return ret;
1175: }
1176:
1177: /* Try to connect to SNMP agent. */
1178: static int
1179: smux_connect (struct thread *t)
1180: {
1181: int ret;
1182:
1183: if (debug_smux)
1184: zlog_debug ("SMUX connect try %d", fail + 1);
1185:
1186: /* Clear thread poner of myself. */
1187: smux_connect_thread = NULL;
1188:
1189: /* Make socket. Try to connect. */
1190: smux_sock = smux_socket ();
1191: if (smux_sock < 0)
1192: {
1193: if (++fail < SMUX_MAX_FAILURE)
1194: smux_event (SMUX_CONNECT, 0);
1195: return 0;
1196: }
1197:
1198: /* Send OPEN PDU. */
1199: ret = smux_open (smux_sock);
1200: if (ret < 0)
1201: {
1202: zlog_warn ("SMUX open message send failed: %s", safe_strerror (errno));
1203: close (smux_sock);
1204: smux_sock = -1;
1205: if (++fail < SMUX_MAX_FAILURE)
1206: smux_event (SMUX_CONNECT, 0);
1207: return -1;
1208: }
1209:
1210: /* Send any outstanding register PDUs. */
1211: ret = smux_register (smux_sock);
1212: if (ret < 0)
1213: {
1214: zlog_warn ("SMUX register message send failed: %s", safe_strerror (errno));
1215: close (smux_sock);
1216: smux_sock = -1;
1217: if (++fail < SMUX_MAX_FAILURE)
1218: smux_event (SMUX_CONNECT, 0);
1219: return -1;
1220: }
1221:
1222: /* Everything goes fine. */
1223: smux_event (SMUX_READ, smux_sock);
1224:
1225: return 0;
1226: }
1227:
1228: /* Clear all SMUX related resources. */
1229: static void
1230: smux_stop (void)
1231: {
1232: if (smux_read_thread)
1233: {
1234: thread_cancel (smux_read_thread);
1235: smux_read_thread = NULL;
1236: }
1237:
1238: if (smux_connect_thread)
1239: {
1240: thread_cancel (smux_connect_thread);
1241: smux_connect_thread = NULL;
1242: }
1243:
1244: if (smux_sock >= 0)
1245: {
1246: close (smux_sock);
1247: smux_sock = -1;
1248: }
1249: }
1250:
1251:
1252:
1253: void
1254: smux_event (enum smux_event event, int sock)
1255: {
1256: switch (event)
1257: {
1258: case SMUX_SCHEDULE:
1259: smux_connect_thread = thread_add_event (master, smux_connect, NULL, 0);
1260: break;
1261: case SMUX_CONNECT:
1262: smux_connect_thread = thread_add_timer (master, smux_connect, NULL, 10);
1263: break;
1264: case SMUX_READ:
1265: smux_read_thread = thread_add_read (master, smux_read, NULL, sock);
1266: break;
1267: default:
1268: break;
1269: }
1270: }
1271:
1272: static int
1273: smux_str2oid (const char *str, oid *oid, size_t *oid_len)
1274: {
1275: int len;
1276: int val;
1277:
1278: len = 0;
1279: val = 0;
1280: *oid_len = 0;
1281:
1282: if (*str == '.')
1283: str++;
1284: if (*str == '\0')
1285: return 0;
1286:
1287: while (1)
1288: {
1289: if (! isdigit (*str))
1290: return -1;
1291:
1292: while (isdigit (*str))
1293: {
1294: val *= 10;
1295: val += (*str - '0');
1296: str++;
1297: }
1298:
1299: if (*str == '\0')
1300: break;
1301: if (*str != '.')
1302: return -1;
1303:
1304: oid[len++] = val;
1305: val = 0;
1306: str++;
1307: }
1308:
1309: oid[len++] = val;
1310: *oid_len = len;
1311:
1312: return 0;
1313: }
1314:
1315: static oid *
1316: smux_oid_dup (oid *objid, size_t objid_len)
1317: {
1318: oid *new;
1319:
1320: new = XMALLOC (MTYPE_TMP, sizeof (oid) * objid_len);
1321: oid_copy (new, objid, objid_len);
1322:
1323: return new;
1324: }
1325:
1326: static int
1327: smux_peer_oid (struct vty *vty, const char *oid_str, const char *passwd_str)
1328: {
1329: int ret;
1330: oid oid[MAX_OID_LEN];
1331: size_t oid_len;
1332:
1333: ret = smux_str2oid (oid_str, oid, &oid_len);
1334: if (ret != 0)
1335: {
1336: vty_out (vty, "object ID malformed%s", VTY_NEWLINE);
1337: return CMD_WARNING;
1338: }
1339:
1340: if (smux_oid)
1341: {
1342: free (smux_oid);
1343: smux_oid = NULL;
1344: }
1345:
1346: /* careful, smux_passwd might point to string constant */
1347: if (smux_passwd)
1348: {
1349: free (smux_passwd);
1350: smux_passwd = NULL;
1351: }
1352:
1353: smux_oid = smux_oid_dup (oid, oid_len);
1354: smux_oid_len = oid_len;
1355:
1356: if (passwd_str)
1357: smux_passwd = strdup (passwd_str);
1358: else
1359: smux_passwd = strdup ("");
1360:
1361: return 0;
1362: }
1363:
1364: int
1365: smux_header_generic (struct variable *v, oid *name, size_t *length, int exact,
1366: size_t *var_len, WriteMethod **write_method)
1367: {
1368: oid fulloid[MAX_OID_LEN];
1369: int ret;
1370:
1371: oid_copy (fulloid, v->name, v->namelen);
1372: fulloid[v->namelen] = 0;
1373: /* Check against full instance. */
1374: ret = oid_compare (name, *length, fulloid, v->namelen + 1);
1375:
1376: /* Check single instance. */
1377: if ((exact && (ret != 0)) || (!exact && (ret >= 0)))
1378: return MATCH_FAILED;
1379:
1380: /* In case of getnext, fill in full instance. */
1381: memcpy (name, fulloid, (v->namelen + 1) * sizeof (oid));
1382: *length = v->namelen + 1;
1383:
1384: *write_method = 0;
1385: *var_len = sizeof(long); /* default to 'long' results */
1386:
1387: return MATCH_SUCCEEDED;
1388: }
1389:
1390: static int
1391: smux_peer_default (void)
1392: {
1393: if (smux_oid)
1394: {
1395: free (smux_oid);
1396: smux_oid = NULL;
1397: }
1398:
1399: /* careful, smux_passwd might be pointing at string constant */
1400: if (smux_passwd)
1401: {
1402: free (smux_passwd);
1403: smux_passwd = NULL;
1404: }
1405:
1406: return CMD_SUCCESS;
1407: }
1408:
1409: DEFUN (smux_peer,
1410: smux_peer_cmd,
1411: "smux peer OID",
1412: "SNMP MUX protocol settings\n"
1413: "SNMP MUX peer settings\n"
1414: "Object ID used in SMUX peering\n")
1415: {
1416: if (smux_peer_oid (vty, argv[0], NULL) == 0)
1417: {
1418: smux_start();
1419: return CMD_SUCCESS;
1420: }
1421: else
1422: return CMD_WARNING;
1423: }
1424:
1425: DEFUN (smux_peer_password,
1426: smux_peer_password_cmd,
1427: "smux peer OID PASSWORD",
1428: "SNMP MUX protocol settings\n"
1429: "SNMP MUX peer settings\n"
1430: "SMUX peering object ID\n"
1431: "SMUX peering password\n")
1432: {
1433: if (smux_peer_oid (vty, argv[0], argv[1]) == 0)
1434: {
1435: smux_start();
1436: return CMD_SUCCESS;
1437: }
1438: else
1439: return CMD_WARNING;
1440: }
1441:
1442: DEFUN (no_smux_peer,
1443: no_smux_peer_cmd,
1444: "no smux peer",
1445: NO_STR
1446: "SNMP MUX protocol settings\n"
1447: "SNMP MUX peer settings\n")
1448: {
1449: smux_stop();
1450: return smux_peer_default ();
1451: }
1452:
1453: ALIAS (no_smux_peer,
1454: no_smux_peer_oid_cmd,
1455: "no smux peer OID",
1456: NO_STR
1457: "SNMP MUX protocol settings\n"
1458: "SNMP MUX peer settings\n"
1459: "SMUX peering object ID\n")
1460:
1461: ALIAS (no_smux_peer,
1462: no_smux_peer_oid_password_cmd,
1463: "no smux peer OID PASSWORD",
1464: NO_STR
1465: "SNMP MUX protocol settings\n"
1466: "SNMP MUX peer settings\n"
1467: "SMUX peering object ID\n"
1468: "SMUX peering password\n")
1469:
1470: static int
1471: config_write_smux (struct vty *vty)
1472: {
1473: int first = 1;
1474: unsigned int i;
1475:
1476: if (smux_oid)
1477: {
1478: vty_out (vty, "smux peer ");
1479: for (i = 0; i < smux_oid_len; i++)
1480: {
1481: vty_out (vty, "%s%d", first ? "" : ".", (int) smux_oid[i]);
1482: first = 0;
1483: }
1484: vty_out (vty, " %s%s", smux_passwd, VTY_NEWLINE);
1485: }
1486: return 0;
1487: }
1488:
1489: /* Register subtree to smux master tree. */
1490: void
1491: smux_register_mib (const char *descr, struct variable *var,
1492: size_t width, int num,
1493: oid name[], size_t namelen)
1494: {
1495: struct subtree *tree;
1496:
1497: tree = (struct subtree *)malloc(sizeof(struct subtree));
1498: oid_copy (tree->name, name, namelen);
1499: tree->name_len = namelen;
1500: tree->variables = var;
1501: tree->variables_num = num;
1502: tree->variables_width = width;
1503: tree->registered = 0;
1504: listnode_add_sort(treelist, tree);
1505: }
1506:
1507: /* Compare function to keep treelist sorted */
1508: static int
1509: smux_tree_cmp(struct subtree *tree1, struct subtree *tree2)
1510: {
1511: return oid_compare(tree1->name, tree1->name_len,
1512: tree2->name, tree2->name_len);
1513: }
1514:
1515: /* Initialize some values then schedule first SMUX connection. */
1516: void
1517: smux_init (struct thread_master *tm)
1518: {
1519: /* copy callers thread master */
1520: master = tm;
1521:
1522: /* Make MIB tree. */
1523: treelist = list_new();
1524: treelist->cmp = (int (*)(void *, void *))smux_tree_cmp;
1525:
1526: /* Install commands. */
1527: install_node (&smux_node, config_write_smux);
1528:
1529: install_element (CONFIG_NODE, &smux_peer_cmd);
1530: install_element (CONFIG_NODE, &smux_peer_password_cmd);
1531: install_element (CONFIG_NODE, &no_smux_peer_cmd);
1532: install_element (CONFIG_NODE, &no_smux_peer_oid_cmd);
1533: install_element (CONFIG_NODE, &no_smux_peer_oid_password_cmd);
1534: }
1535:
1536: void
1537: smux_start(void)
1538: {
1539: /* Close any existing connections. */
1540: smux_stop();
1541:
1542: /* Schedule first connection. */
1543: smux_event (SMUX_SCHEDULE, 0);
1544: }
1545: #endif /* HAVE_SNMP */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>