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