/src/net-snmp/apps/snmptrapd_handlers.c
Line | Count | Source |
1 | | #include <net-snmp/net-snmp-config.h> |
2 | | #include <net-snmp/net-snmp-features.h> |
3 | | |
4 | | #ifdef HAVE_STDLIB_H |
5 | | #include <stdlib.h> |
6 | | #endif |
7 | | #ifdef HAVE_UNISTD_H |
8 | | #include <unistd.h> |
9 | | #endif |
10 | | #include <stdio.h> |
11 | | #ifdef HAVE_STRING_H |
12 | | #include <string.h> |
13 | | #else |
14 | | #include <strings.h> |
15 | | #endif |
16 | | #include <ctype.h> |
17 | | #include <sys/types.h> |
18 | | #ifdef HAVE_ARPA_INET_H |
19 | | #include <arpa/inet.h> |
20 | | #endif |
21 | | #ifdef HAVE_NETINET_IN_H |
22 | | #include <netinet/in.h> |
23 | | #endif |
24 | | #ifdef HAVE_NETDB_H |
25 | | #include <netdb.h> |
26 | | #endif |
27 | | #ifdef HAVE_SYS_WAIT_H |
28 | | #include <sys/wait.h> |
29 | | #endif |
30 | | #ifdef HAVE_LIMITS_H |
31 | | #include <limits.h> |
32 | | #endif |
33 | | |
34 | | #include <net-snmp/config_api.h> |
35 | | #include <net-snmp/output_api.h> |
36 | | #include <net-snmp/mib_api.h> |
37 | | #include <net-snmp/utilities.h> |
38 | | |
39 | | #include <net-snmp/net-snmp-includes.h> |
40 | | #include <net-snmp/agent/net-snmp-agent-includes.h> |
41 | | #include "utilities/execute.h" |
42 | | #include "snmptrapd_handlers.h" |
43 | | #include "snmptrapd_auth.h" |
44 | | #include "snmptrapd_log.h" |
45 | | #include "notification-log-mib/notification_log.h" |
46 | | |
47 | | netsnmp_feature_child_of(add_default_traphandler, snmptrapd); |
48 | | |
49 | | char *syslog_format1 = NULL; |
50 | | char *syslog_format2 = NULL; |
51 | | char *print_format1 = NULL; |
52 | | char *print_format2 = NULL; |
53 | | char *exec_format1 = NULL; |
54 | | char *exec_format2 = NULL; |
55 | | |
56 | | int SyslogTrap = 0; |
57 | | int dropauth = 0; |
58 | | |
59 | | const char *trap1_std_str = "%.4y-%.2m-%.2l %.2h:%.2j:%.2k %B [%b] (via %A [%a]): %N\n\t%W Trap (%q) Uptime: %#T\n%v\n"; |
60 | | const char *trap2_std_str = "%.4y-%.2m-%.2l %.2h:%.2j:%.2k %B [%b]:\n%v\n"; |
61 | | |
62 | | void snmptrapd_free_traphandle(void); |
63 | | |
64 | | const char * |
65 | | trap_description(int trap) |
66 | 0 | { |
67 | 0 | switch (trap) { |
68 | 0 | case SNMP_TRAP_COLDSTART: |
69 | 0 | return "Cold Start"; |
70 | 0 | case SNMP_TRAP_WARMSTART: |
71 | 0 | return "Warm Start"; |
72 | 0 | case SNMP_TRAP_LINKDOWN: |
73 | 0 | return "Link Down"; |
74 | 0 | case SNMP_TRAP_LINKUP: |
75 | 0 | return "Link Up"; |
76 | 0 | case SNMP_TRAP_AUTHFAIL: |
77 | 0 | return "Authentication Failure"; |
78 | 0 | case SNMP_TRAP_EGPNEIGHBORLOSS: |
79 | 0 | return "EGP Neighbor Loss"; |
80 | 0 | case SNMP_TRAP_ENTERPRISESPECIFIC: |
81 | 0 | return "Enterprise Specific"; |
82 | 0 | default: |
83 | 0 | return "Unknown Type"; |
84 | 0 | } |
85 | 0 | } |
86 | | |
87 | | |
88 | | |
89 | | void |
90 | | snmptrapd_parse_traphandle(const char *token, char *line) |
91 | 0 | { |
92 | 0 | char buf[STRINGMAX]; |
93 | 0 | oid obuf[MAX_OID_LEN]; |
94 | 0 | size_t olen = MAX_OID_LEN; |
95 | 0 | char *cptr, *cp; |
96 | 0 | netsnmp_trapd_handler *traph; |
97 | 0 | int flags = 0; |
98 | 0 | char *format = NULL; |
99 | |
|
100 | 0 | memset( buf, 0, sizeof(buf)); |
101 | 0 | memset(obuf, 0, sizeof(obuf)); |
102 | 0 | cptr = copy_nword(line, buf, sizeof(buf)); |
103 | |
|
104 | 0 | if ( buf[0] == '-' && buf[1] == 'F' ) { |
105 | 0 | cptr = copy_nword(cptr, buf, sizeof(buf)); |
106 | 0 | format = strdup( buf ); |
107 | 0 | cptr = copy_nword(cptr, buf, sizeof(buf)); |
108 | 0 | } |
109 | 0 | if ( !cptr ) { |
110 | 0 | netsnmp_config_error("Missing traphandle command (%s)", buf); |
111 | 0 | free(format); |
112 | 0 | return; |
113 | 0 | } |
114 | | |
115 | 0 | DEBUGMSGTL(("read_config:traphandle", "registering handler for: ")); |
116 | 0 | if (!strcmp(buf, "default")) { |
117 | 0 | DEBUGMSG(("read_config:traphandle", "default")); |
118 | 0 | traph = netsnmp_add_global_traphandler(NETSNMPTRAPD_DEFAULT_HANDLER, |
119 | 0 | command_handler ); |
120 | 0 | } else { |
121 | 0 | cp = buf+strlen(buf)-1; |
122 | 0 | if ( *cp == '*' ) { |
123 | 0 | flags |= NETSNMP_TRAPHANDLER_FLAG_MATCH_TREE; |
124 | 0 | *(cp--) = '\0'; |
125 | 0 | if ( *cp == '.' ) { |
126 | | /* |
127 | | * Distinguish between 'oid.*' & 'oid*' |
128 | | */ |
129 | 0 | flags |= NETSNMP_TRAPHANDLER_FLAG_STRICT_SUBTREE; |
130 | 0 | *(cp--) = '\0'; |
131 | 0 | } |
132 | 0 | } |
133 | 0 | if (!read_objid(buf, obuf, &olen)) { |
134 | 0 | netsnmp_config_error("Bad trap OID in traphandle directive: %s", |
135 | 0 | buf); |
136 | 0 | free(format); |
137 | 0 | return; |
138 | 0 | } |
139 | 0 | DEBUGMSGOID(("read_config:traphandle", obuf, olen)); |
140 | 0 | traph = netsnmp_add_traphandler( command_handler, obuf, olen ); |
141 | 0 | } |
142 | | |
143 | 0 | DEBUGMSG(("read_config:traphandle", "\n")); |
144 | |
|
145 | 0 | if (traph) { |
146 | 0 | traph->flags = flags; |
147 | 0 | traph->authtypes = TRAP_AUTH_EXE; |
148 | 0 | traph->token = strdup(cptr); |
149 | 0 | if (format) { |
150 | 0 | traph->format = format; |
151 | 0 | format = NULL; |
152 | 0 | } |
153 | 0 | } |
154 | 0 | free(format); |
155 | 0 | } |
156 | | |
157 | | |
158 | | static void |
159 | | parse_forward(const char *token, char *line) |
160 | 0 | { |
161 | 0 | char buf[STRINGMAX]; |
162 | 0 | oid obuf[MAX_OID_LEN]; |
163 | 0 | size_t olen = MAX_OID_LEN; |
164 | 0 | char *cptr, *cp; |
165 | 0 | netsnmp_trapd_handler *traph; |
166 | 0 | int flags = 0; |
167 | 0 | char *format = NULL; |
168 | |
|
169 | 0 | memset( buf, 0, sizeof(buf)); |
170 | 0 | memset(obuf, 0, sizeof(obuf)); |
171 | 0 | cptr = copy_nword(line, buf, sizeof(buf)); |
172 | |
|
173 | 0 | if ( buf[0] == '-' && buf[1] == 'F' ) { |
174 | 0 | cptr = copy_nword(cptr, buf, sizeof(buf)); |
175 | 0 | format = strdup( buf ); |
176 | 0 | cptr = copy_nword(cptr, buf, sizeof(buf)); |
177 | 0 | } |
178 | 0 | DEBUGMSGTL(("read_config:forward", "registering forward for: ")); |
179 | 0 | if (!strcmp(buf, "default")) { |
180 | 0 | DEBUGMSG(("read_config:forward", "default")); |
181 | 0 | if ( !strcmp( cptr, "agentx" )) |
182 | 0 | traph = netsnmp_add_global_traphandler(NETSNMPTRAPD_DEFAULT_HANDLER, |
183 | 0 | axforward_handler ); |
184 | 0 | else |
185 | 0 | traph = netsnmp_add_global_traphandler(NETSNMPTRAPD_DEFAULT_HANDLER, |
186 | 0 | forward_handler ); |
187 | 0 | } else { |
188 | 0 | cp = buf+strlen(buf)-1; |
189 | 0 | if ( *cp == '*' ) { |
190 | 0 | flags |= NETSNMP_TRAPHANDLER_FLAG_MATCH_TREE; |
191 | 0 | *(cp--) = '\0'; |
192 | 0 | if ( *cp == '.' ) { |
193 | | /* |
194 | | * Distinguish between 'oid.*' & 'oid*' |
195 | | */ |
196 | 0 | flags |= NETSNMP_TRAPHANDLER_FLAG_STRICT_SUBTREE; |
197 | 0 | *(cp--) = '\0'; |
198 | 0 | } |
199 | 0 | } |
200 | |
|
201 | 0 | if (!read_objid(buf, obuf, &olen)) { |
202 | 0 | netsnmp_config_error("Bad trap OID in forward directive: %s", buf); |
203 | 0 | free(format); |
204 | 0 | return; |
205 | 0 | } |
206 | 0 | DEBUGMSGOID(("read_config:forward", obuf, olen)); |
207 | 0 | if ( !strcmp( cptr, "agentx" )) |
208 | 0 | traph = netsnmp_add_traphandler( axforward_handler, obuf, olen ); |
209 | 0 | else |
210 | 0 | traph = netsnmp_add_traphandler( forward_handler, obuf, olen ); |
211 | 0 | } |
212 | | |
213 | 0 | DEBUGMSG(("read_config:forward", "\n")); |
214 | |
|
215 | 0 | if (traph) { |
216 | 0 | traph->flags = flags; |
217 | 0 | traph->authtypes = TRAP_AUTH_NET; |
218 | 0 | traph->token = strdup(cptr); |
219 | 0 | if (format) |
220 | 0 | traph->format = format; |
221 | 0 | } else { |
222 | 0 | free(format); |
223 | 0 | } |
224 | 0 | } |
225 | | |
226 | | |
227 | | void |
228 | | parse_format(const char *token, char *line) |
229 | 0 | { |
230 | 0 | char *cp, *sep; |
231 | | |
232 | | /* |
233 | | * Extract the first token from the value |
234 | | * which tells us which style of format this is |
235 | | */ |
236 | 0 | cp = line; |
237 | 0 | while (*cp && !isspace((unsigned char)(*cp))) |
238 | 0 | cp++; |
239 | 0 | if (!(*cp)) { |
240 | | /* |
241 | | * If we haven't got anything left, |
242 | | * then this entry is malformed. |
243 | | * So report this, and give up |
244 | | */ |
245 | 0 | return; |
246 | 0 | } |
247 | | |
248 | 0 | sep = cp; |
249 | 0 | *(cp++) = '\0'; |
250 | 0 | while (*cp && isspace((unsigned char)(*cp))) |
251 | 0 | cp++; |
252 | | |
253 | | /* |
254 | | * OK - now "line" contains the format type, |
255 | | * and cp points to the actual format string. |
256 | | * So update the appropriate pointer(s). |
257 | | */ |
258 | 0 | if (!strcmp( line, "print1")) { |
259 | 0 | SNMP_FREE( print_format1 ); |
260 | 0 | print_format1 = strdup(cp); |
261 | 0 | } else if (!strcmp( line, "print2")) { |
262 | 0 | SNMP_FREE( print_format2 ); |
263 | 0 | print_format2 = strdup(cp); |
264 | 0 | } else if (!strcmp( line, "print")) { |
265 | 0 | SNMP_FREE( print_format1 ); |
266 | 0 | SNMP_FREE( print_format2 ); |
267 | 0 | print_format1 = strdup(cp); |
268 | 0 | print_format2 = strdup(cp); |
269 | 0 | } else if (!strcmp( line, "syslog1")) { |
270 | 0 | SNMP_FREE( syslog_format1 ); |
271 | 0 | syslog_format1 = strdup(cp); |
272 | 0 | } else if (!strcmp( line, "syslog2")) { |
273 | 0 | SNMP_FREE( syslog_format2 ); |
274 | 0 | syslog_format2 = strdup(cp); |
275 | 0 | } else if (!strcmp( line, "syslog")) { |
276 | 0 | SNMP_FREE( syslog_format1 ); |
277 | 0 | SNMP_FREE( syslog_format2 ); |
278 | 0 | syslog_format1 = strdup(cp); |
279 | 0 | syslog_format2 = strdup(cp); |
280 | 0 | } else if (!strcmp( line, "execute1")) { |
281 | 0 | SNMP_FREE( exec_format1 ); |
282 | 0 | exec_format1 = strdup(cp); |
283 | 0 | } else if (!strcmp( line, "execute2")) { |
284 | 0 | SNMP_FREE( exec_format2 ); |
285 | 0 | exec_format2 = strdup(cp); |
286 | 0 | } else if (!strcmp( line, "execute")) { |
287 | 0 | SNMP_FREE( exec_format1 ); |
288 | 0 | SNMP_FREE( exec_format2 ); |
289 | 0 | exec_format1 = strdup(cp); |
290 | 0 | exec_format2 = strdup(cp); |
291 | 0 | } |
292 | |
|
293 | 0 | *sep = ' '; |
294 | 0 | } |
295 | | |
296 | | |
297 | | static void |
298 | | parse_trap1_fmt(const char *token, char *line) |
299 | 0 | { |
300 | 0 | print_format1 = strdup(line); |
301 | 0 | } |
302 | | |
303 | | |
304 | | void |
305 | | free_trap1_fmt(void) |
306 | 0 | { |
307 | 0 | if (print_format1 && print_format1 != trap1_std_str) |
308 | 0 | free(print_format1); |
309 | 0 | print_format1 = NULL; |
310 | 0 | } |
311 | | |
312 | | |
313 | | static void |
314 | | parse_trap2_fmt(const char *token, char *line) |
315 | 0 | { |
316 | 0 | print_format2 = strdup(line); |
317 | 0 | } |
318 | | |
319 | | |
320 | | void |
321 | | free_trap2_fmt(void) |
322 | 0 | { |
323 | 0 | if (print_format2 && print_format2 != trap2_std_str) |
324 | 0 | free(print_format2); |
325 | 0 | print_format2 = NULL; |
326 | 0 | } |
327 | | |
328 | | |
329 | | void |
330 | | snmptrapd_register_configs( void ) |
331 | 0 | { |
332 | 0 | register_config_handler("snmptrapd", "traphandle", |
333 | 0 | snmptrapd_parse_traphandle, |
334 | 0 | snmptrapd_free_traphandle, |
335 | 0 | "oid|\"default\" program [args ...] "); |
336 | 0 | register_config_handler("snmptrapd", "format1", |
337 | 0 | parse_trap1_fmt, free_trap1_fmt, "format"); |
338 | 0 | register_config_handler("snmptrapd", "format2", |
339 | 0 | parse_trap2_fmt, free_trap2_fmt, "format"); |
340 | 0 | register_config_handler("snmptrapd", "format", |
341 | 0 | parse_format, NULL, |
342 | 0 | "[print{,1,2}|syslog{,1,2}|execute{,1,2}] format"); |
343 | 0 | register_config_handler("snmptrapd", "forward", |
344 | 0 | parse_forward, NULL, "OID|\"default\" destination"); |
345 | 0 | } |
346 | | |
347 | | |
348 | | |
349 | | /*----------------------------- |
350 | | * |
351 | | * Routines to implement a "registry" of trap handlers |
352 | | * |
353 | | *-----------------------------*/ |
354 | | |
355 | | netsnmp_trapd_handler *netsnmp_auth_global_traphandlers = NULL; |
356 | | netsnmp_trapd_handler *netsnmp_pre_global_traphandlers = NULL; |
357 | | netsnmp_trapd_handler *netsnmp_post_global_traphandlers = NULL; |
358 | | netsnmp_trapd_handler *netsnmp_default_traphandlers = NULL; |
359 | | netsnmp_trapd_handler *netsnmp_specific_traphandlers = NULL; |
360 | | |
361 | | typedef struct netsnmp_handler_map_t { |
362 | | netsnmp_trapd_handler **handler; |
363 | | const char *descr; |
364 | | } netsnmp_handler_map; |
365 | | |
366 | | static netsnmp_handler_map handlers[] = { |
367 | | { &netsnmp_auth_global_traphandlers, "auth trap" }, |
368 | | { &netsnmp_pre_global_traphandlers, "pre-global trap" }, |
369 | | { NULL, "trap specific" }, |
370 | | { &netsnmp_post_global_traphandlers, "global" }, |
371 | | { NULL, NULL } |
372 | | }; |
373 | | |
374 | | /* |
375 | | * Register a new "global" traphandler, |
376 | | * to be applied to *all* incoming traps |
377 | | */ |
378 | | netsnmp_trapd_handler * |
379 | | netsnmp_add_global_traphandler(int list, Netsnmp_Trap_Handler *handler) |
380 | 0 | { |
381 | 0 | netsnmp_trapd_handler *traph; |
382 | |
|
383 | 0 | if ( !handler ) |
384 | 0 | return NULL; |
385 | | |
386 | 0 | traph = SNMP_MALLOC_TYPEDEF(netsnmp_trapd_handler); |
387 | 0 | if ( !traph ) |
388 | 0 | return NULL; |
389 | | |
390 | | /* |
391 | | * Add this new handler to the front of the appropriate list |
392 | | * (or should it go on the end?) |
393 | | */ |
394 | 0 | traph->handler = handler; |
395 | 0 | traph->authtypes = TRAP_AUTH_ALL; /* callers will likely change this */ |
396 | 0 | switch (list) { |
397 | 0 | case NETSNMPTRAPD_AUTH_HANDLER: |
398 | 0 | traph->nexth = netsnmp_auth_global_traphandlers; |
399 | 0 | netsnmp_auth_global_traphandlers = traph; |
400 | 0 | break; |
401 | 0 | case NETSNMPTRAPD_PRE_HANDLER: |
402 | 0 | traph->nexth = netsnmp_pre_global_traphandlers; |
403 | 0 | netsnmp_pre_global_traphandlers = traph; |
404 | 0 | break; |
405 | 0 | case NETSNMPTRAPD_POST_HANDLER: |
406 | 0 | traph->nexth = netsnmp_post_global_traphandlers; |
407 | 0 | netsnmp_post_global_traphandlers = traph; |
408 | 0 | break; |
409 | 0 | case NETSNMPTRAPD_DEFAULT_HANDLER: |
410 | 0 | traph->nexth = netsnmp_default_traphandlers; |
411 | 0 | netsnmp_default_traphandlers = traph; |
412 | 0 | break; |
413 | 0 | default: |
414 | 0 | free( traph ); |
415 | 0 | return NULL; |
416 | 0 | } |
417 | 0 | return traph; |
418 | 0 | } |
419 | | |
420 | | #ifndef NETSNMP_FEATURE_REMOVE_ADD_DEFAULT_TRAPHANDLER |
421 | | /* |
422 | | * Register a new "default" traphandler, to be applied to all |
423 | | * traps with no specific trap handlers of their own. |
424 | | */ |
425 | | netsnmp_trapd_handler * |
426 | 0 | netsnmp_add_default_traphandler(Netsnmp_Trap_Handler *handler) { |
427 | 0 | return netsnmp_add_global_traphandler(NETSNMPTRAPD_DEFAULT_HANDLER, |
428 | 0 | handler); |
429 | 0 | } |
430 | | #endif /* NETSNMP_FEATURE_REMOVE_ADD_DEFAULT_TRAPHANDLER */ |
431 | | |
432 | | |
433 | | /* |
434 | | * Register a new trap-specific traphandler |
435 | | */ |
436 | | netsnmp_trapd_handler * |
437 | | netsnmp_add_traphandler(Netsnmp_Trap_Handler* handler, |
438 | 0 | oid *trapOid, int trapOidLen ) { |
439 | 0 | netsnmp_trapd_handler *traph, *traph2; |
440 | |
|
441 | 0 | if ( !handler ) |
442 | 0 | return NULL; |
443 | | |
444 | 0 | traph = SNMP_MALLOC_TYPEDEF(netsnmp_trapd_handler); |
445 | 0 | if ( !traph ) |
446 | 0 | return NULL; |
447 | | |
448 | | /* |
449 | | * Populate this new handler with the trap information |
450 | | * (NB: the OID fields were not used in the default/global lists) |
451 | | */ |
452 | 0 | traph->authtypes = TRAP_AUTH_ALL; /* callers will likely change this */ |
453 | 0 | traph->handler = handler; |
454 | 0 | traph->trapoid_len = trapOidLen; |
455 | 0 | traph->trapoid = snmp_duplicate_objid(trapOid, trapOidLen); |
456 | | |
457 | | /* |
458 | | * Now try to find the appropriate place in the trap-specific |
459 | | * list for this particular trap OID. If there's a matching OID |
460 | | * already, then find it. Otherwise find the one that follows. |
461 | | * If we run out of entries, the new one should be tacked onto the end. |
462 | | */ |
463 | 0 | for (traph2 = netsnmp_specific_traphandlers; |
464 | 0 | traph2; traph2 = traph2->nextt) { |
465 | | /* XXX - check this! */ |
466 | 0 | if (snmp_oid_compare(traph2->trapoid, traph2->trapoid_len, |
467 | 0 | trapOid, trapOidLen) <= 0) |
468 | 0 | break; |
469 | 0 | } |
470 | 0 | if (traph2) { |
471 | | /* |
472 | | * OK - We've either got an exact match, or we've found the |
473 | | * entry *after* where the new one should go. |
474 | | */ |
475 | 0 | if (!snmp_oid_compare(traph->trapoid, traph->trapoid_len, |
476 | 0 | traph2->trapoid, traph2->trapoid_len)) { |
477 | | /* |
478 | | * Exact match, so find the end of the *handler* list |
479 | | * and tack on this new entry... |
480 | | */ |
481 | 0 | while (traph2->nexth) |
482 | 0 | traph2 = traph2->nexth; |
483 | 0 | traph2->nexth = traph; |
484 | 0 | traph->nextt = traph2->nextt; /* Might as well... */ |
485 | 0 | traph->prevt = traph2->prevt; |
486 | 0 | } else { |
487 | | /* |
488 | | * .. or the following entry, so insert the new one before it. |
489 | | */ |
490 | 0 | traph->prevt = traph2->prevt; |
491 | 0 | if (traph2->prevt) |
492 | 0 | traph2->prevt->nextt = traph; |
493 | 0 | else |
494 | 0 | netsnmp_specific_traphandlers = traph; |
495 | 0 | traph2->prevt = traph; |
496 | 0 | traph->nextt = traph2; |
497 | 0 | } |
498 | 0 | } else { |
499 | | /* |
500 | | * If we've run out of entries without finding a suitable spot, |
501 | | * the new one should be tacked onto the end..... |
502 | | */ |
503 | 0 | if (netsnmp_specific_traphandlers) { |
504 | 0 | traph2 = netsnmp_specific_traphandlers; |
505 | 0 | while (traph2->nextt) |
506 | 0 | traph2 = traph2->nextt; |
507 | 0 | traph2->nextt = traph; |
508 | 0 | traph->prevt = traph2; |
509 | 0 | } else { |
510 | | /* |
511 | | * .... unless this is the very first entry, of course! |
512 | | */ |
513 | 0 | netsnmp_specific_traphandlers = traph; |
514 | 0 | } |
515 | 0 | } |
516 | |
|
517 | 0 | return traph; |
518 | 0 | } |
519 | | |
520 | | void |
521 | | snmptrapd_free_traphandle(void) |
522 | 0 | { |
523 | 0 | netsnmp_trapd_handler *traph = NULL, *nextt = NULL, *nexth = NULL; |
524 | |
|
525 | 0 | DEBUGMSGTL(("snmptrapd", "Freeing trap handler lists\n")); |
526 | | |
527 | | /* |
528 | | * Free default trap handlers |
529 | | */ |
530 | 0 | traph = netsnmp_default_traphandlers; |
531 | | /* loop over handlers */ |
532 | 0 | while (traph) { |
533 | 0 | DEBUGMSG(("snmptrapd", "Freeing default trap handler\n")); |
534 | 0 | nexth = traph->nexth; |
535 | 0 | SNMP_FREE(traph->token); |
536 | 0 | SNMP_FREE(traph); |
537 | 0 | traph = nexth; |
538 | 0 | } |
539 | 0 | netsnmp_default_traphandlers = NULL; |
540 | | |
541 | | /* |
542 | | * Free specific trap handlers |
543 | | */ |
544 | 0 | traph = netsnmp_specific_traphandlers; |
545 | | /* loop over traps */ |
546 | 0 | while (traph) { |
547 | 0 | nextt = traph->nextt; |
548 | | /* loop over handlers for this trap */ |
549 | 0 | while (traph) { |
550 | 0 | DEBUGMSG(("snmptrapd", "Freeing specific trap handler\n")); |
551 | 0 | nexth = traph->nexth; |
552 | 0 | SNMP_FREE(traph->token); |
553 | 0 | SNMP_FREE(traph->trapoid); |
554 | 0 | SNMP_FREE(traph); |
555 | 0 | traph = nexth; |
556 | 0 | } |
557 | 0 | traph = nextt; |
558 | 0 | } |
559 | 0 | netsnmp_specific_traphandlers = NULL; |
560 | 0 | } |
561 | | |
562 | | /* |
563 | | * Locate the list of handlers for this particular Trap OID |
564 | | * Returns NULL if there are no relevant traps |
565 | | */ |
566 | | netsnmp_trapd_handler * |
567 | 334 | netsnmp_get_traphandler( oid *trapOid, int trapOidLen ) { |
568 | 334 | netsnmp_trapd_handler *traph; |
569 | | |
570 | 334 | if (!trapOid || !trapOidLen) { |
571 | 10 | DEBUGMSGTL(( "snmptrapd:lookup", "get_traphandler no OID!\n")); |
572 | 10 | return NULL; |
573 | 10 | } |
574 | 324 | DEBUGMSGTL(( "snmptrapd:lookup", "Looking up Trap OID: ")); |
575 | 324 | DEBUGMSGOID(("snmptrapd:lookup", trapOid, trapOidLen)); |
576 | 324 | DEBUGMSG(( "snmptrapd:lookup", "\n")); |
577 | | |
578 | | /* |
579 | | * Look for a matching OID, and return that list... |
580 | | */ |
581 | 324 | for (traph = netsnmp_specific_traphandlers; |
582 | 324 | traph; traph=traph->nextt ) { |
583 | | |
584 | | /* |
585 | | * If the trap handler wasn't wildcarded, then the trapOID |
586 | | * should match the registered OID exactly. |
587 | | */ |
588 | 0 | if (!(traph->flags & NETSNMP_TRAPHANDLER_FLAG_MATCH_TREE)) { |
589 | 0 | if (snmp_oid_compare(traph->trapoid, traph->trapoid_len, |
590 | 0 | trapOid, trapOidLen) == 0) { |
591 | 0 | DEBUGMSGTL(( "snmptrapd:lookup", |
592 | 0 | "get_traphandler exact match (%p)\n", traph)); |
593 | 0 | return traph; |
594 | 0 | } |
595 | 0 | } else { |
596 | | /* |
597 | | * If the trap handler *was* wildcarded, then the trapOID |
598 | | * should have the registered OID as a prefix... |
599 | | */ |
600 | 0 | if (snmp_oidsubtree_compare(traph->trapoid, |
601 | 0 | traph->trapoid_len, |
602 | 0 | trapOid, trapOidLen) == 0) { |
603 | 0 | if (traph->flags & NETSNMP_TRAPHANDLER_FLAG_STRICT_SUBTREE) { |
604 | | /* |
605 | | * ... and (optionally) *strictly* as a prefix |
606 | | * i.e. not including an exact match. |
607 | | */ |
608 | 0 | if (snmp_oid_compare(traph->trapoid, traph->trapoid_len, |
609 | 0 | trapOid, trapOidLen) != 0) { |
610 | 0 | DEBUGMSGTL(( "snmptrapd:lookup", "get_traphandler strict subtree match (%p)\n", traph)); |
611 | 0 | return traph; |
612 | 0 | } |
613 | 0 | } else { |
614 | 0 | DEBUGMSGTL(( "snmptrapd:lookup", "get_traphandler subtree match (%p)\n", traph)); |
615 | 0 | return traph; |
616 | 0 | } |
617 | 0 | } |
618 | 0 | } |
619 | 0 | } |
620 | | |
621 | | /* |
622 | | * .... or failing that, return the "default" list (which may be NULL) |
623 | | */ |
624 | 324 | DEBUGMSGTL(( "snmptrapd:lookup", "get_traphandler default (%p)\n", |
625 | 324 | netsnmp_default_traphandlers)); |
626 | 324 | return netsnmp_default_traphandlers; |
627 | 324 | } |
628 | | |
629 | | /*----------------------------- |
630 | | * |
631 | | * Standard traphandlers for the common requirements |
632 | | * |
633 | | *-----------------------------*/ |
634 | | |
635 | 0 | #define SYSLOG_V1_STANDARD_FORMAT "%a: %W Trap (%q) Uptime: %#T%#v\n" |
636 | 0 | #define SYSLOG_V1_ENTERPRISE_FORMAT "%a: %W Trap (%q) Uptime: %#T%#v\n" /* XXX - (%q) become (.N) ??? */ |
637 | 0 | #define SYSLOG_V23_NOTIFICATION_FORMAT "%B [%b]: Trap %#v\n" /* XXX - introduces a leading " ," */ |
638 | | |
639 | | /* |
640 | | * Trap handler for logging via syslog |
641 | | */ |
642 | | int syslog_handler( netsnmp_pdu *pdu, |
643 | | netsnmp_transport *transport, |
644 | | netsnmp_trapd_handler *handler) |
645 | 0 | { |
646 | 0 | u_char *rbuf = NULL; |
647 | 0 | size_t r_len = 64, o_len = 0; |
648 | 0 | int trunc = 0; |
649 | |
|
650 | 0 | DEBUGMSGTL(( "snmptrapd", "syslog_handler\n")); |
651 | |
|
652 | 0 | if (SyslogTrap) |
653 | 0 | return NETSNMPTRAPD_HANDLER_OK; |
654 | | |
655 | 0 | if ((rbuf = calloc(r_len, 1)) == NULL) { |
656 | 0 | snmp_log(LOG_ERR, "couldn't display trap -- malloc failed\n"); |
657 | 0 | return NETSNMPTRAPD_HANDLER_FAIL; /* Failed but keep going */ |
658 | 0 | } |
659 | | |
660 | | /* |
661 | | * If there's a format string registered for this trap, then use it. |
662 | | */ |
663 | 0 | if (handler && handler->format) { |
664 | 0 | DEBUGMSGTL(( "snmptrapd", "format = '%s'\n", handler->format)); |
665 | 0 | if (*handler->format) { |
666 | 0 | trunc = !realloc_format_trap(&rbuf, &r_len, &o_len, 1, |
667 | 0 | handler->format, pdu, transport); |
668 | 0 | } else { |
669 | 0 | free(rbuf); |
670 | 0 | return NETSNMPTRAPD_HANDLER_OK; /* A 0-length format string means don't log */ |
671 | 0 | } |
672 | | |
673 | | /* |
674 | | * Otherwise (i.e. a NULL handler format string), |
675 | | * use a standard output format setting |
676 | | * either configurable, or hardwired |
677 | | * |
678 | | * XXX - v1 traps use a different hardwired formats for |
679 | | * standard and enterprise specific traps |
680 | | * Do we actually need this? |
681 | | */ |
682 | 0 | } else { |
683 | 0 | if ( pdu->command == SNMP_MSG_TRAP ) { |
684 | 0 | if (syslog_format1) { |
685 | 0 | DEBUGMSGTL(( "snmptrapd", "syslog_format v1 = '%s'\n", syslog_format1)); |
686 | 0 | trunc = !realloc_format_trap(&rbuf, &r_len, &o_len, 1, |
687 | 0 | syslog_format1, pdu, transport); |
688 | |
|
689 | 0 | } else if (pdu->trap_type == SNMP_TRAP_ENTERPRISESPECIFIC) { |
690 | 0 | DEBUGMSGTL(( "snmptrapd", "v1 enterprise format\n")); |
691 | 0 | trunc = !realloc_format_trap(&rbuf, &r_len, &o_len, 1, |
692 | 0 | SYSLOG_V1_ENTERPRISE_FORMAT, |
693 | 0 | pdu, transport); |
694 | 0 | } else { |
695 | 0 | DEBUGMSGTL(( "snmptrapd", "v1 standard trap format\n")); |
696 | 0 | trunc = !realloc_format_trap(&rbuf, &r_len, &o_len, 1, |
697 | 0 | SYSLOG_V1_STANDARD_FORMAT, |
698 | 0 | pdu, transport); |
699 | 0 | } |
700 | 0 | } else { /* SNMPv2/3 notifications */ |
701 | 0 | if (syslog_format2) { |
702 | 0 | DEBUGMSGTL(( "snmptrapd", "syslog_format v1 = '%s'\n", syslog_format2)); |
703 | 0 | trunc = !realloc_format_trap(&rbuf, &r_len, &o_len, 1, |
704 | 0 | syslog_format2, pdu, transport); |
705 | 0 | } else { |
706 | 0 | DEBUGMSGTL(( "snmptrapd", "v2/3 format\n")); |
707 | 0 | trunc = !realloc_format_trap(&rbuf, &r_len, &o_len, 1, |
708 | 0 | SYSLOG_V23_NOTIFICATION_FORMAT, |
709 | 0 | pdu, transport); |
710 | 0 | } |
711 | 0 | } |
712 | 0 | } |
713 | 0 | snmp_log(LOG_WARNING, "%s%s", rbuf, (trunc?" [TRUNCATED]\n":"")); |
714 | 0 | free(rbuf); |
715 | 0 | return NETSNMPTRAPD_HANDLER_OK; |
716 | 0 | } |
717 | | |
718 | | |
719 | 0 | #define PRINT_V23_NOTIFICATION_FORMAT "%.4y-%.2m-%.2l %.2h:%.2j:%.2k %B [%b]:\n%v\n" |
720 | | |
721 | | /* |
722 | | * Trap handler for logging to a file |
723 | | */ |
724 | | int print_handler( netsnmp_pdu *pdu, |
725 | | netsnmp_transport *transport, |
726 | | netsnmp_trapd_handler *handler) |
727 | 0 | { |
728 | 0 | u_char *rbuf = NULL; |
729 | 0 | size_t r_len = 64, o_len = 0; |
730 | 0 | int trunc = 0; |
731 | |
|
732 | 0 | DEBUGMSGTL(( "snmptrapd", "print_handler\n")); |
733 | | |
734 | | /* |
735 | | * Don't bother logging authentication failures |
736 | | * XXX - can we handle this via suitable handler entries instead? |
737 | | */ |
738 | 0 | if (pdu->trap_type == SNMP_TRAP_AUTHFAIL && dropauth) |
739 | 0 | return NETSNMPTRAPD_HANDLER_OK; |
740 | | |
741 | 0 | if ((rbuf = calloc(r_len, 1)) == NULL) { |
742 | 0 | snmp_log(LOG_ERR, "couldn't display trap -- malloc failed\n"); |
743 | 0 | return NETSNMPTRAPD_HANDLER_FAIL; /* Failed but keep going */ |
744 | 0 | } |
745 | | |
746 | | /* |
747 | | * If there's a format string registered for this trap, then use it. |
748 | | */ |
749 | 0 | if (handler && handler->format) { |
750 | 0 | DEBUGMSGTL(( "snmptrapd", "format = '%s'\n", handler->format)); |
751 | 0 | if (*handler->format) { |
752 | 0 | trunc = !realloc_format_trap(&rbuf, &r_len, &o_len, 1, |
753 | 0 | handler->format, pdu, transport); |
754 | 0 | } else { |
755 | 0 | free(rbuf); |
756 | 0 | return NETSNMPTRAPD_HANDLER_OK; /* A 0-length format string means don't log */ |
757 | 0 | } |
758 | | |
759 | | /* |
760 | | * Otherwise (i.e. a NULL handler format string), |
761 | | * use a standard output format setting |
762 | | * either configurable, or hardwired |
763 | | * |
764 | | * XXX - v1 traps use a different routine for hardwired output |
765 | | * Do we actually need this separate v1 routine? |
766 | | * Or would a suitable format string be sufficient? |
767 | | */ |
768 | 0 | } else { |
769 | 0 | if ( pdu->command == SNMP_MSG_TRAP ) { |
770 | 0 | if (print_format1) { |
771 | 0 | DEBUGMSGTL(( "snmptrapd", "print_format v1 = '%s'\n", print_format1)); |
772 | 0 | trunc = !realloc_format_trap(&rbuf, &r_len, &o_len, 1, |
773 | 0 | print_format1, pdu, transport); |
774 | 0 | } else { |
775 | 0 | DEBUGMSGTL(( "snmptrapd", "v1 format\n")); |
776 | 0 | trunc = !realloc_format_plain_trap(&rbuf, &r_len, &o_len, 1, |
777 | 0 | pdu, transport); |
778 | 0 | } |
779 | 0 | } else { |
780 | 0 | if (print_format2) { |
781 | 0 | DEBUGMSGTL(( "snmptrapd", "print_format v2 = '%s'\n", print_format2)); |
782 | 0 | trunc = !realloc_format_trap(&rbuf, &r_len, &o_len, 1, |
783 | 0 | print_format2, pdu, transport); |
784 | 0 | } else { |
785 | 0 | DEBUGMSGTL(( "snmptrapd", "v2/3 format\n")); |
786 | 0 | trunc = !realloc_format_trap(&rbuf, &r_len, &o_len, 1, |
787 | 0 | PRINT_V23_NOTIFICATION_FORMAT, |
788 | 0 | pdu, transport); |
789 | 0 | } |
790 | 0 | } |
791 | 0 | } |
792 | 0 | snmp_log(LOG_INFO, "%s%s", rbuf, (trunc?" [TRUNCATED]\n":"")); |
793 | 0 | free(rbuf); |
794 | 0 | return NETSNMPTRAPD_HANDLER_OK; |
795 | 0 | } |
796 | | |
797 | | |
798 | 0 | #define EXECUTE_FORMAT "%B\n%b\n%V\n%v\n" |
799 | | |
800 | | /* |
801 | | * Trap handler for invoking a suitable script |
802 | | */ |
803 | | int command_handler( netsnmp_pdu *pdu, |
804 | | netsnmp_transport *transport, |
805 | | netsnmp_trapd_handler *handler) |
806 | 0 | { |
807 | | #ifndef USING_UTILITIES_EXECUTE_MODULE |
808 | | NETSNMP_LOGONCE((LOG_WARNING, |
809 | | "support for run_shell_command not available\n")); |
810 | | return NETSNMPTRAPD_HANDLER_FAIL; |
811 | | #else |
812 | 0 | u_char *rbuf = NULL; |
813 | 0 | size_t r_len = 64, o_len = 0; |
814 | 0 | int oldquick; |
815 | |
|
816 | 0 | netsnmp_assert(handler); |
817 | |
|
818 | 0 | DEBUGMSGTL(( "snmptrapd", "command_handler\n")); |
819 | 0 | DEBUGMSGTL(( "snmptrapd", "token = '%s'\n", handler->token)); |
820 | 0 | if (handler->token && *handler->token) { |
821 | 0 | netsnmp_pdu *v2_pdu = NULL; |
822 | 0 | if (pdu->command == SNMP_MSG_TRAP) |
823 | 0 | v2_pdu = convert_v1pdu_to_v2(pdu); |
824 | 0 | else |
825 | 0 | v2_pdu = pdu; |
826 | 0 | oldquick = netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, |
827 | 0 | NETSNMP_DS_LIB_QUICK_PRINT); |
828 | 0 | netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, |
829 | 0 | NETSNMP_DS_LIB_QUICK_PRINT, 1); |
830 | | |
831 | | /* |
832 | | * Format the trap and pass this string to the external command |
833 | | */ |
834 | 0 | if ((rbuf = calloc(r_len, 1)) == NULL) { |
835 | 0 | snmp_log(LOG_ERR, "couldn't display trap -- malloc failed\n"); |
836 | 0 | return NETSNMPTRAPD_HANDLER_FAIL; /* Failed but keep going */ |
837 | 0 | } |
838 | | |
839 | | /* |
840 | | * If there's a format string registered for this trap, then use it. |
841 | | * Otherwise use the standard execution format setting. |
842 | | */ |
843 | 0 | if (handler->format && *handler->format) { |
844 | 0 | DEBUGMSGTL(( "snmptrapd", "format = '%s'\n", handler->format)); |
845 | 0 | realloc_format_trap(&rbuf, &r_len, &o_len, 1, |
846 | 0 | handler->format, |
847 | 0 | v2_pdu, transport); |
848 | 0 | } else { |
849 | 0 | if ( pdu->command == SNMP_MSG_TRAP && exec_format1 ) { |
850 | 0 | DEBUGMSGTL(( "snmptrapd", "exec v1 = '%s'\n", exec_format1)); |
851 | 0 | realloc_format_trap(&rbuf, &r_len, &o_len, 1, |
852 | 0 | exec_format1, pdu, transport); |
853 | 0 | } else if ( pdu->command != SNMP_MSG_TRAP && exec_format2 ) { |
854 | 0 | DEBUGMSGTL(( "snmptrapd", "exec v2/3 = '%s'\n", exec_format2)); |
855 | 0 | realloc_format_trap(&rbuf, &r_len, &o_len, 1, |
856 | 0 | exec_format2, pdu, transport); |
857 | 0 | } else { |
858 | 0 | DEBUGMSGTL(( "snmptrapd", "execute format\n")); |
859 | 0 | realloc_format_trap(&rbuf, &r_len, &o_len, 1, EXECUTE_FORMAT, |
860 | 0 | v2_pdu, transport); |
861 | 0 | } |
862 | 0 | } |
863 | | |
864 | | /* |
865 | | * and pass this formatted string to the command specified |
866 | | */ |
867 | 0 | run_shell_command(handler->token, (char*)rbuf, NULL, NULL); /* Not interested in output */ |
868 | 0 | netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, |
869 | 0 | NETSNMP_DS_LIB_QUICK_PRINT, oldquick); |
870 | 0 | if (pdu->command == SNMP_MSG_TRAP) |
871 | 0 | snmp_free_pdu(v2_pdu); |
872 | 0 | free(rbuf); |
873 | 0 | } |
874 | 0 | return NETSNMPTRAPD_HANDLER_OK; |
875 | 0 | #endif /* !def USING_UTILITIES_EXECUTE_MODULE */ |
876 | 0 | } |
877 | | |
878 | | |
879 | | |
880 | | |
881 | | /* |
882 | | * Trap handler for forwarding to the AgentX master agent |
883 | | */ |
884 | | int axforward_handler( netsnmp_pdu *pdu, |
885 | | netsnmp_transport *transport, |
886 | | netsnmp_trapd_handler *handler) |
887 | 0 | { |
888 | 0 | send_v2trap( pdu->variables ); |
889 | 0 | return NETSNMPTRAPD_HANDLER_OK; |
890 | 0 | } |
891 | | |
892 | | static int add_forwarder_info(netsnmp_pdu *pdu, netsnmp_pdu *pdu2) |
893 | 0 | { |
894 | 0 | netsnmp_indexed_addr_pair *addr_pair1; |
895 | 0 | struct sockaddr_in *to1 = NULL; |
896 | 0 | struct sockaddr_in *to2 = NULL; |
897 | 0 | int last_snmpTrapAddress_index = -1; |
898 | | /* snmpTrapAddress_oid.0 is also defined as agentaddr_oid */ |
899 | 0 | const oid snmpTrapAddress_oid[] = { 1,3,6,1,6,3,18,1,3}; |
900 | | /* each forwarder will add this OID with changed last index */ |
901 | 0 | oid forwarder_oid[] = { 1,3,6,1,6,3,18,1,3,0}; |
902 | 0 | const size_t snmpTrapAddress_oid_size = OID_LENGTH(snmpTrapAddress_oid); |
903 | 0 | const size_t forwarder_oid_len = OID_LENGTH(forwarder_oid); |
904 | 0 | struct in_addr agent_addr; |
905 | 0 | struct in_addr my_ip_addr; |
906 | |
|
907 | 0 | memset(&agent_addr, 0, sizeof(agent_addr)); |
908 | 0 | memset(&my_ip_addr, 0, sizeof(my_ip_addr)); |
909 | |
|
910 | 0 | if (pdu && pdu->transport_data && |
911 | 0 | pdu->transport_data_length == sizeof(*addr_pair1)) { |
912 | 0 | addr_pair1 = (netsnmp_indexed_addr_pair *)pdu->transport_data; |
913 | | |
914 | | /* |
915 | | * Get the IPv4 address of the host that this trap was sent from = |
916 | | * last forwarder's IP address. |
917 | | */ |
918 | 0 | if (addr_pair1->remote_addr.sa.sa_family == AF_INET) { |
919 | 0 | to1 = (struct sockaddr_in *)&(addr_pair1->remote_addr); |
920 | 0 | agent_addr = to1->sin_addr; |
921 | 0 | } |
922 | | /* |
923 | | * Get the IPv4 address of the host that this trap was sent to = |
924 | | * this forwarder's IP address. |
925 | | */ |
926 | 0 | if (addr_pair1->local_addr.sa.sa_family == AF_INET) { |
927 | 0 | to2 = (struct sockaddr_in *)&(addr_pair1->local_addr); |
928 | 0 | my_ip_addr = to2->sin_addr; |
929 | 0 | } |
930 | 0 | } |
931 | |
|
932 | 0 | if (to1) { |
933 | 0 | netsnmp_variable_list *vblist = NULL; |
934 | 0 | netsnmp_variable_list *var = NULL; |
935 | |
|
936 | 0 | if (*(in_addr_t *)pdu2->agent_addr == INADDR_ANY) { |
937 | | /* |
938 | | * there was no agent address defined in PDU. copy the forwarding |
939 | | * agent IP address from the transport socket. |
940 | | */ |
941 | 0 | *(struct in_addr *)pdu2->agent_addr = agent_addr; |
942 | 0 | } |
943 | |
|
944 | 0 | vblist = pdu2->variables; |
945 | | |
946 | | /* |
947 | | * Iterate over all varbinds in the PDU to see if it already has any |
948 | | * forwarder information. |
949 | | */ |
950 | 0 | for (var = vblist; var; var = var->next_variable) { |
951 | 0 | if (snmp_oid_ncompare(var->name, var->name_length, |
952 | 0 | snmpTrapAddress_oid, |
953 | 0 | snmpTrapAddress_oid_size, |
954 | 0 | snmpTrapAddress_oid_size) == 0) { |
955 | 0 | int my_snmpTrapAddress_index = |
956 | 0 | var->name[var->name_length - 1]; |
957 | |
|
958 | 0 | DEBUGMSGTL(("snmptrapd", " my_snmpTrapAddress_index=%d, last_snmpTrapAddress_index=%d, my_ip_addr=%s\n", |
959 | 0 | my_snmpTrapAddress_index, |
960 | 0 | last_snmpTrapAddress_index, |
961 | 0 | inet_ntoa(my_ip_addr))); |
962 | |
|
963 | 0 | if (last_snmpTrapAddress_index < my_snmpTrapAddress_index) |
964 | 0 | last_snmpTrapAddress_index = my_snmpTrapAddress_index; |
965 | | |
966 | | /* Detect forwarding loop. */ |
967 | 0 | if (var->val_len < 4) { |
968 | 0 | snmp_log(LOG_ERR, "Length of IP address of OID .1.3.6.1.6.3.18.1.3.%d in PDU is less than %d bytes = %d\n", |
969 | 0 | my_snmpTrapAddress_index, 4, |
970 | 0 | (int)var->val_len); |
971 | 0 | } else { |
972 | 0 | if (to2 && |
973 | 0 | memcmp(var->val.string, &my_ip_addr, 4) == 0) { |
974 | 0 | snmp_log(LOG_ERR, "Forwarding loop detected, OID .1.3.6.1.6.3.18.1.3.%d already has this forwarder's IP address=%s, not forwarding this trap\n", |
975 | 0 | my_snmpTrapAddress_index, |
976 | 0 | inet_ntoa(my_ip_addr)); |
977 | 0 | return 0; |
978 | 0 | } |
979 | 0 | if (memcmp(var->val.string, &agent_addr, 4) == 0) { |
980 | 0 | snmp_log(LOG_ERR, "Forwarding loop detected, OID .1.3.6.1.6.3.18.1.3.%d already has the sender's IP address=%s, not forwarding this trap\n", |
981 | 0 | my_snmpTrapAddress_index, |
982 | 0 | inet_ntoa(agent_addr)); |
983 | 0 | return 0; |
984 | 0 | } |
985 | 0 | } |
986 | 0 | } |
987 | 0 | } /* for var in vblist */ |
988 | | |
989 | 0 | DEBUGMSGTL(("snmptrapd", |
990 | 0 | " last_snmpTrapAddress_index=%d, adding index=%d\n", |
991 | 0 | last_snmpTrapAddress_index, last_snmpTrapAddress_index+1)); |
992 | | /* Change the last index of this OID to the next available number. */ |
993 | 0 | forwarder_oid[forwarder_oid_len - 1] = last_snmpTrapAddress_index + 1; |
994 | | |
995 | | /* |
996 | | * Add forwarder IP address as OID to trap payload. Use the value |
997 | | * from the transport, so if a v1 PDU is sent, the same IP is not |
998 | | * duplicated you want every forwarder to add this OID with its |
999 | | * own IP address. |
1000 | | */ |
1001 | 0 | snmp_pdu_add_variable(pdu2, forwarder_oid, forwarder_oid_len, |
1002 | 0 | ASN_IPADDRESS, (u_char *)&agent_addr, 4); |
1003 | 0 | } |
1004 | 0 | return 1; |
1005 | 0 | } |
1006 | | |
1007 | | /* |
1008 | | * Trap handler for forwarding to another destination |
1009 | | */ |
1010 | | int forward_handler( netsnmp_pdu *pdu, |
1011 | | netsnmp_transport *transport, |
1012 | | netsnmp_trapd_handler *handler) |
1013 | 0 | { |
1014 | 0 | netsnmp_session session, *ss; |
1015 | 0 | netsnmp_pdu *pdu2; |
1016 | 0 | char buf[BUFSIZ], *cp; |
1017 | |
|
1018 | 0 | DEBUGMSGTL(( "snmptrapd", "forward_handler (%s)\n", handler->token)); |
1019 | |
|
1020 | 0 | snmp_sess_init( &session ); |
1021 | 0 | if (strchr( handler->token, ':') == NULL) { |
1022 | 0 | snprintf( buf, BUFSIZ, "%s:%d", handler->token, SNMP_TRAP_PORT); |
1023 | 0 | cp = buf; |
1024 | 0 | } else { |
1025 | 0 | cp = handler->token; |
1026 | 0 | } |
1027 | 0 | session.peername = cp; |
1028 | 0 | session.version = pdu->version; |
1029 | 0 | ss = snmp_open( &session ); |
1030 | 0 | if (!ss) |
1031 | 0 | return NETSNMPTRAPD_HANDLER_FAIL; |
1032 | | |
1033 | | /* XXX: wjh we should be caching sessions here and not always |
1034 | | reopening a session. It's very inefficient, especially with v3 |
1035 | | INFORMS which may require engineID probing */ |
1036 | | |
1037 | 0 | pdu2 = snmp_clone_pdu(pdu); |
1038 | |
|
1039 | 0 | if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, |
1040 | 0 | NETSNMP_DS_LIB_ADD_FORWARDER_INFO) && |
1041 | 0 | !add_forwarder_info(pdu, pdu2)) { |
1042 | 0 | snmp_close(ss); |
1043 | 0 | return NETSNMPTRAPD_HANDLER_FAIL; |
1044 | 0 | } |
1045 | | |
1046 | 0 | if (pdu2->transport_data) { |
1047 | 0 | free(pdu2->transport_data); |
1048 | 0 | pdu2->transport_data = NULL; |
1049 | 0 | pdu2->transport_data_length = 0; |
1050 | 0 | } |
1051 | |
|
1052 | 0 | ss->s_snmp_errno = SNMPERR_SUCCESS; |
1053 | 0 | if (!snmp_send( ss, pdu2 ) && |
1054 | 0 | ss->s_snmp_errno != SNMPERR_SUCCESS) { |
1055 | 0 | snmp_sess_perror("Forward failed", ss); |
1056 | 0 | snmp_free_pdu(pdu2); |
1057 | 0 | } |
1058 | 0 | snmp_close( ss ); |
1059 | 0 | return NETSNMPTRAPD_HANDLER_OK; |
1060 | 0 | } |
1061 | | |
1062 | | #if defined(USING_NOTIFICATION_LOG_MIB_NOTIFICATION_LOG_MODULE) && defined(USING_AGENTX_SUBAGENT_MODULE) && !defined(NETSNMP_SNMPTRAPD_DISABLE_AGENTX) |
1063 | | /* |
1064 | | * "Notification" handler for implementing NOTIFICATION-MIB |
1065 | | * (presumably) |
1066 | | */ |
1067 | | int notification_handler(netsnmp_pdu *pdu, |
1068 | | netsnmp_transport *transport, |
1069 | | netsnmp_trapd_handler *handler) |
1070 | 0 | { |
1071 | 0 | DEBUGMSGTL(( "snmptrapd", "notification_handler\n")); |
1072 | 0 | log_notification(pdu, transport); |
1073 | 0 | return NETSNMPTRAPD_HANDLER_OK; |
1074 | 0 | } |
1075 | | #endif |
1076 | | |
1077 | | /*----------------------------- |
1078 | | * |
1079 | | * Main driving code, to process an incoming trap |
1080 | | * |
1081 | | *-----------------------------*/ |
1082 | | |
1083 | | |
1084 | | |
1085 | | int |
1086 | | snmp_input(int op, netsnmp_session *session, |
1087 | | int reqid, netsnmp_pdu *pdu, void *magic) |
1088 | 400 | { |
1089 | 400 | oid stdTrapOidRoot[] = { 1, 3, 6, 1, 6, 3, 1, 1, 5 }; |
1090 | 400 | oid snmpTrapOid[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 }; |
1091 | 400 | oid trapOid[MAX_OID_LEN+2] = {0}; |
1092 | 400 | int trapOidLen; |
1093 | 400 | netsnmp_variable_list *vars; |
1094 | 400 | netsnmp_trapd_handler *traph; |
1095 | 400 | netsnmp_transport *transport = (netsnmp_transport *) magic; |
1096 | 400 | int ret, idx; |
1097 | | |
1098 | 400 | switch (op) { |
1099 | 400 | case NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE: |
1100 | | /* |
1101 | | * Drops packets with reception problems |
1102 | | */ |
1103 | 400 | if (session->s_snmp_errno) { |
1104 | | /* drop problem packets */ |
1105 | 0 | return 1; |
1106 | 0 | } |
1107 | | |
1108 | | /* |
1109 | | * Determine the OID that identifies the trap being handled |
1110 | | */ |
1111 | 400 | DEBUGMSGTL(("snmptrapd", "input: %x\n", pdu->command)); |
1112 | 400 | switch (pdu->command) { |
1113 | 322 | case SNMP_MSG_TRAP: |
1114 | | /* |
1115 | | * Convert v1 traps into a v2-style trap OID |
1116 | | * (following RFC 2576) |
1117 | | */ |
1118 | 322 | if (pdu->trap_type == SNMP_TRAP_ENTERPRISESPECIFIC) { |
1119 | 127 | trapOidLen = pdu->enterprise_length; |
1120 | | /* |
1121 | | * Drop packets that would trigger an out-of-bounds trapOid[] |
1122 | | * access. |
1123 | | */ |
1124 | 127 | if (trapOidLen < 1 || trapOidLen > OID_LENGTH(trapOid) - 2) |
1125 | 12 | return 1; |
1126 | 115 | memcpy(trapOid, pdu->enterprise, sizeof(oid) * trapOidLen); |
1127 | 115 | if (trapOid[trapOidLen - 1] != 0) { |
1128 | 113 | trapOid[trapOidLen++] = 0; |
1129 | 113 | } |
1130 | 115 | trapOid[trapOidLen++] = pdu->specific_type; |
1131 | 195 | } else { |
1132 | 195 | memcpy(trapOid, stdTrapOidRoot, sizeof(stdTrapOidRoot)); |
1133 | 195 | trapOidLen = OID_LENGTH(stdTrapOidRoot); /* 9 */ |
1134 | | /* Drop packets with an invalid trap type. */ |
1135 | 195 | if (pdu->trap_type == LONG_MAX) |
1136 | 3 | return 1; |
1137 | 192 | trapOid[trapOidLen++] = pdu->trap_type+1; |
1138 | 192 | } |
1139 | 307 | break; |
1140 | | |
1141 | 307 | case SNMP_MSG_TRAP2: |
1142 | 27 | case SNMP_MSG_INFORM: |
1143 | | /* |
1144 | | * v2c/v3 notifications *should* have snmpTrapOID as the |
1145 | | * second varbind, so we can go straight there. |
1146 | | * But check, just to make sure |
1147 | | */ |
1148 | 27 | vars = pdu->variables; |
1149 | 27 | if (vars) |
1150 | 27 | vars = vars->next_variable; |
1151 | 27 | if (!vars || snmp_oid_compare(vars->name, vars->name_length, |
1152 | 27 | snmpTrapOid, OID_LENGTH(snmpTrapOid))) { |
1153 | | /* |
1154 | | * Didn't find it! |
1155 | | * Let's look through the full list.... |
1156 | | */ |
1157 | 0 | for ( vars = pdu->variables; vars; vars=vars->next_variable) { |
1158 | 0 | if (vars->type != ASN_OBJECT_ID) |
1159 | 0 | continue; |
1160 | 0 | if (!snmp_oid_compare(vars->name, vars->name_length, |
1161 | 0 | snmpTrapOid, OID_LENGTH(snmpTrapOid))) |
1162 | 0 | break; |
1163 | 0 | } |
1164 | 0 | if (!vars) { |
1165 | | /* |
1166 | | * Still can't find it! Give up. |
1167 | | */ |
1168 | 0 | snmp_log(LOG_ERR, "Cannot find TrapOID in TRAP2 PDU\n"); |
1169 | 0 | return 1; /* ??? */ |
1170 | 0 | } |
1171 | 0 | } |
1172 | 27 | trapOidLen = SNMP_MIN(sizeof(trapOid), vars->val_len) / sizeof(oid); |
1173 | 27 | memcpy(trapOid, vars->val.objid, trapOidLen * sizeof(oid)); |
1174 | 27 | break; |
1175 | | |
1176 | 51 | default: |
1177 | | /* SHOULDN'T HAPPEN! */ |
1178 | 51 | return 1; /* ??? */ |
1179 | 400 | } |
1180 | 334 | DEBUGMSGTL(( "snmptrapd", "Trap OID: ")); |
1181 | 334 | DEBUGMSGOID(("snmptrapd", trapOid, trapOidLen)); |
1182 | 334 | DEBUGMSG(( "snmptrapd", "\n")); |
1183 | | |
1184 | | |
1185 | | /* |
1186 | | * OK - We've found the Trap OID used to identify this trap. |
1187 | | * Call each of the various lists of handlers: |
1188 | | * a) authentication-related handlers, |
1189 | | * b) other handlers to be applied to all traps |
1190 | | * (*before* trap-specific handlers) |
1191 | | * c) the handler(s) specific to this trap |
1192 | | t * d) any other global handlers |
1193 | | * |
1194 | | * In each case, a particular trap handler can abort further |
1195 | | * processing - either just for that particular list, |
1196 | | * or for the trap completely. |
1197 | | * |
1198 | | * This is particularly designed for authentication-related |
1199 | | * handlers, but can also be used elsewhere. |
1200 | | * |
1201 | | * OK - Enough waffling, let's get to work..... |
1202 | | */ |
1203 | | |
1204 | 1.67k | for( idx = 0; handlers[idx].descr; ++idx ) { |
1205 | 1.33k | DEBUGMSGTL(("snmptrapd", "Running %s handlers\n", |
1206 | 1.33k | handlers[idx].descr)); |
1207 | 1.33k | if (NULL == handlers[idx].handler) /* specific */ |
1208 | 334 | traph = netsnmp_get_traphandler(trapOid, trapOidLen); |
1209 | 1.00k | else |
1210 | 1.00k | traph = *handlers[idx].handler; |
1211 | | |
1212 | 1.33k | for( ; traph; traph = traph->nexth) { |
1213 | 0 | if (!netsnmp_trapd_check_auth(traph->authtypes)) |
1214 | 0 | continue; /* we continue on and skip this one */ |
1215 | | |
1216 | 0 | ret = (*(traph->handler))(pdu, transport, traph); |
1217 | 0 | if(NETSNMPTRAPD_HANDLER_FINISH == ret) |
1218 | 0 | return 1; |
1219 | 0 | if (ret == NETSNMPTRAPD_HANDLER_BREAK) |
1220 | 0 | break; /* move on to next type */ |
1221 | 0 | } /* traph */ |
1222 | 1.33k | } /* handlers */ |
1223 | | |
1224 | | |
1225 | 334 | if (pdu->command == SNMP_MSG_INFORM) { |
1226 | 26 | netsnmp_pdu *reply = snmp_clone_pdu(pdu); |
1227 | 26 | if (!reply) { |
1228 | 0 | snmp_log(LOG_ERR, "couldn't clone PDU for INFORM response\n"); |
1229 | 26 | } else { |
1230 | 26 | reply->command = SNMP_MSG_RESPONSE; |
1231 | 26 | reply->errstat = 0; |
1232 | 26 | reply->errindex = 0; |
1233 | 26 | if (!snmp_send(session, reply)) { |
1234 | 26 | snmp_sess_perror("snmptrapd: Couldn't respond to inform pdu", |
1235 | 26 | session); |
1236 | 26 | snmp_free_pdu(reply); |
1237 | 26 | } |
1238 | 26 | } |
1239 | 26 | } |
1240 | | |
1241 | 334 | break; |
1242 | | |
1243 | 0 | case NETSNMP_CALLBACK_OP_TIMED_OUT: |
1244 | 0 | snmp_log(LOG_ERR, "Timeout: This shouldn't happen!\n"); |
1245 | 0 | break; |
1246 | | |
1247 | 0 | case NETSNMP_CALLBACK_OP_SEND_FAILED: |
1248 | 0 | snmp_log(LOG_ERR, "Send Failed: This shouldn't happen either!\n"); |
1249 | 0 | break; |
1250 | | |
1251 | 0 | case NETSNMP_CALLBACK_OP_CONNECT: |
1252 | 0 | case NETSNMP_CALLBACK_OP_DISCONNECT: |
1253 | | /* Ignore silently */ |
1254 | 0 | break; |
1255 | | |
1256 | 0 | default: |
1257 | 0 | snmp_log(LOG_ERR, "Unknown operation (%d): This shouldn't happen!\n", op); |
1258 | 0 | break; |
1259 | 400 | } |
1260 | 334 | return 0; |
1261 | 400 | } |
1262 | | |