blob: 32f8c8ff31e3a90f26620b5a33a4db64bb7c07cb [file] [log] [blame]
paul718e3742002-12-13 20:15:29 +00001/* 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
26#include <asn1.h>
27#include <snmp.h>
28#include <snmp_impl.h>
29
30#include "smux.h"
31#include "log.h"
32#include "thread.h"
33#include "linklist.h"
34#include "command.h"
35#include "version.h"
36#include "memory.h"
37#include "sockunion.h"
38
39#define min(A,B) ((A) < (B) ? (A) : (B))
40
41enum smux_event {SMUX_SCHEDULE, SMUX_CONNECT, SMUX_READ};
42
43void smux_event (enum smux_event, int);
44
45
46/* SMUX socket. */
47int smux_sock = -1;
48
49/* SMUX subtree list. */
50struct list *treelist;
51
52/* SMUX oid. */
53oid *smux_oid;
54size_t smux_oid_len;
55
56/* SMUX default oid. */
57oid *smux_default_oid;
58size_t smux_default_oid_len;
59
60/* SMUX password. */
61char *smux_passwd;
62char *smux_default_passwd = "";
63
64/* SMUX read threads. */
65struct thread *smux_read_thread;
66
67/* SMUX connect thrads. */
68struct thread *smux_connect_thread;
69
70/* SMUX debug flag. */
71int debug_smux = 0;
72
73/* SMUX failure count. */
74int fail = 0;
75
76/* SMUX node. */
77struct cmd_node smux_node =
78{
79 SMUX_NODE,
80 "" /* SMUX has no interface. */
81};
82
83void *
84oid_copy (void *dest, void *src, size_t size)
85{
86 return memcpy (dest, src, size * sizeof (oid));
87}
88
89void
90oid2in_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
104void
105oid_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
119int
120oid_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
139int
140oid_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
157void
158smux_oid_dump (char *prefix, oid *oid, size_t oid_len)
159{
160 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_info ("%s: %s", prefix, buf);
172}
173
174int
175smux_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
267void
268smux_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 int len, length;
275
276 ptr = buf;
277 len = BUFSIZ;
278 length = len;
279
280 if (debug_smux)
281 {
282 zlog_info ("SMUX GETRSP send");
283 zlog_info ("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_info ("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_info ("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_info ("SMUX getresp send: %d", ptr - buf);
326
327 ret = send (smux_sock, buf, (ptr - buf), 0);
328}
329
330char *
331smux_var (char *ptr, int 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_info ("SMUX var parse: len %d", len);
343
344 /* Parse header. */
345 ptr = asn_parse_header (ptr, &len, &type);
346
347 if (debug_smux)
348 {
349 zlog_info ("SMUX var parse: type %d len %d", type, len);
350 zlog_info ("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_info ("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_info ("ASN_NULL");
383 break;
384
385 case ASN_INTEGER:
386 zlog_info ("ASN_INTEGER");
387 break;
388 case ASN_COUNTER:
389 case ASN_GAUGE:
390 case ASN_TIMETICKS:
391 case ASN_UINTEGER:
392 zlog_info ("ASN_COUNTER");
393 break;
394 case ASN_COUNTER64:
395 zlog_info ("ASN_COUNTER64");
396 break;
397 case ASN_IPADDRESS:
398 zlog_info ("ASN_IPADDRESS");
399 break;
400 case ASN_OCTET_STR:
401 zlog_info ("ASN_OCTET_STR");
402 break;
403 case ASN_OPAQUE:
404 case ASN_NSAP:
405 case ASN_OBJECT_ID:
406 zlog_info ("ASN_OPAQUE");
407 break;
408 case SNMP_NOSUCHOBJECT:
409 zlog_info ("SNMP_NOSUCHOBJECT");
410 break;
411 case SNMP_NOSUCHINSTANCE:
412 zlog_info ("SNMP_NOSUCHINSTANCE");
413 break;
414 case SNMP_ENDOFMIBVIEW:
415 zlog_info ("SNMP_ENDOFMIBVIEW");
416 break;
417 case ASN_BIT_STR:
418 zlog_info ("ASN_BIT_STR");
419 break;
420 default:
421 zlog_info ("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
431int
432smux_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 int 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_info ("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
500int
501smux_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 int 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_info ("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
567int
568smux_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 int 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_info ("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. */
654char *
655smux_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_info ("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_info ("SMUX GET errstat %ld len: %d", errstat, *len);
672
673 /* Error index. */
674 ptr = asn_parse_int (ptr, len, &type, &errindex, sizeof (errindex));
675
676 if (debug_smux)
677 zlog_info ("SMUX GET errindex %ld len: %d", errindex, *len);
678
679 return ptr;
680}
681
682void
683smux_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_info ("SMUX SET(%s) message parse: len %d",
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_info ("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
713void
714smux_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_info ("SMUX GET message parse: len %d", 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. */
747void
748smux_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. */
761void
762smux_parse_rrsp (char *ptr, int 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_info ("SMUX_RRSP value: %d errstat: %ld", val, errstat);
771}
772
773/* Parse SMUX message. */
774int
775smux_parse (char *ptr, int 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
789process_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_info ("SMUX message received type: %d rest len: %d", 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_info ("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_info ("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_info ("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_info ("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_info ("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_info ("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. */
887int
888smux_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_info ("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", 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_info ("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
943int
944smux_open (int sock)
945{
946 u_char buf[BUFSIZ];
947 u_char *ptr;
948 int len;
949 u_long version;
950 u_char progname[] = "zebra-" ZEBRA_VERSION;
951
952 if (debug_smux)
953 {
954 smux_oid_dump ("SMUX open oid", smux_oid, smux_oid_len);
955 zlog_info ("SMUX open progname: %s", progname);
956 zlog_info ("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
996int
997smux_trap (oid *name, size_t namelen,
998 oid *iname, size_t inamelen,
999 struct trap_object *trapobj, size_t trapobjlen,
1000 unsigned int tick)
1001{
1002 int i;
1003 u_char buf[BUFSIZ];
1004 u_char *ptr;
1005 int 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 = 2;
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_info ("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
1101int
1102smux_register (int sock)
1103{
1104 u_char buf[BUFSIZ];
1105 u_char *ptr;
1106 int len, ret;
1107 long priority;
1108 long operation;
1109 struct subtree *subtree;
1110 struct listnode *node;
1111
1112 ret = 0;
1113
1114 for (node = treelist->head; node; node = node->next)
1115 {
1116 ptr = buf;
1117 len = BUFSIZ;
1118
1119 subtree = node->data;
1120
1121 /* SMUX RReq Header. */
1122 ptr = asn_build_header (ptr, &len, (u_char) SMUX_RREQ, 0);
1123
1124 /* Register MIB tree. */
1125 ptr = asn_build_objid (ptr, &len,
1126 (u_char)
1127 (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID),
1128 subtree->name, subtree->name_len);
1129
1130 /* Priority. */
1131 priority = -1;
1132 ptr = asn_build_int (ptr, &len,
1133 (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
1134 &priority, sizeof (u_long));
1135
1136 /* Operation. */
1137 operation = 2; /* Register R/W */
1138 ptr = asn_build_int (ptr, &len,
1139 (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
1140 &operation, sizeof (u_long));
1141
1142 if (debug_smux)
1143 {
1144 smux_oid_dump ("SMUX register oid", subtree->name, subtree->name_len);
1145 zlog_info ("SMUX register priority: %ld", priority);
1146 zlog_info ("SMUX register operation: %ld", operation);
1147 }
1148
1149 len = BUFSIZ;
1150 asn_build_header (buf, &len, (u_char) SMUX_RREQ, (ptr - buf) - 2);
1151 ret = send (sock, buf, (ptr - buf), 0);
1152 if (ret < 0)
1153 return ret;
1154 }
1155 return ret;
1156}
1157
1158/* Try to connect to SNMP agent. */
1159int
1160smux_connect (struct thread *t)
1161{
1162 int ret;
1163
1164 if (debug_smux)
1165 zlog_info ("SMUX connect try %d", fail + 1);
1166
1167 /* Clear thread poner of myself. */
1168 smux_connect_thread = NULL;
1169
1170 /* Make socket. Try to connect. */
1171 smux_sock = smux_socket ();
1172 if (smux_sock < 0)
1173 {
1174 if (++fail < SMUX_MAX_FAILURE)
1175 smux_event (SMUX_CONNECT, 0);
1176 return 0;
1177 }
1178
1179 /* Send OPEN PDU. */
1180 ret = smux_open (smux_sock);
1181 if (ret < 0)
1182 {
1183 zlog_warn ("SMUX open message send failed: %s", strerror (errno));
1184 close (smux_sock);
1185 smux_sock = -1;
1186 if (++fail < SMUX_MAX_FAILURE)
1187 smux_event (SMUX_CONNECT, 0);
1188 return -1;
1189 }
1190
1191 /* Send any outstanding register PDUs. */
1192 ret = smux_register (smux_sock);
1193 if (ret < 0)
1194 {
1195 zlog_warn ("SMUX register message send failed: %s", strerror (errno));
1196 close (smux_sock);
1197 smux_sock = -1;
1198 if (++fail < SMUX_MAX_FAILURE)
1199 smux_event (SMUX_CONNECT, 0);
1200 return -1;
1201 }
1202
1203 /* Everything goes fine. */
1204 smux_event (SMUX_READ, smux_sock);
1205
1206 return 0;
1207}
1208
1209/* Clear all SMUX related resources. */
1210void
1211smux_stop ()
1212{
1213 if (smux_read_thread)
1214 thread_cancel (smux_read_thread);
1215 if (smux_connect_thread)
1216 thread_cancel (smux_connect_thread);
1217
1218 if (smux_sock >= 0)
1219 {
1220 close (smux_sock);
1221 smux_sock = -1;
1222 }
1223}
1224
1225extern struct thread_master *master;
1226
1227void
1228smux_event (enum smux_event event, int sock)
1229{
1230 switch (event)
1231 {
1232 case SMUX_SCHEDULE:
1233 smux_connect_thread = thread_add_event (master, smux_connect, NULL, 0);
1234 break;
1235 case SMUX_CONNECT:
1236 smux_connect_thread = thread_add_timer (master, smux_connect, NULL, 10);
1237 break;
1238 case SMUX_READ:
1239 smux_read_thread = thread_add_read (master, smux_read, NULL, sock);
1240 break;
1241 default:
1242 break;
1243 }
1244}
1245
1246int
1247smux_str2oid (char *str, oid *oid, size_t *oid_len)
1248{
1249 int len;
1250 int val;
1251
1252 len = 0;
1253 val = 0;
1254 *oid_len = 0;
1255
1256 if (*str == '.')
1257 str++;
1258 if (*str == '\0')
1259 return 0;
1260
1261 while (1)
1262 {
1263 if (! isdigit (*str))
1264 return -1;
1265
1266 while (isdigit (*str))
1267 {
1268 val *= 10;
1269 val += (*str - '0');
1270 str++;
1271 }
1272
1273 if (*str == '\0')
1274 break;
1275 if (*str != '.')
1276 return -1;
1277
1278 oid[len++] = val;
1279 val = 0;
1280 str++;
1281 }
1282
1283 oid[len++] = val;
1284 *oid_len = len;
1285
1286 return 0;
1287}
1288
1289oid *
1290smux_oid_dup (oid *objid, size_t objid_len)
1291{
1292 oid *new;
1293
1294 new = XMALLOC (MTYPE_TMP, sizeof (oid) * objid_len);
1295 oid_copy (new, objid, objid_len);
1296
1297 return new;
1298}
1299
1300int
1301smux_peer_oid (struct vty *vty, char *oid_str, char *passwd_str)
1302{
1303 int ret;
1304 oid oid[MAX_OID_LEN];
1305 size_t oid_len;
1306
1307 ret = smux_str2oid (oid_str, oid, &oid_len);
1308 if (ret != 0)
1309 {
1310 vty_out (vty, "object ID malformed%s", VTY_NEWLINE);
1311 return CMD_WARNING;
1312 }
1313
1314 if (smux_oid && smux_oid != smux_default_oid)
1315 free (smux_oid);
1316
1317 if (smux_passwd && smux_passwd != smux_default_passwd)
1318 {
1319 free (smux_passwd);
1320 smux_passwd = NULL;
1321 }
1322
1323 smux_oid = smux_oid_dup (oid, oid_len);
1324 smux_oid_len = oid_len;
1325
1326 if (passwd_str)
1327 smux_passwd = strdup (passwd_str);
1328
1329 return CMD_SUCCESS;
1330}
1331
1332int
1333smux_header_generic (struct variable *v, oid *name, size_t *length, int exact,
1334 size_t *var_len, WriteMethod **write_method)
1335{
1336 oid fulloid[MAX_OID_LEN];
1337 int ret;
1338
1339 oid_copy (fulloid, v->name, v->namelen);
1340 fulloid[v->namelen] = 0;
1341 /* Check against full instance. */
1342 ret = oid_compare (name, *length, fulloid, v->namelen + 1);
1343
1344 /* Check single instance. */
1345 if ((exact && (ret != 0)) || (!exact && (ret >= 0)))
1346 return MATCH_FAILED;
1347
1348 /* In case of getnext, fill in full instance. */
1349 memcpy (name, fulloid, (v->namelen + 1) * sizeof (oid));
1350 *length = v->namelen + 1;
1351
1352 *write_method = 0;
1353 *var_len = sizeof(long); /* default to 'long' results */
1354
1355 return MATCH_SUCCEEDED;
1356}
1357
1358int
1359smux_peer_default ()
1360{
1361 if (smux_oid != smux_default_oid)
1362 {
1363 free (smux_oid);
1364 smux_oid = smux_default_oid;
1365 smux_oid_len = smux_default_oid_len;
1366 }
1367 if (smux_passwd != smux_default_passwd)
1368 {
1369 free (smux_passwd);
1370 smux_passwd = smux_default_passwd;
1371 }
1372 return CMD_SUCCESS;
1373}
1374
1375DEFUN (smux_peer,
1376 smux_peer_cmd,
1377 "smux peer OID",
1378 "SNMP MUX protocol settings\n"
1379 "SNMP MUX peer settings\n"
1380 "Object ID used in SMUX peering\n")
1381{
1382 return smux_peer_oid (vty, argv[0], NULL);
1383}
1384
1385DEFUN (smux_peer_password,
1386 smux_peer_password_cmd,
1387 "smux peer OID PASSWORD",
1388 "SNMP MUX protocol settings\n"
1389 "SNMP MUX peer settings\n"
1390 "SMUX peering object ID\n"
1391 "SMUX peering password\n")
1392{
1393 return smux_peer_oid (vty, argv[0], argv[1]);
1394}
1395
1396DEFUN (no_smux_peer,
1397 no_smux_peer_cmd,
1398 "no smux peer OID",
1399 NO_STR
1400 "SNMP MUX protocol settings\n"
1401 "SNMP MUX peer settings\n"
1402 "Object ID used in SMUX peering\n")
1403{
1404 return smux_peer_default ();
1405}
1406
1407DEFUN (no_smux_peer_password,
1408 no_smux_peer_password_cmd,
1409 "no smux peer OID PASSWORD",
1410 NO_STR
1411 "SNMP MUX protocol settings\n"
1412 "SNMP MUX peer settings\n"
1413 "SMUX peering object ID\n"
1414 "SMUX peering password\n")
1415{
1416 return smux_peer_default ();
1417}
1418
1419int
1420config_write_smux (struct vty *vty)
1421{
1422 int first = 1;
1423 int i;
1424
1425 if (smux_oid != smux_default_oid || smux_passwd != smux_default_passwd)
1426 {
1427 vty_out (vty, "smux peer ");
1428 for (i = 0; i < smux_oid_len; i++)
1429 {
1430 vty_out (vty, "%s%d", first ? "" : ".", (int) smux_oid[i]);
1431 first = 0;
1432 }
1433 vty_out (vty, " %s%s", smux_passwd, VTY_NEWLINE);
1434 }
1435 return 0;
1436}
1437
1438/* Register subtree to smux master tree. */
1439void
1440smux_register_mib (char *descr, struct variable *var, size_t width, int num,
1441 oid name[], size_t namelen)
1442{
1443 struct subtree *tree;
1444
1445 tree = (struct subtree *)malloc(sizeof(struct subtree));
1446 oid_copy (tree->name, name, namelen);
1447 tree->name_len = namelen;
1448 tree->variables = var;
1449 tree->variables_num = num;
1450 tree->variables_width = width;
1451 tree->registered = 0;
1452 listnode_add_sort(treelist, tree);
1453}
1454
1455void
1456smux_reset ()
1457{
1458 /* Setting configuration to default. */
1459 smux_peer_default ();
1460}
1461
1462/* Compare function to keep treelist sorted */
1463static int
1464smux_tree_cmp(struct subtree *tree1, struct subtree *tree2)
1465{
1466 return oid_compare(tree1->name, tree1->name_len,
1467 tree2->name, tree2->name_len);
1468}
1469
1470/* Initialize some values then schedule first SMUX connection. */
1471void
1472smux_init (oid defoid[], size_t defoid_len)
1473{
1474 /* Set default SMUX oid. */
1475 smux_default_oid = defoid;
1476 smux_default_oid_len = defoid_len;
1477
1478 smux_oid = smux_default_oid;
1479 smux_oid_len = smux_default_oid_len;
1480 smux_passwd = smux_default_passwd;
1481
1482 /* Make MIB tree. */
1483 treelist = list_new();
1484 treelist->cmp = (int (*)(void *, void *))smux_tree_cmp;
1485
1486 /* Install commands. */
1487 install_node (&smux_node, config_write_smux);
1488
1489 install_element (CONFIG_NODE, &smux_peer_cmd);
1490 install_element (CONFIG_NODE, &smux_peer_password_cmd);
1491 install_element (CONFIG_NODE, &no_smux_peer_cmd);
1492 install_element (CONFIG_NODE, &no_smux_peer_password_cmd);
1493}
1494
1495void
1496smux_start(void)
1497{
1498 /* Schedule first connection. */
1499 smux_event (SMUX_SCHEDULE, 0);
1500}
1501#endif /* HAVE_SNMP */