ripd: fix compiler warnings
[jleu-quagga.git] / lib / smux.c
blob1941cf8ca76fdb080c207d2d498a132d71596697
1 /* SNMP support
2 * Copyright (C) 1999 Kunihiro Ishiguro <kunihiro@zebra.org>
4 * This file is part of GNU Zebra.
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.
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.
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.
22 #include <zebra.h>
24 #ifdef HAVE_SNMP
25 #ifdef HAVE_NETSNMP
26 #include <net-snmp/net-snmp-config.h>
27 #include <net-snmp/net-snmp-includes.h>
28 #else
29 #include <asn1.h>
30 #include <snmp.h>
31 #include <snmp_impl.h>
32 #endif
34 #include "log.h"
35 #include "thread.h"
36 #include "linklist.h"
37 #include "command.h"
38 #include <lib/version.h>
39 #include "memory.h"
40 #include "sockunion.h"
41 #include "smux.h"
43 #define min(A,B) ((A) < (B) ? (A) : (B))
45 enum smux_event {SMUX_SCHEDULE, SMUX_CONNECT, SMUX_READ};
47 void smux_event (enum smux_event, int);
50 /* SMUX socket. */
51 int smux_sock = -1;
53 /* SMUX subtree list. */
54 struct list *treelist;
56 /* SMUX oid. */
57 oid *smux_oid = NULL;
58 size_t smux_oid_len;
60 /* SMUX password. */
61 char *smux_passwd = NULL;
63 /* SMUX read threads. */
64 struct thread *smux_read_thread;
66 /* SMUX connect thrads. */
67 struct thread *smux_connect_thread;
69 /* SMUX debug flag. */
70 int debug_smux = 0;
72 /* SMUX failure count. */
73 int fail = 0;
75 /* SMUX node. */
76 static struct cmd_node smux_node =
78 SMUX_NODE,
79 "" /* SMUX has no interface. */
82 /* thread master */
83 static struct thread_master *master;
85 void *
86 oid_copy (void *dest, const void *src, size_t size)
88 return memcpy (dest, src, size * sizeof (oid));
91 void
92 oid2in_addr (oid oid[], int len, struct in_addr *addr)
94 int i;
95 u_char *pnt;
97 if (len == 0)
98 return;
100 pnt = (u_char *) addr;
102 for (i = 0; i < len; i++)
103 *pnt++ = oid[i];
106 void
107 oid_copy_addr (oid oid[], struct in_addr *addr, int len)
109 int i;
110 u_char *pnt;
112 if (len == 0)
113 return;
115 pnt = (u_char *) addr;
117 for (i = 0; i < len; i++)
118 oid[i] = *pnt++;
122 oid_compare (oid *o1, int o1_len, oid *o2, int o2_len)
124 int i;
126 for (i = 0; i < min (o1_len, o2_len); i++)
128 if (o1[i] < o2[i])
129 return -1;
130 else if (o1[i] > o2[i])
131 return 1;
133 if (o1_len < o2_len)
134 return -1;
135 if (o1_len > o2_len)
136 return 1;
138 return 0;
141 static int
142 oid_compare_part (oid *o1, int o1_len, oid *o2, int o2_len)
144 int i;
146 for (i = 0; i < min (o1_len, o2_len); i++)
148 if (o1[i] < o2[i])
149 return -1;
150 else if (o1[i] > o2[i])
151 return 1;
153 if (o1_len < o2_len)
154 return -1;
156 return 0;
159 static void
160 smux_oid_dump (const char *prefix, const oid *oid, size_t oid_len)
162 unsigned int i;
163 int first = 1;
164 char buf[MAX_OID_LEN * 3];
166 buf[0] = '\0';
168 for (i = 0; i < oid_len; i++)
170 sprintf (buf + strlen (buf), "%s%d", first ? "" : ".", (int) oid[i]);
171 first = 0;
173 zlog_debug ("%s: %s", prefix, buf);
176 static int
177 smux_socket (void)
179 int ret;
180 #ifdef HAVE_IPV6
181 struct addrinfo hints, *res0, *res;
182 int gai;
183 #else
184 struct sockaddr_in serv;
185 struct servent *sp;
186 #endif
187 int sock = 0;
189 #ifdef HAVE_IPV6
190 memset(&hints, 0, sizeof(hints));
191 hints.ai_family = PF_UNSPEC;
192 hints.ai_socktype = SOCK_STREAM;
193 gai = getaddrinfo(NULL, "smux", &hints, &res0);
194 if (gai == EAI_SERVICE)
196 char servbuf[NI_MAXSERV];
197 sprintf(servbuf,"%d",SMUX_PORT_DEFAULT);
198 servbuf[sizeof (servbuf) - 1] = '\0';
199 gai = getaddrinfo(NULL, servbuf, &hints, &res0);
201 if (gai)
203 zlog_warn("Cannot locate loopback service smux");
204 return -1;
206 for(res=res0; res; res=res->ai_next)
208 if (res->ai_family != AF_INET
209 #ifdef HAVE_IPV6
210 && res->ai_family != AF_INET6
211 #endif /* HAVE_IPV6 */
213 continue;
215 sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
216 if (sock < 0)
217 continue;
218 sockopt_reuseaddr (sock);
219 sockopt_reuseport (sock);
220 ret = connect (sock, res->ai_addr, res->ai_addrlen);
221 if (ret < 0)
223 close(sock);
224 sock = -1;
225 continue;
227 break;
229 freeaddrinfo(res0);
230 if (sock < 0)
231 zlog_warn ("Can't connect to SNMP agent with SMUX");
232 #else
233 sock = socket (AF_INET, SOCK_STREAM, 0);
234 if (sock < 0)
236 zlog_warn ("Can't make socket for SNMP");
237 return -1;
240 memset (&serv, 0, sizeof (struct sockaddr_in));
241 serv.sin_family = AF_INET;
242 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
243 serv.sin_len = sizeof (struct sockaddr_in);
244 #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
246 sp = getservbyname ("smux", "tcp");
247 if (sp != NULL)
248 serv.sin_port = sp->s_port;
249 else
250 serv.sin_port = htons (SMUX_PORT_DEFAULT);
252 serv.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
254 sockopt_reuseaddr (sock);
255 sockopt_reuseport (sock);
257 ret = connect (sock, (struct sockaddr *) &serv, sizeof (struct sockaddr_in));
258 if (ret < 0)
260 close (sock);
261 smux_sock = -1;
262 zlog_warn ("Can't connect to SNMP agent with SMUX");
263 return -1;
265 #endif
266 return sock;
269 static void
270 smux_getresp_send (oid objid[], size_t objid_len, long reqid, long errstat,
271 long errindex, u_char val_type, void *arg, size_t arg_len)
273 int ret;
274 u_char buf[BUFSIZ];
275 u_char *ptr, *h1, *h1e, *h2, *h2e;
276 size_t len, length;
278 ptr = buf;
279 len = BUFSIZ;
280 length = len;
282 if (debug_smux)
284 zlog_debug ("SMUX GETRSP send");
285 zlog_debug ("SMUX GETRSP reqid: %ld", reqid);
288 h1 = ptr;
289 /* Place holder h1 for complete sequence */
290 ptr = asn_build_sequence (ptr, &len, (u_char) SMUX_GETRSP, 0);
291 h1e = ptr;
293 ptr = asn_build_int (ptr, &len,
294 (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
295 &reqid, sizeof (reqid));
297 if (debug_smux)
298 zlog_debug ("SMUX GETRSP errstat: %ld", errstat);
300 ptr = asn_build_int (ptr, &len,
301 (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
302 &errstat, sizeof (errstat));
303 if (debug_smux)
304 zlog_debug ("SMUX GETRSP errindex: %ld", errindex);
306 ptr = asn_build_int (ptr, &len,
307 (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
308 &errindex, sizeof (errindex));
310 h2 = ptr;
311 /* Place holder h2 for one variable */
312 ptr = asn_build_sequence (ptr, &len,
313 (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR),
315 h2e = ptr;
317 ptr = snmp_build_var_op (ptr, objid, &objid_len,
318 val_type, arg_len, arg, &len);
320 /* Now variable size is known, fill in size */
321 asn_build_sequence(h2,&length,(u_char)(ASN_SEQUENCE|ASN_CONSTRUCTOR),ptr-h2e);
323 /* Fill in size of whole sequence */
324 asn_build_sequence(h1,&length,(u_char)SMUX_GETRSP,ptr-h1e);
326 if (debug_smux)
327 zlog_debug ("SMUX getresp send: %td", (ptr - buf));
329 ret = send (smux_sock, buf, (ptr - buf), 0);
332 static u_char *
333 smux_var (u_char *ptr, size_t len, oid objid[], size_t *objid_len,
334 size_t *var_val_len,
335 u_char *var_val_type,
336 void **var_value)
338 u_char type;
339 u_char val_type;
340 size_t val_len;
341 u_char *val;
343 if (debug_smux)
344 zlog_debug ("SMUX var parse: len %zd", len);
346 /* Parse header. */
347 ptr = asn_parse_header (ptr, &len, &type);
349 if (debug_smux)
351 zlog_debug ("SMUX var parse: type %d len %zd", type, len);
352 zlog_debug ("SMUX var parse: type must be %d",
353 (ASN_SEQUENCE | ASN_CONSTRUCTOR));
356 /* Parse var option. */
357 *objid_len = MAX_OID_LEN;
358 ptr = snmp_parse_var_op(ptr, objid, objid_len, &val_type,
359 &val_len, &val, &len);
361 if (var_val_len)
362 *var_val_len = val_len;
364 if (var_value)
365 *var_value = (void*) val;
367 if (var_val_type)
368 *var_val_type = val_type;
370 /* Requested object id length is objid_len. */
371 if (debug_smux)
372 smux_oid_dump ("Request OID", objid, *objid_len);
374 if (debug_smux)
375 zlog_debug ("SMUX val_type: %d", val_type);
377 /* Check request value type. */
378 if (debug_smux)
379 switch (val_type)
381 case ASN_NULL:
382 /* In case of SMUX_GET or SMUX_GET_NEXT val_type is set to
383 ASN_NULL. */
384 zlog_debug ("ASN_NULL");
385 break;
387 case ASN_INTEGER:
388 zlog_debug ("ASN_INTEGER");
389 break;
390 case ASN_COUNTER:
391 case ASN_GAUGE:
392 case ASN_TIMETICKS:
393 case ASN_UINTEGER:
394 zlog_debug ("ASN_COUNTER");
395 break;
396 case ASN_COUNTER64:
397 zlog_debug ("ASN_COUNTER64");
398 break;
399 case ASN_IPADDRESS:
400 zlog_debug ("ASN_IPADDRESS");
401 break;
402 case ASN_OCTET_STR:
403 zlog_debug ("ASN_OCTET_STR");
404 break;
405 case ASN_OPAQUE:
406 case ASN_NSAP:
407 case ASN_OBJECT_ID:
408 zlog_debug ("ASN_OPAQUE");
409 break;
410 case SNMP_NOSUCHOBJECT:
411 zlog_debug ("SNMP_NOSUCHOBJECT");
412 break;
413 case SNMP_NOSUCHINSTANCE:
414 zlog_debug ("SNMP_NOSUCHINSTANCE");
415 break;
416 case SNMP_ENDOFMIBVIEW:
417 zlog_debug ("SNMP_ENDOFMIBVIEW");
418 break;
419 case ASN_BIT_STR:
420 zlog_debug ("ASN_BIT_STR");
421 break;
422 default:
423 zlog_debug ("Unknown type");
424 break;
426 return ptr;
429 /* NOTE: all 3 functions (smux_set, smux_get & smux_getnext) are based on
430 ucd-snmp smux and as such suppose, that the peer receives in the message
431 only one variable. Fortunately, IBM seems to do the same in AIX. */
433 static int
434 smux_set (oid *reqid, size_t *reqid_len,
435 u_char val_type, void *val, size_t val_len, int action)
437 int j;
438 struct subtree *subtree;
439 struct variable *v;
440 int subresult;
441 oid *suffix;
442 size_t suffix_len;
443 int result;
444 u_char *statP = NULL;
445 WriteMethod *write_method = NULL;
446 struct listnode *node, *nnode;
448 /* Check */
449 for (ALL_LIST_ELEMENTS (treelist, node, nnode, subtree))
451 subresult = oid_compare_part (reqid, *reqid_len,
452 subtree->name, subtree->name_len);
454 /* Subtree matched. */
455 if (subresult == 0)
457 /* Prepare suffix. */
458 suffix = reqid + subtree->name_len;
459 suffix_len = *reqid_len - subtree->name_len;
460 result = subresult;
462 /* Check variables. */
463 for (j = 0; j < subtree->variables_num; j++)
465 v = &subtree->variables[j];
467 /* Always check suffix */
468 result = oid_compare_part (suffix, suffix_len,
469 v->name, v->namelen);
471 /* This is exact match so result must be zero. */
472 if (result == 0)
474 if (debug_smux)
475 zlog_debug ("SMUX function call index is %d", v->magic);
477 statP = (*v->findVar) (v, suffix, &suffix_len, 1,
478 &val_len, &write_method);
480 if (write_method)
482 return (*write_method)(action, val, val_type, val_len,
483 statP, suffix, suffix_len, v);
485 else
487 return SNMP_ERR_READONLY;
491 /* If above execution is failed or oid is small (so
492 there is no further match). */
493 if (result < 0)
494 return SNMP_ERR_NOSUCHNAME;
498 return SNMP_ERR_NOSUCHNAME;
501 static int
502 smux_get (oid *reqid, size_t *reqid_len, int exact,
503 u_char *val_type,void **val, size_t *val_len)
505 int j;
506 struct subtree *subtree;
507 struct variable *v;
508 int subresult;
509 oid *suffix;
510 size_t suffix_len;
511 int result;
512 WriteMethod *write_method=NULL;
513 struct listnode *node, *nnode;
515 /* Check */
516 for (ALL_LIST_ELEMENTS (treelist, node, nnode,subtree))
518 subresult = oid_compare_part (reqid, *reqid_len,
519 subtree->name, subtree->name_len);
521 /* Subtree matched. */
522 if (subresult == 0)
524 /* Prepare suffix. */
525 suffix = reqid + subtree->name_len;
526 suffix_len = *reqid_len - subtree->name_len;
527 result = subresult;
529 /* Check variables. */
530 for (j = 0; j < subtree->variables_num; j++)
532 v = &subtree->variables[j];
534 /* Always check suffix */
535 result = oid_compare_part (suffix, suffix_len,
536 v->name, v->namelen);
538 /* This is exact match so result must be zero. */
539 if (result == 0)
541 if (debug_smux)
542 zlog_debug ("SMUX function call index is %d", v->magic);
544 *val = (*v->findVar) (v, suffix, &suffix_len, exact,
545 val_len, &write_method);
547 /* There is no instance. */
548 if (*val == NULL)
549 return SNMP_NOSUCHINSTANCE;
551 /* Call is suceed. */
552 *val_type = v->type;
554 return 0;
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;
564 return SNMP_ERR_NOSUCHNAME;
567 static int
568 smux_getnext (oid *reqid, size_t *reqid_len, int exact,
569 u_char *val_type,void **val, size_t *val_len)
571 int j;
572 oid save[MAX_OID_LEN];
573 int savelen = 0;
574 struct subtree *subtree;
575 struct variable *v;
576 int subresult;
577 oid *suffix;
578 size_t suffix_len;
579 int result;
580 WriteMethod *write_method=NULL;
581 struct listnode *node, *nnode;
584 /* Save incoming request. */
585 oid_copy (save, reqid, *reqid_len);
586 savelen = *reqid_len;
588 /* Check */
589 for (ALL_LIST_ELEMENTS (treelist, node, nnode, subtree))
591 subresult = oid_compare_part (reqid, *reqid_len,
592 subtree->name, subtree->name_len);
594 /* If request is in the tree. The agent has to make sure we
595 only receive requests we have registered for. */
596 /* Unfortunately, that's not true. In fact, a SMUX subagent has to
597 behave as if it manages the whole SNMP MIB tree itself. It's the
598 duty of the master agent to collect the best answer and return it
599 to the manager. See RFC 1227 chapter 3.1.6 for the glory details
600 :-). ucd-snmp really behaves bad here as it actually might ask
601 multiple times for the same GETNEXT request as it throws away the
602 answer when it expects it in a different subtree and might come
603 back later with the very same request. --jochen */
605 if (subresult <= 0)
607 /* Prepare suffix. */
608 suffix = reqid + subtree->name_len;
609 suffix_len = *reqid_len - subtree->name_len;
610 if (subresult < 0)
612 oid_copy(reqid, subtree->name, subtree->name_len);
613 *reqid_len = subtree->name_len;
615 for (j = 0; j < subtree->variables_num; j++)
617 result = subresult;
618 v = &subtree->variables[j];
620 /* Next then check result >= 0. */
621 if (result == 0)
622 result = oid_compare_part (suffix, suffix_len,
623 v->name, v->namelen);
625 if (result <= 0)
627 if (debug_smux)
628 zlog_debug ("SMUX function call index is %d", v->magic);
629 if(result<0)
631 oid_copy(suffix, v->name, v->namelen);
632 suffix_len = v->namelen;
634 *val = (*v->findVar) (v, suffix, &suffix_len, exact,
635 val_len, &write_method);
636 *reqid_len = suffix_len + subtree->name_len;
637 if (*val)
639 *val_type = v->type;
640 return 0;
646 memcpy (reqid, save, savelen * sizeof(oid));
647 *reqid_len = savelen;
649 return SNMP_ERR_NOSUCHNAME;
652 /* GET message header. */
653 static u_char *
654 smux_parse_get_header (u_char *ptr, size_t *len, long *reqid)
656 u_char type;
657 long errstat;
658 long errindex;
660 /* Request ID. */
661 ptr = asn_parse_int (ptr, len, &type, reqid, sizeof (*reqid));
663 if (debug_smux)
664 zlog_debug ("SMUX GET reqid: %d len: %d", (int) *reqid, (int) *len);
666 /* Error status. */
667 ptr = asn_parse_int (ptr, len, &type, &errstat, sizeof (errstat));
669 if (debug_smux)
670 zlog_debug ("SMUX GET errstat %ld len: %zd", errstat, *len);
672 /* Error index. */
673 ptr = asn_parse_int (ptr, len, &type, &errindex, sizeof (errindex));
675 if (debug_smux)
676 zlog_debug ("SMUX GET errindex %ld len: %zd", errindex, *len);
678 return ptr;
681 static void
682 smux_parse_set (u_char *ptr, size_t len, int action)
684 long reqid;
685 oid oid[MAX_OID_LEN];
686 size_t oid_len;
687 u_char val_type;
688 void *val;
689 size_t val_len;
690 int ret;
692 if (debug_smux)
693 zlog_debug ("SMUX SET(%s) message parse: len %zd",
694 (RESERVE1 == action) ? "RESERVE1" : ((FREE == action) ? "FREE" : "COMMIT"),
695 len);
697 /* Parse SET message header. */
698 ptr = smux_parse_get_header (ptr, &len, &reqid);
700 /* Parse SET message object ID. */
701 ptr = smux_var (ptr, len, oid, &oid_len, &val_len, &val_type, &val);
703 ret = smux_set (oid, &oid_len, val_type, val, val_len, action);
704 if (debug_smux)
705 zlog_debug ("SMUX SET ret %d", ret);
707 /* Return result. */
708 if (RESERVE1 == action)
709 smux_getresp_send (oid, oid_len, reqid, ret, 3, ASN_NULL, NULL, 0);
712 static void
713 smux_parse_get (u_char *ptr, size_t len, int exact)
715 long reqid;
716 oid oid[MAX_OID_LEN];
717 size_t oid_len;
718 u_char val_type;
719 void *val;
720 size_t val_len;
721 int ret;
723 if (debug_smux)
724 zlog_debug ("SMUX GET message parse: len %zd", len);
726 /* Parse GET message header. */
727 ptr = smux_parse_get_header (ptr, &len, &reqid);
729 /* Parse GET message object ID. We needn't the value come */
730 ptr = smux_var (ptr, len, oid, &oid_len, NULL, NULL, NULL);
732 /* Traditional getstatptr. */
733 if (exact)
734 ret = smux_get (oid, &oid_len, exact, &val_type, &val, &val_len);
735 else
736 ret = smux_getnext (oid, &oid_len, exact, &val_type, &val, &val_len);
738 /* Return result. */
739 if (ret == 0)
740 smux_getresp_send (oid, oid_len, reqid, 0, 0, val_type, val, val_len);
741 else
742 smux_getresp_send (oid, oid_len, reqid, ret, 3, ASN_NULL, NULL, 0);
745 /* Parse SMUX_CLOSE message. */
746 static void
747 smux_parse_close (u_char *ptr, int len)
749 long reason = 0;
751 while (len--)
753 reason = (reason << 8) | (long) *ptr;
754 ptr++;
756 zlog_info ("SMUX_CLOSE with reason: %ld", reason);
759 /* SMUX_RRSP message. */
760 static void
761 smux_parse_rrsp (u_char *ptr, size_t len)
763 u_char val;
764 long errstat;
766 ptr = asn_parse_int (ptr, &len, &val, &errstat, sizeof (errstat));
768 if (debug_smux)
769 zlog_debug ("SMUX_RRSP value: %d errstat: %ld", val, errstat);
772 /* Parse SMUX message. */
773 static int
774 smux_parse (u_char *ptr, size_t len)
776 /* This buffer we'll use for SOUT message. We could allocate it with
777 malloc and save only static pointer/lenght, but IMHO static
778 buffer is a faster solusion. */
779 static u_char sout_save_buff[SMUXMAXPKTSIZE];
780 static int sout_save_len = 0;
782 int len_income = len; /* see note below: YYY */
783 u_char type;
784 u_char rollback;
786 rollback = ptr[2]; /* important only for SMUX_SOUT */
788 process_rest: /* see note below: YYY */
790 /* Parse SMUX message type and subsequent length. */
791 ptr = asn_parse_header (ptr, &len, &type);
793 if (debug_smux)
794 zlog_debug ("SMUX message received type: %d rest len: %zd", type, len);
796 switch (type)
798 case SMUX_OPEN:
799 /* Open must be not send from SNMP agent. */
800 zlog_warn ("SMUX_OPEN received: resetting connection.");
801 return -1;
802 break;
803 case SMUX_RREQ:
804 /* SMUX_RREQ message is invalid for us. */
805 zlog_warn ("SMUX_RREQ received: resetting connection.");
806 return -1;
807 break;
808 case SMUX_SOUT:
809 /* SMUX_SOUT message is now valied for us. */
810 if (debug_smux)
811 zlog_debug ("SMUX_SOUT(%s)", rollback ? "rollback" : "commit");
813 if (sout_save_len > 0)
815 smux_parse_set (sout_save_buff, sout_save_len, rollback ? FREE : COMMIT);
816 sout_save_len = 0;
818 else
819 zlog_warn ("SMUX_SOUT sout_save_len=%d - invalid", (int) sout_save_len);
821 if (len_income > 3)
823 /* YYY: this strange code has to solve the "slow peer"
824 problem: When agent sends SMUX_SOUT message it doesn't
825 wait any responce and may send some next message to
826 subagent. Then the peer in 'smux_read()' will recieve
827 from socket the 'concatenated' buffer, contaning both
828 SMUX_SOUT message and the next one
829 (SMUX_GET/SMUX_GETNEXT/SMUX_GET). So we should check: if
830 the buffer is longer than 3 ( length of SMUX_SOUT ), we
831 must process the rest of it. This effect may be observed
832 if 'debug_smux' is set to '1' */
833 ptr++;
834 len = len_income - 3;
835 goto process_rest;
837 break;
838 case SMUX_GETRSP:
839 /* SMUX_GETRSP message is invalid for us. */
840 zlog_warn ("SMUX_GETRSP received: resetting connection.");
841 return -1;
842 break;
843 case SMUX_CLOSE:
844 /* Close SMUX connection. */
845 if (debug_smux)
846 zlog_debug ("SMUX_CLOSE");
847 smux_parse_close (ptr, len);
848 return -1;
849 break;
850 case SMUX_RRSP:
851 /* This is response for register message. */
852 if (debug_smux)
853 zlog_debug ("SMUX_RRSP");
854 smux_parse_rrsp (ptr, len);
855 break;
856 case SMUX_GET:
857 /* Exact request for object id. */
858 if (debug_smux)
859 zlog_debug ("SMUX_GET");
860 smux_parse_get (ptr, len, 1);
861 break;
862 case SMUX_GETNEXT:
863 /* Next request for object id. */
864 if (debug_smux)
865 zlog_debug ("SMUX_GETNEXT");
866 smux_parse_get (ptr, len, 0);
867 break;
868 case SMUX_SET:
869 /* SMUX_SET is supported with some limitations. */
870 if (debug_smux)
871 zlog_debug ("SMUX_SET");
873 /* save the data for future SMUX_SOUT */
874 memcpy (sout_save_buff, ptr, len);
875 sout_save_len = len;
876 smux_parse_set (ptr, len, RESERVE1);
877 break;
878 default:
879 zlog_info ("Unknown type: %d", type);
880 break;
882 return 0;
885 /* SMUX message read function. */
886 static int
887 smux_read (struct thread *t)
889 int sock;
890 int len;
891 u_char buf[SMUXMAXPKTSIZE];
892 int ret;
894 /* Clear thread. */
895 sock = THREAD_FD (t);
896 smux_read_thread = NULL;
898 if (debug_smux)
899 zlog_debug ("SMUX read start");
901 /* Read message from SMUX socket. */
902 len = recv (sock, buf, SMUXMAXPKTSIZE, 0);
904 if (len < 0)
906 zlog_warn ("Can't read all SMUX packet: %s", safe_strerror (errno));
907 close (sock);
908 smux_sock = -1;
909 smux_event (SMUX_CONNECT, 0);
910 return -1;
913 if (len == 0)
915 zlog_warn ("SMUX connection closed: %d", sock);
916 close (sock);
917 smux_sock = -1;
918 smux_event (SMUX_CONNECT, 0);
919 return -1;
922 if (debug_smux)
923 zlog_debug ("SMUX read len: %d", len);
925 /* Parse the message. */
926 ret = smux_parse (buf, len);
928 if (ret < 0)
930 close (sock);
931 smux_sock = -1;
932 smux_event (SMUX_CONNECT, 0);
933 return -1;
936 /* Regiser read thread. */
937 smux_event (SMUX_READ, sock);
939 return 0;
942 static int
943 smux_open (int sock)
945 u_char buf[BUFSIZ];
946 u_char *ptr;
947 size_t len;
948 long version;
949 u_char progname[] = QUAGGA_PROGNAME "-" QUAGGA_VERSION;
951 if (debug_smux)
953 smux_oid_dump ("SMUX open oid", smux_oid, smux_oid_len);
954 zlog_debug ("SMUX open progname: %s", progname);
955 zlog_debug ("SMUX open password: %s", smux_passwd);
958 ptr = buf;
959 len = BUFSIZ;
961 /* SMUX Header. As placeholder. */
962 ptr = asn_build_header (ptr, &len, (u_char) SMUX_OPEN, 0);
964 /* SMUX Open. */
965 version = 0;
966 ptr = asn_build_int (ptr, &len,
967 (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
968 &version, sizeof (version));
970 /* SMUX connection oid. */
971 ptr = asn_build_objid (ptr, &len,
972 (u_char)
973 (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID),
974 smux_oid, smux_oid_len);
976 /* SMUX connection description. */
977 ptr = asn_build_string (ptr, &len,
978 (u_char)
979 (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR),
980 progname, strlen (progname));
982 /* SMUX connection password. */
983 ptr = asn_build_string (ptr, &len,
984 (u_char)
985 (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR),
986 (u_char *)smux_passwd, strlen (smux_passwd));
988 /* Fill in real SMUX header. We exclude ASN header size (2). */
989 len = BUFSIZ;
990 asn_build_header (buf, &len, (u_char) SMUX_OPEN, (ptr - buf) - 2);
992 return send (sock, buf, (ptr - buf), 0);
996 smux_trap (const oid *name, size_t namelen,
997 const oid *iname, size_t inamelen,
998 const struct trap_object *trapobj, size_t trapobjlen,
999 unsigned int tick, u_char sptrap)
1001 unsigned int i;
1002 u_char buf[BUFSIZ];
1003 u_char *ptr;
1004 size_t len, length;
1005 struct in_addr addr;
1006 unsigned long val;
1007 u_char *h1, *h1e;
1009 ptr = buf;
1010 len = BUFSIZ;
1011 length = len;
1013 /* When SMUX connection is not established. */
1014 if (smux_sock < 0)
1015 return 0;
1017 /* SMUX header. */
1018 ptr = asn_build_header (ptr, &len, (u_char) SMUX_TRAP, 0);
1020 /* Sub agent enterprise oid. */
1021 ptr = asn_build_objid (ptr, &len,
1022 (u_char)
1023 (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID),
1024 smux_oid, smux_oid_len);
1026 /* IP address. */
1027 addr.s_addr = 0;
1028 ptr = asn_build_string (ptr, &len,
1029 (u_char)
1030 (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_IPADDRESS),
1031 (u_char *)&addr, sizeof (addr));
1033 /* Generic trap integer. */
1034 val = SNMP_TRAP_ENTERPRISESPECIFIC;
1035 ptr = asn_build_int (ptr, &len,
1036 (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
1037 (long *)&val, sizeof (val));
1039 /* Specific trap integer. */
1040 val = sptrap;
1041 ptr = asn_build_int (ptr, &len,
1042 (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
1043 (long *)&val, sizeof (val));
1045 /* Timeticks timestamp. */
1046 val = 0;
1047 ptr = asn_build_unsigned_int (ptr, &len,
1048 (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_TIMETICKS),
1049 &val, sizeof (val));
1051 /* Variables. */
1052 h1 = ptr;
1053 ptr = asn_build_sequence (ptr, &len,
1054 (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR),
1058 /* Iteration for each objects. */
1059 h1e = ptr;
1060 for (i = 0; i < trapobjlen; i++)
1062 int ret;
1063 oid oid[MAX_OID_LEN];
1064 size_t oid_len;
1065 void *val;
1066 size_t val_len;
1067 u_char val_type;
1069 /* Make OID. */
1070 if (trapobj[i].namelen > 0)
1072 oid_copy (oid, name, namelen);
1073 oid_copy (oid + namelen, trapobj[i].name, trapobj[i].namelen);
1074 oid_copy (oid + namelen + trapobj[i].namelen, iname, inamelen);
1075 oid_len = namelen + trapobj[i].namelen + inamelen;
1077 else
1079 oid_copy (oid, name, namelen);
1080 oid_copy (oid + namelen, trapobj[i].name, trapobj[i].namelen * (-1));
1081 oid_len = namelen + trapobj[i].namelen * (-1) ;
1084 if (debug_smux)
1086 smux_oid_dump ("Trap", name, namelen);
1087 if (trapobj[i].namelen < 0)
1088 smux_oid_dump ("Trap",
1089 trapobj[i].name, (- 1) * (trapobj[i].namelen));
1090 else
1092 smux_oid_dump ("Trap", trapobj[i].name, (trapobj[i].namelen));
1093 smux_oid_dump ("Trap", iname, inamelen);
1095 smux_oid_dump ("Trap", oid, oid_len);
1096 zlog_info ("BUFSIZ: %d // oid_len: %lu", BUFSIZ, (u_long)oid_len);
1099 ret = smux_get (oid, &oid_len, 1, &val_type, &val, &val_len);
1101 if (debug_smux)
1102 zlog_debug ("smux_get result %d", ret);
1104 if (ret == 0)
1105 ptr = snmp_build_var_op (ptr, oid, &oid_len,
1106 val_type, val_len, val, &len);
1109 /* Now variable size is known, fill in size */
1110 asn_build_sequence(h1, &length,
1111 (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR),
1112 ptr - h1e);
1114 /* Fill in size of whole sequence */
1115 len = BUFSIZ;
1116 asn_build_header (buf, &len, (u_char) SMUX_TRAP, (ptr - buf) - 2);
1118 return send (smux_sock, buf, (ptr - buf), 0);
1121 static int
1122 smux_register (int sock)
1124 u_char buf[BUFSIZ];
1125 u_char *ptr;
1126 int ret;
1127 size_t len;
1128 long priority;
1129 long operation;
1130 struct subtree *subtree;
1131 struct listnode *node, *nnode;
1133 ret = 0;
1135 for (ALL_LIST_ELEMENTS (treelist, node, nnode, subtree))
1137 ptr = buf;
1138 len = BUFSIZ;
1140 /* SMUX RReq Header. */
1141 ptr = asn_build_header (ptr, &len, (u_char) SMUX_RREQ, 0);
1143 /* Register MIB tree. */
1144 ptr = asn_build_objid (ptr, &len,
1145 (u_char)
1146 (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID),
1147 subtree->name, subtree->name_len);
1149 /* Priority. */
1150 priority = -1;
1151 ptr = asn_build_int (ptr, &len,
1152 (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
1153 &priority, sizeof (priority));
1155 /* Operation. */
1156 operation = 2; /* Register R/W */
1157 ptr = asn_build_int (ptr, &len,
1158 (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
1159 &operation, sizeof (operation));
1161 if (debug_smux)
1163 smux_oid_dump ("SMUX register oid", subtree->name, subtree->name_len);
1164 zlog_debug ("SMUX register priority: %ld", priority);
1165 zlog_debug ("SMUX register operation: %ld", operation);
1168 len = BUFSIZ;
1169 asn_build_header (buf, &len, (u_char) SMUX_RREQ, (ptr - buf) - 2);
1170 ret = send (sock, buf, (ptr - buf), 0);
1171 if (ret < 0)
1172 return ret;
1174 return ret;
1177 /* Try to connect to SNMP agent. */
1178 static int
1179 smux_connect (struct thread *t)
1181 int ret;
1183 if (debug_smux)
1184 zlog_debug ("SMUX connect try %d", fail + 1);
1186 /* Clear thread poner of myself. */
1187 smux_connect_thread = NULL;
1189 /* Make socket. Try to connect. */
1190 smux_sock = smux_socket ();
1191 if (smux_sock < 0)
1193 if (++fail < SMUX_MAX_FAILURE)
1194 smux_event (SMUX_CONNECT, 0);
1195 return 0;
1198 /* Send OPEN PDU. */
1199 ret = smux_open (smux_sock);
1200 if (ret < 0)
1202 zlog_warn ("SMUX open message send failed: %s", safe_strerror (errno));
1203 close (smux_sock);
1204 smux_sock = -1;
1205 if (++fail < SMUX_MAX_FAILURE)
1206 smux_event (SMUX_CONNECT, 0);
1207 return -1;
1210 /* Send any outstanding register PDUs. */
1211 ret = smux_register (smux_sock);
1212 if (ret < 0)
1214 zlog_warn ("SMUX register message send failed: %s", safe_strerror (errno));
1215 close (smux_sock);
1216 smux_sock = -1;
1217 if (++fail < SMUX_MAX_FAILURE)
1218 smux_event (SMUX_CONNECT, 0);
1219 return -1;
1222 /* Everything goes fine. */
1223 smux_event (SMUX_READ, smux_sock);
1225 return 0;
1228 /* Clear all SMUX related resources. */
1229 static void
1230 smux_stop (void)
1232 if (smux_read_thread)
1234 thread_cancel (smux_read_thread);
1235 smux_read_thread = NULL;
1238 if (smux_connect_thread)
1240 thread_cancel (smux_connect_thread);
1241 smux_connect_thread = NULL;
1244 if (smux_sock >= 0)
1246 close (smux_sock);
1247 smux_sock = -1;
1253 void
1254 smux_event (enum smux_event event, int sock)
1256 switch (event)
1258 case SMUX_SCHEDULE:
1259 smux_connect_thread = thread_add_event (master, smux_connect, NULL, 0);
1260 break;
1261 case SMUX_CONNECT:
1262 smux_connect_thread = thread_add_timer (master, smux_connect, NULL, 10);
1263 break;
1264 case SMUX_READ:
1265 smux_read_thread = thread_add_read (master, smux_read, NULL, sock);
1266 break;
1267 default:
1268 break;
1272 static int
1273 smux_str2oid (const char *str, oid *oid, size_t *oid_len)
1275 int len;
1276 int val;
1278 len = 0;
1279 val = 0;
1280 *oid_len = 0;
1282 if (*str == '.')
1283 str++;
1284 if (*str == '\0')
1285 return 0;
1287 while (1)
1289 if (! isdigit (*str))
1290 return -1;
1292 while (isdigit (*str))
1294 val *= 10;
1295 val += (*str - '0');
1296 str++;
1299 if (*str == '\0')
1300 break;
1301 if (*str != '.')
1302 return -1;
1304 oid[len++] = val;
1305 val = 0;
1306 str++;
1309 oid[len++] = val;
1310 *oid_len = len;
1312 return 0;
1315 static oid *
1316 smux_oid_dup (oid *objid, size_t objid_len)
1318 oid *new;
1320 new = XMALLOC (MTYPE_TMP, sizeof (oid) * objid_len);
1321 oid_copy (new, objid, objid_len);
1323 return new;
1326 static int
1327 smux_peer_oid (struct vty *vty, const char *oid_str, const char *passwd_str)
1329 int ret;
1330 oid oid[MAX_OID_LEN];
1331 size_t oid_len;
1333 ret = smux_str2oid (oid_str, oid, &oid_len);
1334 if (ret != 0)
1336 vty_out (vty, "object ID malformed%s", VTY_NEWLINE);
1337 return CMD_WARNING;
1340 if (smux_oid)
1342 free (smux_oid);
1343 smux_oid = NULL;
1346 /* careful, smux_passwd might point to string constant */
1347 if (smux_passwd)
1349 free (smux_passwd);
1350 smux_passwd = NULL;
1353 smux_oid = smux_oid_dup (oid, oid_len);
1354 smux_oid_len = oid_len;
1356 if (passwd_str)
1357 smux_passwd = strdup (passwd_str);
1358 else
1359 smux_passwd = strdup ("");
1361 return 0;
1365 smux_header_generic (struct variable *v, oid *name, size_t *length, int exact,
1366 size_t *var_len, WriteMethod **write_method)
1368 oid fulloid[MAX_OID_LEN];
1369 int ret;
1371 oid_copy (fulloid, v->name, v->namelen);
1372 fulloid[v->namelen] = 0;
1373 /* Check against full instance. */
1374 ret = oid_compare (name, *length, fulloid, v->namelen + 1);
1376 /* Check single instance. */
1377 if ((exact && (ret != 0)) || (!exact && (ret >= 0)))
1378 return MATCH_FAILED;
1380 /* In case of getnext, fill in full instance. */
1381 memcpy (name, fulloid, (v->namelen + 1) * sizeof (oid));
1382 *length = v->namelen + 1;
1384 *write_method = 0;
1385 *var_len = sizeof(long); /* default to 'long' results */
1387 return MATCH_SUCCEEDED;
1390 static int
1391 smux_peer_default (void)
1393 if (smux_oid)
1395 free (smux_oid);
1396 smux_oid = NULL;
1399 /* careful, smux_passwd might be pointing at string constant */
1400 if (smux_passwd)
1402 free (smux_passwd);
1403 smux_passwd = NULL;
1406 return CMD_SUCCESS;
1409 DEFUN (smux_peer,
1410 smux_peer_cmd,
1411 "smux peer OID",
1412 "SNMP MUX protocol settings\n"
1413 "SNMP MUX peer settings\n"
1414 "Object ID used in SMUX peering\n")
1416 if (smux_peer_oid (vty, argv[0], NULL) == 0)
1418 smux_start();
1419 return CMD_SUCCESS;
1421 else
1422 return CMD_WARNING;
1425 DEFUN (smux_peer_password,
1426 smux_peer_password_cmd,
1427 "smux peer OID PASSWORD",
1428 "SNMP MUX protocol settings\n"
1429 "SNMP MUX peer settings\n"
1430 "SMUX peering object ID\n"
1431 "SMUX peering password\n")
1433 if (smux_peer_oid (vty, argv[0], argv[1]) == 0)
1435 smux_start();
1436 return CMD_SUCCESS;
1438 else
1439 return CMD_WARNING;
1442 DEFUN (no_smux_peer,
1443 no_smux_peer_cmd,
1444 "no smux peer",
1445 NO_STR
1446 "SNMP MUX protocol settings\n"
1447 "SNMP MUX peer settings\n")
1449 smux_stop();
1450 return smux_peer_default ();
1453 ALIAS (no_smux_peer,
1454 no_smux_peer_oid_cmd,
1455 "no smux peer OID",
1456 NO_STR
1457 "SNMP MUX protocol settings\n"
1458 "SNMP MUX peer settings\n"
1459 "SMUX peering object ID\n")
1461 ALIAS (no_smux_peer,
1462 no_smux_peer_oid_password_cmd,
1463 "no smux peer OID PASSWORD",
1464 NO_STR
1465 "SNMP MUX protocol settings\n"
1466 "SNMP MUX peer settings\n"
1467 "SMUX peering object ID\n"
1468 "SMUX peering password\n")
1470 static int
1471 config_write_smux (struct vty *vty)
1473 int first = 1;
1474 unsigned int i;
1476 if (smux_oid)
1478 vty_out (vty, "smux peer ");
1479 for (i = 0; i < smux_oid_len; i++)
1481 vty_out (vty, "%s%d", first ? "" : ".", (int) smux_oid[i]);
1482 first = 0;
1484 vty_out (vty, " %s%s", smux_passwd, VTY_NEWLINE);
1486 return 0;
1489 /* Register subtree to smux master tree. */
1490 void
1491 smux_register_mib (const char *descr, struct variable *var,
1492 size_t width, int num,
1493 oid name[], size_t namelen)
1495 struct subtree *tree;
1497 tree = (struct subtree *)malloc(sizeof(struct subtree));
1498 oid_copy (tree->name, name, namelen);
1499 tree->name_len = namelen;
1500 tree->variables = var;
1501 tree->variables_num = num;
1502 tree->variables_width = width;
1503 tree->registered = 0;
1504 listnode_add_sort(treelist, tree);
1507 /* Compare function to keep treelist sorted */
1508 static int
1509 smux_tree_cmp(struct subtree *tree1, struct subtree *tree2)
1511 return oid_compare(tree1->name, tree1->name_len,
1512 tree2->name, tree2->name_len);
1515 /* Initialize some values then schedule first SMUX connection. */
1516 void
1517 smux_init (struct thread_master *tm)
1519 /* copy callers thread master */
1520 master = tm;
1522 /* Make MIB tree. */
1523 treelist = list_new();
1524 treelist->cmp = (int (*)(void *, void *))smux_tree_cmp;
1526 /* Install commands. */
1527 install_node (&smux_node, config_write_smux);
1529 install_element (CONFIG_NODE, &smux_peer_cmd);
1530 install_element (CONFIG_NODE, &smux_peer_password_cmd);
1531 install_element (CONFIG_NODE, &no_smux_peer_cmd);
1532 install_element (CONFIG_NODE, &no_smux_peer_oid_cmd);
1533 install_element (CONFIG_NODE, &no_smux_peer_oid_password_cmd);
1536 void
1537 smux_start(void)
1539 /* Close any existing connections. */
1540 smux_stop();
1542 /* Schedule first connection. */
1543 smux_event (SMUX_SCHEDULE, 0);
1545 #endif /* HAVE_SNMP */