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