]> git.proxmox.com Git - mirror_frr.git/blob - lib/smux.c
Merge commit '78986c0' into tmp-3.0-master-merge
[mirror_frr.git] / lib / smux.c
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 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
19 */
20
21 #include <zebra.h>
22
23 #ifdef SNMP_SMUX
24 #include <net-snmp/net-snmp-config.h>
25 #include <net-snmp/net-snmp-includes.h>
26
27 #include "log.h"
28 #include "thread.h"
29 #include "linklist.h"
30 #include "command.h"
31 #include <lib/version.h>
32 #include "memory.h"
33 #include "sockunion.h"
34 #include "smux.h"
35
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. */
56 struct subtree {
57 /* Tree's oid. */
58 oid name[MAX_OID_LEN];
59 u_char name_len;
60
61 /* List of the variables. */
62 struct variable *variables;
63
64 /* Length of the variables list. */
65 int variables_num;
66
67 /* Width of the variables list. */
68 int variables_width;
69
70 /* Registered flag. */
71 int registered;
72 };
73
74 #define min(A,B) ((A) < (B) ? (A) : (B))
75
76 enum smux_event { SMUX_SCHEDULE, SMUX_CONNECT, SMUX_READ };
77
78 void smux_event(enum smux_event, int);
79
80
81 /* SMUX socket. */
82 int smux_sock = -1;
83
84 /* SMUX subtree list. */
85 struct list *treelist;
86
87 /* SMUX oid. */
88 oid *smux_oid = NULL;
89 size_t smux_oid_len;
90
91 /* SMUX password. */
92 char *smux_passwd = NULL;
93
94 /* SMUX read threads. */
95 struct thread *smux_read_thread;
96
97 /* SMUX connect thrads. */
98 struct thread *smux_connect_thread;
99
100 /* SMUX debug flag. */
101 int debug_smux = 0;
102
103 /* SMUX failure count. */
104 int fail = 0;
105
106 /* SMUX node. */
107 static struct cmd_node smux_node = {
108 SMUX_NODE, "" /* SMUX has no interface. */
109 };
110
111 /* thread master */
112 static struct thread_master *smux_master;
113
114 static int oid_compare_part(oid *o1, int o1_len, oid *o2, int o2_len)
115 {
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;
128 }
129
130 static void smux_oid_dump(const char *prefix, const oid *oid, size_t oid_len)
131 {
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);
144 }
145
146 static int smux_socket(void)
147 {
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;
184 }
185 freeaddrinfo(res0);
186 if (sock < 0)
187 zlog_warn("Can't connect to SNMP agent with SMUX");
188 return sock;
189 }
190
191 static 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)
194 {
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
213 ptr = asn_build_int(ptr, &len, (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE
214 | ASN_INTEGER),
215 &reqid, sizeof(reqid));
216
217 if (debug_smux)
218 zlog_debug("SMUX GETRSP errstat: %ld", errstat);
219
220 ptr = asn_build_int(ptr, &len, (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE
221 | ASN_INTEGER),
222 &errstat, sizeof(errstat));
223 if (debug_smux)
224 zlog_debug("SMUX GETRSP errindex: %ld", errindex);
225
226 ptr = asn_build_int(ptr, &len, (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE
227 | ASN_INTEGER),
228 &errindex, sizeof(errindex));
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);
250 }
251
252 static 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)
255 {
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;
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
350 static int smux_set(oid *reqid, size_t *reqid_len, u_char val_type, void *val,
351 size_t val_len, int action)
352 {
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;
415 }
416
417 static int smux_get(oid *reqid, size_t *reqid_len, int exact, u_char *val_type,
418 void **val, size_t *val_len)
419 {
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 }
478 }
479 }
480 return SNMP_ERR_NOSUCHNAME;
481 }
482
483 static int smux_getnext(oid *reqid, size_t *reqid_len, int exact,
484 u_char *val_type, void **val, size_t *val_len)
485 {
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 }
566 }
567 }
568 memcpy(reqid, save, savelen * sizeof(oid));
569 *reqid_len = savelen;
570
571 return SNMP_ERR_NOSUCHNAME;
572 }
573
574 /* GET message header. */
575 static u_char *smux_parse_get_header(u_char *ptr, size_t *len, long *reqid)
576 {
577 u_char type;
578 long errstat;
579 long errindex;
580
581 /* Request ID. */
582 ptr = asn_parse_int(ptr, len, &type, reqid, sizeof(*reqid));
583
584 if (debug_smux)
585 zlog_debug("SMUX GET reqid: %d len: %d", (int)*reqid,
586 (int)*len);
587
588 /* Error status. */
589 ptr = asn_parse_int(ptr, len, &type, &errstat, sizeof(errstat));
590
591 if (debug_smux)
592 zlog_debug("SMUX GET errstat %ld len: %zd", errstat, *len);
593
594 /* Error index. */
595 ptr = asn_parse_int(ptr, len, &type, &errindex, sizeof(errindex));
596
597 if (debug_smux)
598 zlog_debug("SMUX GET errindex %ld len: %zd", errindex, *len);
599
600 return ptr;
601 }
602
603 static void smux_parse_set(u_char *ptr, size_t len, int action)
604 {
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);
634 }
635
636 static void smux_parse_get(u_char *ptr, size_t len, int exact)
637 {
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);
669 }
670
671 /* Parse SMUX_CLOSE message. */
672 static void smux_parse_close(u_char *ptr, int len)
673 {
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);
681 }
682
683 /* SMUX_RRSP message. */
684 static void smux_parse_rrsp(u_char *ptr, size_t len)
685 {
686 u_char val;
687 long errstat;
688
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);
693 }
694
695 /* Parse SMUX message. */
696 static int smux_parse(u_char *ptr, size_t len)
697 {
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;
703
704 int len_income = len; /* see note below: YYY */
705 u_char type;
706 u_char rollback;
707
708 rollback = ptr[2]; /* important only for SMUX_SOUT */
709
710 process_rest: /* see note below: YYY */
711
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;
809 }
810
811 /* SMUX message read function. */
812 static int smux_read(struct thread *t)
813 {
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;
863 }
864
865 static int smux_open(int sock)
866 {
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;
887 ptr = asn_build_int(ptr, &len, (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE
888 | ASN_INTEGER),
889 &version, sizeof(version));
890
891 /* SMUX connection oid. */
892 ptr = asn_build_objid(ptr, &len, (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE
893 | ASN_OBJECT_ID),
894 smux_oid, smux_oid_len);
895
896 /* SMUX connection description. */
897 ptr = asn_build_string(ptr, &len, (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE
898 | ASN_OCTET_STR),
899 (const u_char *)progname, strlen(progname));
900
901 /* SMUX connection password. */
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));
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);
911 }
912
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. */
918 int 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)
923 {
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. */
944 ptr = asn_build_objid(ptr, &len, (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE
945 | ASN_OBJECT_ID),
946 smux_oid, smux_oid_len);
947
948 /* IP address. */
949 addr.s_addr = 0;
950 ptr = asn_build_string(ptr, &len, (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE
951 | ASN_IPADDRESS),
952 (u_char *)&addr, sizeof(addr));
953
954 /* Generic trap integer. */
955 val = SNMP_TRAP_ENTERPRISESPECIFIC;
956 ptr = asn_build_int(ptr, &len, (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE
957 | ASN_INTEGER),
958 (long *)&val, sizeof(val));
959
960 /* Specific trap integer. */
961 val = sptrap;
962 ptr = asn_build_int(ptr, &len, (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE
963 | ASN_INTEGER),
964 (long *)&val, sizeof(val));
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);
1038 }
1039
1040 static int smux_register(int sock)
1041 {
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;
1094 }
1095
1096 /* Try to connect to SNMP agent. */
1097 static int smux_connect(struct thread *t)
1098 {
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;
1143 }
1144
1145 /* Clear all SMUX related resources. */
1146 static void smux_stop(void)
1147 {
1148 if (smux_read_thread) {
1149 thread_cancel(smux_read_thread);
1150 smux_read_thread = NULL;
1151 }
1152
1153 if (smux_connect_thread) {
1154 thread_cancel(smux_connect_thread);
1155 smux_connect_thread = NULL;
1156 }
1157
1158 if (smux_sock >= 0) {
1159 close(smux_sock);
1160 smux_sock = -1;
1161 }
1162 }
1163
1164
1165 void smux_event(enum smux_event event, int sock)
1166 {
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 }
1186 }
1187
1188 static int smux_str2oid(const char *str, oid *oid, size_t *oid_len)
1189 {
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 }
1211
1212 if (*str == '\0')
1213 break;
1214 if (*str != '.')
1215 return -1;
1216
1217 oid[len++] = val;
1218 val = 0;
1219 str++;
1220 }
1221
1222 oid[len++] = val;
1223 *oid_len = len;
1224
1225 return 0;
1226 }
1227
1228 static oid *smux_oid_dup(oid *objid, size_t objid_len)
1229 {
1230 oid *new;
1231
1232 new = XMALLOC(MTYPE_TMP, sizeof(oid) * objid_len);
1233 oid_copy(new, objid, objid_len);
1234
1235 return new;
1236 }
1237
1238 static int smux_peer_oid(struct vty *vty, const char *oid_str,
1239 const char *passwd_str)
1240 {
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;
1271 }
1272
1273 static int smux_peer_default(void)
1274 {
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;
1287 }
1288
1289 DEFUN (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 {
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;
1302 }
1303
1304 DEFUN (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 {
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;
1318 }
1319
1320 DEFUN (no_smux_peer,
1321 no_smux_peer_cmd,
1322 "no smux peer [OID [PASSWORD]]",
1323 NO_STR
1324 "SNMP MUX protocol settings\n"
1325 "SNMP MUX peer settings\n"
1326 "SMUX peering object ID\n"
1327 "SMUX peering password\n")
1328 {
1329 smux_stop();
1330 return smux_peer_default();
1331 }
1332
1333 static int config_write_smux(struct vty *vty)
1334 {
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);
1346 }
1347 return 0;
1348 }
1349
1350 /* Register subtree to smux master tree. */
1351 void smux_register_mib(const char *descr, struct variable *var, size_t width,
1352 int num, oid name[], size_t namelen)
1353 {
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);
1364 }
1365
1366 /* Compare function to keep treelist sorted */
1367 static int smux_tree_cmp(struct subtree *tree1, struct subtree *tree2)
1368 {
1369 return oid_compare(tree1->name, tree1->name_len, tree2->name,
1370 tree2->name_len);
1371 }
1372
1373 /* Initialize some values then schedule first SMUX connection. */
1374 void smux_init(struct thread_master *tm)
1375 {
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);
1392 }
1393
1394 void smux_start(void)
1395 {
1396 /* Close any existing connections. */
1397 smux_stop();
1398
1399 /* Schedule first connection. */
1400 smux_event(SMUX_SCHEDULE, 0);
1401 }
1402 #endif /* SNMP_SMUX */