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