| #include <net-snmp/net-snmp-config.h> |
| #include <net-snmp/net-snmp-features.h> |
| |
| #if HAVE_STDLIB_H |
| #include <stdlib.h> |
| #endif |
| #if HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| #include <stdio.h> |
| #if HAVE_STRING_H |
| #include <string.h> |
| #else |
| #include <strings.h> |
| #endif |
| #include <ctype.h> |
| #include <sys/types.h> |
| #if HAVE_NETINET_IN_H |
| #include <netinet/in.h> |
| #endif |
| #if HAVE_NETDB_H |
| #include <netdb.h> |
| #endif |
| #if HAVE_SYS_WAIT_H |
| #include <sys/wait.h> |
| #endif |
| |
| #include <net-snmp/config_api.h> |
| #include <net-snmp/output_api.h> |
| #include <net-snmp/mib_api.h> |
| #include <net-snmp/utilities.h> |
| |
| #include <net-snmp/net-snmp-includes.h> |
| #include <net-snmp/agent/net-snmp-agent-includes.h> |
| #include "utilities/execute.h" |
| #include "snmptrapd_handlers.h" |
| #include "snmptrapd_auth.h" |
| #include "snmptrapd_log.h" |
| #include "notification-log-mib/notification_log.h" |
| |
| netsnmp_feature_child_of(add_default_traphandler, snmptrapd) |
| |
| char *syslog_format1 = NULL; |
| char *syslog_format2 = NULL; |
| char *print_format1 = NULL; |
| char *print_format2 = NULL; |
| char *exec_format1 = NULL; |
| char *exec_format2 = NULL; |
| |
| int SyslogTrap = 0; |
| int dropauth = 0; |
| |
| 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"; |
| const char *trap2_std_str = "%.4y-%.2m-%.2l %.2h:%.2j:%.2k %B [%b]:\n%v\n"; |
| |
| void snmptrapd_free_traphandle(void); |
| |
| const char * |
| trap_description(int trap) |
| { |
| switch (trap) { |
| case SNMP_TRAP_COLDSTART: |
| return "Cold Start"; |
| case SNMP_TRAP_WARMSTART: |
| return "Warm Start"; |
| case SNMP_TRAP_LINKDOWN: |
| return "Link Down"; |
| case SNMP_TRAP_LINKUP: |
| return "Link Up"; |
| case SNMP_TRAP_AUTHFAIL: |
| return "Authentication Failure"; |
| case SNMP_TRAP_EGPNEIGHBORLOSS: |
| return "EGP Neighbor Loss"; |
| case SNMP_TRAP_ENTERPRISESPECIFIC: |
| return "Enterprise Specific"; |
| default: |
| return "Unknown Type"; |
| } |
| } |
| |
| |
| |
| void |
| snmptrapd_parse_traphandle(const char *token, char *line) |
| { |
| char buf[STRINGMAX]; |
| oid obuf[MAX_OID_LEN]; |
| size_t olen = MAX_OID_LEN; |
| char *cptr, *cp; |
| netsnmp_trapd_handler *traph; |
| int flags = 0; |
| char *format = NULL; |
| |
| memset( buf, 0, sizeof(buf)); |
| memset(obuf, 0, sizeof(obuf)); |
| cptr = copy_nword(line, buf, sizeof(buf)); |
| |
| if ( buf[0] == '-' && buf[1] == 'F' ) { |
| cptr = copy_nword(cptr, buf, sizeof(buf)); |
| format = strdup( buf ); |
| cptr = copy_nword(cptr, buf, sizeof(buf)); |
| } |
| if ( !cptr ) { |
| netsnmp_config_error("Missing traphandle command (%s)", buf); |
| return; |
| } |
| |
| DEBUGMSGTL(("read_config:traphandle", "registering handler for: ")); |
| if (!strcmp(buf, "default")) { |
| DEBUGMSG(("read_config:traphandle", "default")); |
| traph = netsnmp_add_global_traphandler(NETSNMPTRAPD_DEFAULT_HANDLER, |
| command_handler ); |
| } else { |
| cp = buf+strlen(buf)-1; |
| if ( *cp == '*' ) { |
| flags |= NETSNMP_TRAPHANDLER_FLAG_MATCH_TREE; |
| *(cp--) = '\0'; |
| if ( *cp == '.' ) { |
| /* |
| * Distinguish between 'oid.*' & 'oid*' |
| */ |
| flags |= NETSNMP_TRAPHANDLER_FLAG_STRICT_SUBTREE; |
| *(cp--) = '\0'; |
| } |
| } |
| if (!read_objid(buf, obuf, &olen)) { |
| netsnmp_config_error("Bad trap OID in traphandle directive: %s", |
| buf); |
| return; |
| } |
| DEBUGMSGOID(("read_config:traphandle", obuf, olen)); |
| traph = netsnmp_add_traphandler( command_handler, obuf, olen ); |
| } |
| |
| DEBUGMSG(("read_config:traphandle", "\n")); |
| |
| if (traph) { |
| traph->flags = flags; |
| traph->authtypes = TRAP_AUTH_EXE; |
| traph->token = strdup(cptr); |
| if (format) |
| traph->format = format; |
| } |
| } |
| |
| |
| static void |
| parse_forward(const char *token, char *line) |
| { |
| char buf[STRINGMAX]; |
| oid obuf[MAX_OID_LEN]; |
| size_t olen = MAX_OID_LEN; |
| char *cptr, *cp; |
| netsnmp_trapd_handler *traph; |
| int flags = 0; |
| char *format = NULL; |
| |
| memset( buf, 0, sizeof(buf)); |
| memset(obuf, 0, sizeof(obuf)); |
| cptr = copy_nword(line, buf, sizeof(buf)); |
| |
| if ( buf[0] == '-' && buf[1] == 'F' ) { |
| cptr = copy_nword(cptr, buf, sizeof(buf)); |
| format = strdup( buf ); |
| cptr = copy_nword(cptr, buf, sizeof(buf)); |
| } |
| DEBUGMSGTL(("read_config:forward", "registering forward for: ")); |
| if (!strcmp(buf, "default")) { |
| DEBUGMSG(("read_config:forward", "default")); |
| if ( !strcmp( cptr, "agentx" )) |
| traph = netsnmp_add_global_traphandler(NETSNMPTRAPD_DEFAULT_HANDLER, |
| axforward_handler ); |
| else |
| traph = netsnmp_add_global_traphandler(NETSNMPTRAPD_DEFAULT_HANDLER, |
| forward_handler ); |
| } else { |
| cp = buf+strlen(buf)-1; |
| if ( *cp == '*' ) { |
| flags |= NETSNMP_TRAPHANDLER_FLAG_MATCH_TREE; |
| *(cp--) = '\0'; |
| if ( *cp == '.' ) { |
| /* |
| * Distinguish between 'oid.*' & 'oid*' |
| */ |
| flags |= NETSNMP_TRAPHANDLER_FLAG_STRICT_SUBTREE; |
| *(cp--) = '\0'; |
| } |
| } |
| |
| if (!read_objid(buf, obuf, &olen)) { |
| netsnmp_config_error("Bad trap OID in forward directive: %s", buf); |
| return; |
| } |
| DEBUGMSGOID(("read_config:forward", obuf, olen)); |
| if ( !strcmp( cptr, "agentx" )) |
| traph = netsnmp_add_traphandler( axforward_handler, obuf, olen ); |
| else |
| traph = netsnmp_add_traphandler( forward_handler, obuf, olen ); |
| } |
| |
| DEBUGMSG(("read_config:forward", "\n")); |
| |
| if (traph) { |
| traph->flags = flags; |
| traph->authtypes = TRAP_AUTH_NET; |
| traph->token = strdup(cptr); |
| if (format) |
| traph->format = format; |
| } |
| } |
| |
| |
| void |
| parse_format(const char *token, char *line) |
| { |
| char *cp, *sep; |
| |
| /* |
| * Extract the first token from the value |
| * which tells us which style of format this is |
| */ |
| cp = line; |
| while (*cp && !isspace((unsigned char)(*cp))) |
| cp++; |
| if (!(*cp)) { |
| /* |
| * If we haven't got anything left, |
| * then this entry is malformed. |
| * So report this, and give up |
| */ |
| return; |
| } |
| |
| sep = cp; |
| *(cp++) = '\0'; |
| while (*cp && isspace((unsigned char)(*cp))) |
| cp++; |
| |
| /* |
| * OK - now "line" contains the format type, |
| * and cp points to the actual format string. |
| * So update the appropriate pointer(s). |
| */ |
| if (!strcmp( line, "print1")) { |
| SNMP_FREE( print_format1 ); |
| print_format1 = strdup(cp); |
| } else if (!strcmp( line, "print2")) { |
| SNMP_FREE( print_format2 ); |
| print_format2 = strdup(cp); |
| } else if (!strcmp( line, "print")) { |
| SNMP_FREE( print_format1 ); |
| SNMP_FREE( print_format2 ); |
| print_format1 = strdup(cp); |
| print_format2 = strdup(cp); |
| } else if (!strcmp( line, "syslog1")) { |
| SNMP_FREE( syslog_format1 ); |
| syslog_format1 = strdup(cp); |
| } else if (!strcmp( line, "syslog2")) { |
| SNMP_FREE( syslog_format2 ); |
| syslog_format2 = strdup(cp); |
| } else if (!strcmp( line, "syslog")) { |
| SNMP_FREE( syslog_format1 ); |
| SNMP_FREE( syslog_format2 ); |
| syslog_format1 = strdup(cp); |
| syslog_format2 = strdup(cp); |
| } else if (!strcmp( line, "execute1")) { |
| SNMP_FREE( exec_format1 ); |
| exec_format1 = strdup(cp); |
| } else if (!strcmp( line, "execute2")) { |
| SNMP_FREE( exec_format2 ); |
| exec_format2 = strdup(cp); |
| } else if (!strcmp( line, "execute")) { |
| SNMP_FREE( exec_format1 ); |
| SNMP_FREE( exec_format2 ); |
| exec_format1 = strdup(cp); |
| exec_format2 = strdup(cp); |
| } |
| |
| *sep = ' '; |
| } |
| |
| |
| static void |
| parse_trap1_fmt(const char *token, char *line) |
| { |
| print_format1 = strdup(line); |
| } |
| |
| |
| void |
| free_trap1_fmt(void) |
| { |
| if (print_format1 && print_format1 != trap1_std_str) |
| free((char *) print_format1); |
| print_format1 = NULL; |
| } |
| |
| |
| static void |
| parse_trap2_fmt(const char *token, char *line) |
| { |
| print_format2 = strdup(line); |
| } |
| |
| |
| void |
| free_trap2_fmt(void) |
| { |
| if (print_format2 && print_format2 != trap2_std_str) |
| free((char *) print_format2); |
| print_format2 = NULL; |
| } |
| |
| |
| void |
| snmptrapd_register_configs( void ) |
| { |
| register_config_handler("snmptrapd", "traphandle", |
| snmptrapd_parse_traphandle, |
| snmptrapd_free_traphandle, |
| "oid|\"default\" program [args ...] "); |
| register_config_handler("snmptrapd", "format1", |
| parse_trap1_fmt, free_trap1_fmt, "format"); |
| register_config_handler("snmptrapd", "format2", |
| parse_trap2_fmt, free_trap2_fmt, "format"); |
| register_config_handler("snmptrapd", "format", |
| parse_format, NULL, |
| "[print{,1,2}|syslog{,1,2}|execute{,1,2}] format"); |
| register_config_handler("snmptrapd", "forward", |
| parse_forward, NULL, "OID|\"default\" destination"); |
| } |
| |
| |
| |
| /*----------------------------- |
| * |
| * Routines to implement a "registry" of trap handlers |
| * |
| *-----------------------------*/ |
| |
| netsnmp_trapd_handler *netsnmp_auth_global_traphandlers = NULL; |
| netsnmp_trapd_handler *netsnmp_pre_global_traphandlers = NULL; |
| netsnmp_trapd_handler *netsnmp_post_global_traphandlers = NULL; |
| netsnmp_trapd_handler *netsnmp_default_traphandlers = NULL; |
| netsnmp_trapd_handler *netsnmp_specific_traphandlers = NULL; |
| |
| typedef struct netsnmp_handler_map_t { |
| netsnmp_trapd_handler **handler; |
| const char *descr; |
| } netsnmp_handler_map; |
| |
| static netsnmp_handler_map handlers[] = { |
| { &netsnmp_auth_global_traphandlers, "auth trap" }, |
| { &netsnmp_pre_global_traphandlers, "pre-global trap" }, |
| { NULL, "trap specific" }, |
| { &netsnmp_post_global_traphandlers, "global" }, |
| { NULL, NULL } |
| }; |
| |
| /* |
| * Register a new "global" traphandler, |
| * to be applied to *all* incoming traps |
| */ |
| netsnmp_trapd_handler * |
| netsnmp_add_global_traphandler(int list, Netsnmp_Trap_Handler *handler) |
| { |
| netsnmp_trapd_handler *traph; |
| |
| if ( !handler ) |
| return NULL; |
| |
| traph = SNMP_MALLOC_TYPEDEF(netsnmp_trapd_handler); |
| if ( !traph ) |
| return NULL; |
| |
| /* |
| * Add this new handler to the front of the appropriate list |
| * (or should it go on the end?) |
| */ |
| traph->handler = handler; |
| traph->authtypes = TRAP_AUTH_ALL; /* callers will likely change this */ |
| switch (list) { |
| case NETSNMPTRAPD_AUTH_HANDLER: |
| traph->nexth = netsnmp_auth_global_traphandlers; |
| netsnmp_auth_global_traphandlers = traph; |
| break; |
| case NETSNMPTRAPD_PRE_HANDLER: |
| traph->nexth = netsnmp_pre_global_traphandlers; |
| netsnmp_pre_global_traphandlers = traph; |
| break; |
| case NETSNMPTRAPD_POST_HANDLER: |
| traph->nexth = netsnmp_post_global_traphandlers; |
| netsnmp_post_global_traphandlers = traph; |
| break; |
| case NETSNMPTRAPD_DEFAULT_HANDLER: |
| traph->nexth = netsnmp_default_traphandlers; |
| netsnmp_default_traphandlers = traph; |
| break; |
| default: |
| free( traph ); |
| return NULL; |
| } |
| return traph; |
| } |
| |
| #ifndef NETSNMP_FEATURE_REMOVE_ADD_DEFAULT_TRAPHANDLER |
| /* |
| * Register a new "default" traphandler, to be applied to all |
| * traps with no specific trap handlers of their own. |
| */ |
| netsnmp_trapd_handler * |
| netsnmp_add_default_traphandler(Netsnmp_Trap_Handler *handler) { |
| return netsnmp_add_global_traphandler(NETSNMPTRAPD_DEFAULT_HANDLER, |
| handler); |
| } |
| #endif /* NETSNMP_FEATURE_REMOVE_ADD_DEFAULT_TRAPHANDLER */ |
| |
| |
| /* |
| * Register a new trap-specific traphandler |
| */ |
| netsnmp_trapd_handler * |
| netsnmp_add_traphandler(Netsnmp_Trap_Handler* handler, |
| oid *trapOid, int trapOidLen ) { |
| netsnmp_trapd_handler *traph, *traph2; |
| |
| if ( !handler ) |
| return NULL; |
| |
| traph = SNMP_MALLOC_TYPEDEF(netsnmp_trapd_handler); |
| if ( !traph ) |
| return NULL; |
| |
| /* |
| * Populate this new handler with the trap information |
| * (NB: the OID fields were not used in the default/global lists) |
| */ |
| traph->authtypes = TRAP_AUTH_ALL; /* callers will likely change this */ |
| traph->handler = handler; |
| traph->trapoid_len = trapOidLen; |
| traph->trapoid = snmp_duplicate_objid(trapOid, trapOidLen); |
| |
| /* |
| * Now try to find the appropriate place in the trap-specific |
| * list for this particular trap OID. If there's a matching OID |
| * already, then find it. Otherwise find the one that follows. |
| * If we run out of entried, the new one should be tacked onto the end. |
| */ |
| for (traph2 = netsnmp_specific_traphandlers; |
| traph2; traph2 = traph2->nextt) { |
| /* XXX - check this! */ |
| if (snmp_oid_compare(traph2->trapoid, traph2->trapoid_len, |
| trapOid, trapOidLen) <= 0) |
| break; |
| } |
| if (traph2) { |
| /* |
| * OK - We've either got an exact match, or we've found the |
| * entry *after* where the new one should go. |
| */ |
| if (!snmp_oid_compare(traph->trapoid, traph->trapoid_len, |
| traph2->trapoid, traph2->trapoid_len)) { |
| /* |
| * Exact match, so find the end of the *handler* list |
| * and tack on this new entry... |
| */ |
| while (traph2->nexth) |
| traph2 = traph2->nexth; |
| traph2->nexth = traph; |
| traph->nextt = traph2->nextt; /* Might as well... */ |
| traph->prevt = traph2->prevt; |
| } else { |
| /* |
| * .. or the following entry, so insert the new one before it. |
| */ |
| traph->prevt = traph2->prevt; |
| if (traph2->prevt) |
| traph2->prevt->nextt = traph; |
| else |
| netsnmp_specific_traphandlers = traph; |
| traph2->prevt = traph; |
| traph->nextt = traph2; |
| } |
| } else { |
| /* |
| * If we've run out of entries without finding a suitable spot, |
| * the new one should be tacked onto the end..... |
| */ |
| if (netsnmp_specific_traphandlers) { |
| traph2 = netsnmp_specific_traphandlers; |
| while (traph2->nextt) |
| traph2 = traph2->nextt; |
| traph2->nextt = traph; |
| traph->prevt = traph2; |
| } else { |
| /* |
| * .... unless this is the very first entry, of course! |
| */ |
| netsnmp_specific_traphandlers = traph; |
| } |
| } |
| |
| return traph; |
| } |
| |
| void |
| snmptrapd_free_traphandle(void) |
| { |
| netsnmp_trapd_handler *traph = NULL, *nextt = NULL, *nexth = NULL; |
| |
| DEBUGMSGTL(("snmptrapd", "Freeing trap handler lists\n")); |
| |
| /* |
| * Free default trap handlers |
| */ |
| traph = netsnmp_default_traphandlers; |
| /* loop over handlers */ |
| while (traph) { |
| DEBUGMSG(("snmptrapd", "Freeing default trap handler\n")); |
| nexth = traph->nexth; |
| SNMP_FREE(traph->token); |
| SNMP_FREE(traph); |
| traph = nexth; |
| } |
| netsnmp_default_traphandlers = NULL; |
| |
| /* |
| * Free specific trap handlers |
| */ |
| traph = netsnmp_specific_traphandlers; |
| /* loop over traps */ |
| while (traph) { |
| nextt = traph->nextt; |
| /* loop over handlers for this trap */ |
| while (traph) { |
| DEBUGMSG(("snmptrapd", "Freeing specific trap handler\n")); |
| nexth = traph->nexth; |
| SNMP_FREE(traph->token); |
| SNMP_FREE(traph->trapoid); |
| SNMP_FREE(traph); |
| traph = nexth; |
| } |
| traph = nextt; |
| } |
| netsnmp_specific_traphandlers = NULL; |
| } |
| |
| /* |
| * Locate the list of handlers for this particular Trap OID |
| * Returns NULL if there are no relevant traps |
| */ |
| netsnmp_trapd_handler * |
| netsnmp_get_traphandler( oid *trapOid, int trapOidLen ) { |
| netsnmp_trapd_handler *traph; |
| |
| if (!trapOid || !trapOidLen) { |
| DEBUGMSGTL(( "snmptrapd:lookup", "get_traphandler no OID!\n")); |
| return NULL; |
| } |
| DEBUGMSGTL(( "snmptrapd:lookup", "Looking up Trap OID: ")); |
| DEBUGMSGOID(("snmptrapd:lookup", trapOid, trapOidLen)); |
| DEBUGMSG(( "snmptrapd:lookup", "\n")); |
| |
| /* |
| * Look for a matching OID, and return that list... |
| */ |
| for (traph = netsnmp_specific_traphandlers; |
| traph; traph=traph->nextt ) { |
| |
| /* |
| * If the trap handler wasn't wildcarded, then the trapOID |
| * should match the registered OID exactly. |
| */ |
| if (!(traph->flags & NETSNMP_TRAPHANDLER_FLAG_MATCH_TREE)) { |
| if (snmp_oid_compare(traph->trapoid, traph->trapoid_len, |
| trapOid, trapOidLen) == 0) { |
| DEBUGMSGTL(( "snmptrapd:lookup", |
| "get_traphandler exact match (%p)\n", traph)); |
| return traph; |
| } |
| } else { |
| /* |
| * If the trap handler *was* wildcarded, then the trapOID |
| * should have the registered OID as a prefix... |
| */ |
| if (snmp_oidsubtree_compare(traph->trapoid, |
| traph->trapoid_len, |
| trapOid, trapOidLen) == 0) { |
| if (traph->flags & NETSNMP_TRAPHANDLER_FLAG_STRICT_SUBTREE) { |
| /* |
| * ... and (optionally) *strictly* as a prefix |
| * i.e. not including an exact match. |
| */ |
| if (snmp_oid_compare(traph->trapoid, traph->trapoid_len, |
| trapOid, trapOidLen) != 0) { |
| DEBUGMSGTL(( "snmptrapd:lookup", "get_traphandler strict subtree match (%p)\n", traph)); |
| return traph; |
| } |
| } else { |
| DEBUGMSGTL(( "snmptrapd:lookup", "get_traphandler subtree match (%p)\n", traph)); |
| return traph; |
| } |
| } |
| } |
| } |
| |
| /* |
| * .... or failing that, return the "default" list (which may be NULL) |
| */ |
| DEBUGMSGTL(( "snmptrapd:lookup", "get_traphandler default (%p)\n", |
| netsnmp_default_traphandlers)); |
| return netsnmp_default_traphandlers; |
| } |
| |
| /*----------------------------- |
| * |
| * Standard traphandlers for the common requirements |
| * |
| *-----------------------------*/ |
| |
| #define SYSLOG_V1_STANDARD_FORMAT "%a: %W Trap (%q) Uptime: %#T%#v\n" |
| #define SYSLOG_V1_ENTERPRISE_FORMAT "%a: %W Trap (%q) Uptime: %#T%#v\n" /* XXX - (%q) become (.N) ??? */ |
| #define SYSLOG_V23_NOTIFICATION_FORMAT "%B [%b]: Trap %#v\n" /* XXX - introduces a leading " ," */ |
| |
| /* |
| * Trap handler for logging via syslog |
| */ |
| int syslog_handler( netsnmp_pdu *pdu, |
| netsnmp_transport *transport, |
| netsnmp_trapd_handler *handler) |
| { |
| u_char *rbuf = NULL; |
| size_t r_len = 64, o_len = 0; |
| int trunc = 0; |
| |
| DEBUGMSGTL(( "snmptrapd", "syslog_handler\n")); |
| |
| if (SyslogTrap) |
| return NETSNMPTRAPD_HANDLER_OK; |
| |
| if ((rbuf = (u_char *) calloc(r_len, 1)) == NULL) { |
| snmp_log(LOG_ERR, "couldn't display trap -- malloc failed\n"); |
| return NETSNMPTRAPD_HANDLER_FAIL; /* Failed but keep going */ |
| } |
| |
| /* |
| * If there's a format string registered for this trap, then use it. |
| */ |
| if (handler && handler->format) { |
| DEBUGMSGTL(( "snmptrapd", "format = '%s'\n", handler->format)); |
| if (*handler->format) { |
| trunc = !realloc_format_trap(&rbuf, &r_len, &o_len, 1, |
| handler->format, pdu, transport); |
| } else { |
| free(rbuf); |
| return NETSNMPTRAPD_HANDLER_OK; /* A 0-length format string means don't log */ |
| } |
| |
| /* |
| * Otherwise (i.e. a NULL handler format string), |
| * use a standard output format setting |
| * either configurable, or hardwired |
| * |
| * XXX - v1 traps use a different hardwired formats for |
| * standard and enterprise specific traps |
| * Do we actually need this? |
| */ |
| } else { |
| if ( pdu->command == SNMP_MSG_TRAP ) { |
| if (syslog_format1) { |
| DEBUGMSGTL(( "snmptrapd", "syslog_format v1 = '%s'\n", syslog_format1)); |
| trunc = !realloc_format_trap(&rbuf, &r_len, &o_len, 1, |
| syslog_format1, pdu, transport); |
| |
| } else if (pdu->trap_type == SNMP_TRAP_ENTERPRISESPECIFIC) { |
| DEBUGMSGTL(( "snmptrapd", "v1 enterprise format\n")); |
| trunc = !realloc_format_trap(&rbuf, &r_len, &o_len, 1, |
| SYSLOG_V1_ENTERPRISE_FORMAT, |
| pdu, transport); |
| } else { |
| DEBUGMSGTL(( "snmptrapd", "v1 standard trap format\n")); |
| trunc = !realloc_format_trap(&rbuf, &r_len, &o_len, 1, |
| SYSLOG_V1_STANDARD_FORMAT, |
| pdu, transport); |
| } |
| } else { /* SNMPv2/3 notifications */ |
| if (syslog_format2) { |
| DEBUGMSGTL(( "snmptrapd", "syslog_format v1 = '%s'\n", syslog_format2)); |
| trunc = !realloc_format_trap(&rbuf, &r_len, &o_len, 1, |
| syslog_format2, pdu, transport); |
| } else { |
| DEBUGMSGTL(( "snmptrapd", "v2/3 format\n")); |
| trunc = !realloc_format_trap(&rbuf, &r_len, &o_len, 1, |
| SYSLOG_V23_NOTIFICATION_FORMAT, |
| pdu, transport); |
| } |
| } |
| } |
| snmp_log(LOG_WARNING, "%s%s", rbuf, (trunc?" [TRUNCATED]\n":"")); |
| free(rbuf); |
| return NETSNMPTRAPD_HANDLER_OK; |
| } |
| |
| |
| #define PRINT_V23_NOTIFICATION_FORMAT "%.4y-%.2m-%.2l %.2h:%.2j:%.2k %B [%b]:\n%v\n" |
| |
| /* |
| * Trap handler for logging to a file |
| */ |
| int print_handler( netsnmp_pdu *pdu, |
| netsnmp_transport *transport, |
| netsnmp_trapd_handler *handler) |
| { |
| u_char *rbuf = NULL; |
| size_t r_len = 64, o_len = 0; |
| int trunc = 0; |
| |
| DEBUGMSGTL(( "snmptrapd", "print_handler\n")); |
| |
| /* |
| * Don't bother logging authentication failures |
| * XXX - can we handle this via suitable handler entries instead? |
| */ |
| if (pdu->trap_type == SNMP_TRAP_AUTHFAIL && dropauth) |
| return NETSNMPTRAPD_HANDLER_OK; |
| |
| if ((rbuf = (u_char *) calloc(r_len, 1)) == NULL) { |
| snmp_log(LOG_ERR, "couldn't display trap -- malloc failed\n"); |
| return NETSNMPTRAPD_HANDLER_FAIL; /* Failed but keep going */ |
| } |
| |
| /* |
| * If there's a format string registered for this trap, then use it. |
| */ |
| if (handler && handler->format) { |
| DEBUGMSGTL(( "snmptrapd", "format = '%s'\n", handler->format)); |
| if (*handler->format) { |
| trunc = !realloc_format_trap(&rbuf, &r_len, &o_len, 1, |
| handler->format, pdu, transport); |
| } else { |
| free(rbuf); |
| return NETSNMPTRAPD_HANDLER_OK; /* A 0-length format string means don't log */ |
| } |
| |
| /* |
| * Otherwise (i.e. a NULL handler format string), |
| * use a standard output format setting |
| * either configurable, or hardwired |
| * |
| * XXX - v1 traps use a different routine for hardwired output |
| * Do we actually need this separate v1 routine? |
| * Or would a suitable format string be sufficient? |
| */ |
| } else { |
| if ( pdu->command == SNMP_MSG_TRAP ) { |
| if (print_format1) { |
| DEBUGMSGTL(( "snmptrapd", "print_format v1 = '%s'\n", print_format1)); |
| trunc = !realloc_format_trap(&rbuf, &r_len, &o_len, 1, |
| print_format1, pdu, transport); |
| } else { |
| DEBUGMSGTL(( "snmptrapd", "v1 format\n")); |
| trunc = !realloc_format_plain_trap(&rbuf, &r_len, &o_len, 1, |
| pdu, transport); |
| } |
| } else { |
| if (print_format2) { |
| DEBUGMSGTL(( "snmptrapd", "print_format v2 = '%s'\n", print_format2)); |
| trunc = !realloc_format_trap(&rbuf, &r_len, &o_len, 1, |
| print_format2, pdu, transport); |
| } else { |
| DEBUGMSGTL(( "snmptrapd", "v2/3 format\n")); |
| trunc = !realloc_format_trap(&rbuf, &r_len, &o_len, 1, |
| PRINT_V23_NOTIFICATION_FORMAT, |
| pdu, transport); |
| } |
| } |
| } |
| snmp_log(LOG_INFO, "%s%s", rbuf, (trunc?" [TRUNCATED]\n":"")); |
| free(rbuf); |
| return NETSNMPTRAPD_HANDLER_OK; |
| } |
| |
| |
| #define EXECUTE_FORMAT "%B\n%b\n%V\n%v\n" |
| |
| /* |
| * Trap handler for invoking a suitable script |
| */ |
| int command_handler( netsnmp_pdu *pdu, |
| netsnmp_transport *transport, |
| netsnmp_trapd_handler *handler) |
| { |
| #ifndef USING_UTILITIES_EXECUTE_MODULE |
| NETSNMP_LOGONCE((LOG_WARNING, |
| "support for run_shell_command not available\n")); |
| return NETSNMPTRAPD_HANDLER_FAIL; |
| #else |
| u_char *rbuf = NULL; |
| size_t r_len = 64, o_len = 0; |
| int oldquick; |
| |
| DEBUGMSGTL(( "snmptrapd", "command_handler\n")); |
| DEBUGMSGTL(( "snmptrapd", "token = '%s'\n", handler->token)); |
| if (handler && handler->token && *handler->token) { |
| netsnmp_pdu *v2_pdu = NULL; |
| if (pdu->command == SNMP_MSG_TRAP) |
| v2_pdu = convert_v1pdu_to_v2(pdu); |
| else |
| v2_pdu = pdu; |
| oldquick = netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_QUICK_PRINT); |
| netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_QUICK_PRINT, 1); |
| |
| /* |
| * Format the trap and pass this string to the external command |
| */ |
| if ((rbuf = (u_char *) calloc(r_len, 1)) == NULL) { |
| snmp_log(LOG_ERR, "couldn't display trap -- malloc failed\n"); |
| return NETSNMPTRAPD_HANDLER_FAIL; /* Failed but keep going */ |
| } |
| |
| /* |
| * If there's a format string registered for this trap, then use it. |
| * Otherwise use the standard execution format setting. |
| */ |
| if (handler && handler->format && *handler->format) { |
| DEBUGMSGTL(( "snmptrapd", "format = '%s'\n", handler->format)); |
| realloc_format_trap(&rbuf, &r_len, &o_len, 1, |
| handler->format, |
| v2_pdu, transport); |
| } else { |
| if ( pdu->command == SNMP_MSG_TRAP && exec_format1 ) { |
| DEBUGMSGTL(( "snmptrapd", "exec v1 = '%s'\n", exec_format1)); |
| realloc_format_trap(&rbuf, &r_len, &o_len, 1, |
| exec_format1, pdu, transport); |
| } else if ( pdu->command != SNMP_MSG_TRAP && exec_format2 ) { |
| DEBUGMSGTL(( "snmptrapd", "exec v2/3 = '%s'\n", exec_format2)); |
| realloc_format_trap(&rbuf, &r_len, &o_len, 1, |
| exec_format2, pdu, transport); |
| } else { |
| DEBUGMSGTL(( "snmptrapd", "execute format\n")); |
| realloc_format_trap(&rbuf, &r_len, &o_len, 1, EXECUTE_FORMAT, |
| v2_pdu, transport); |
| } |
| } |
| |
| /* |
| * and pass this formatted string to the command specified |
| */ |
| run_shell_command(handler->token, (char*)rbuf, NULL, NULL); /* Not interested in output */ |
| netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_QUICK_PRINT, oldquick); |
| if (pdu->command == SNMP_MSG_TRAP) |
| snmp_free_pdu(v2_pdu); |
| free(rbuf); |
| } |
| return NETSNMPTRAPD_HANDLER_OK; |
| #endif /* !def USING_UTILITIES_EXECUTE_MODULE */ |
| } |
| |
| |
| |
| |
| /* |
| * Trap handler for forwarding to the AgentX master agent |
| */ |
| int axforward_handler( netsnmp_pdu *pdu, |
| netsnmp_transport *transport, |
| netsnmp_trapd_handler *handler) |
| { |
| send_v2trap( pdu->variables ); |
| return NETSNMPTRAPD_HANDLER_OK; |
| } |
| |
| /* |
| * Trap handler for forwarding to another destination |
| */ |
| int forward_handler( netsnmp_pdu *pdu, |
| netsnmp_transport *transport, |
| netsnmp_trapd_handler *handler) |
| { |
| netsnmp_session session, *ss; |
| netsnmp_pdu *pdu2; |
| char buf[BUFSIZ], *cp; |
| |
| DEBUGMSGTL(( "snmptrapd", "forward_handler (%s)\n", handler->token)); |
| |
| snmp_sess_init( &session ); |
| if (strchr( handler->token, ':') == NULL) { |
| snprintf( buf, BUFSIZ, "%s:%d", handler->token, SNMP_TRAP_PORT); |
| cp = buf; |
| } else { |
| cp = handler->token; |
| } |
| session.peername = cp; |
| session.version = pdu->version; |
| ss = snmp_open( &session ); |
| if (!ss) |
| return NETSNMPTRAPD_HANDLER_FAIL; |
| |
| /* XXX: wjh we should be caching sessions here and not always |
| reopening a session. It's very ineffecient, especially with v3 |
| INFORMS which may require engineID probing */ |
| |
| pdu2 = snmp_clone_pdu(pdu); |
| if (pdu2->transport_data) { |
| free(pdu2->transport_data); |
| pdu2->transport_data = NULL; |
| pdu2->transport_data_length = 0; |
| } |
| if (!snmp_send( ss, pdu2 )) { |
| snmp_sess_perror("Forward failed", ss); |
| snmp_free_pdu(pdu2); |
| } |
| snmp_close( ss ); |
| return NETSNMPTRAPD_HANDLER_OK; |
| } |
| |
| #if defined(USING_NOTIFICATION_LOG_MIB_NOTIFICATION_LOG_MODULE) && defined(USING_AGENTX_SUBAGENT_MODULE) && !defined(NETSNMP_SNMPTRAPD_DISABLE_AGENTX) |
| /* |
| * "Notification" handler for implementing NOTIFICATION-MIB |
| * (presumably) |
| */ |
| int notification_handler(netsnmp_pdu *pdu, |
| netsnmp_transport *transport, |
| netsnmp_trapd_handler *handler) |
| { |
| DEBUGMSGTL(( "snmptrapd", "notification_handler\n")); |
| log_notification(pdu, transport); |
| return NETSNMPTRAPD_HANDLER_OK; |
| } |
| #endif |
| |
| /*----------------------------- |
| * |
| * Main driving code, to process an incoming trap |
| * |
| *-----------------------------*/ |
| |
| |
| |
| int |
| snmp_input(int op, netsnmp_session *session, |
| int reqid, netsnmp_pdu *pdu, void *magic) |
| { |
| oid stdTrapOidRoot[] = { 1, 3, 6, 1, 6, 3, 1, 1, 5 }; |
| oid snmpTrapOid[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 }; |
| oid trapOid[MAX_OID_LEN+2] = {0}; |
| int trapOidLen; |
| netsnmp_variable_list *vars; |
| netsnmp_trapd_handler *traph; |
| netsnmp_transport *transport = (netsnmp_transport *) magic; |
| int ret, idx; |
| |
| switch (op) { |
| case NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE: |
| /* |
| * Drops packets with reception problems |
| */ |
| if (session->s_snmp_errno) { |
| /* drop problem packets */ |
| return 1; |
| } |
| |
| /* |
| * Determine the OID that identifies the trap being handled |
| */ |
| DEBUGMSGTL(("snmptrapd", "input: %x\n", pdu->command)); |
| switch (pdu->command) { |
| case SNMP_MSG_TRAP: |
| /* |
| * Convert v1 traps into a v2-style trap OID |
| * (following RFC 2576) |
| */ |
| if (pdu->trap_type == SNMP_TRAP_ENTERPRISESPECIFIC) { |
| trapOidLen = pdu->enterprise_length; |
| memcpy(trapOid, pdu->enterprise, sizeof(oid) * trapOidLen); |
| if (trapOid[trapOidLen - 1] != 0) { |
| trapOid[trapOidLen++] = 0; |
| } |
| trapOid[trapOidLen++] = pdu->specific_type; |
| } else { |
| memcpy(trapOid, stdTrapOidRoot, sizeof(stdTrapOidRoot)); |
| trapOidLen = OID_LENGTH(stdTrapOidRoot); /* 9 */ |
| trapOid[trapOidLen++] = pdu->trap_type+1; |
| } |
| break; |
| |
| case SNMP_MSG_TRAP2: |
| case SNMP_MSG_INFORM: |
| /* |
| * v2c/v3 notifications *should* have snmpTrapOID as the |
| * second varbind, so we can go straight there. |
| * But check, just to make sure |
| */ |
| vars = pdu->variables; |
| if (vars) |
| vars = vars->next_variable; |
| if (!vars || snmp_oid_compare(vars->name, vars->name_length, |
| snmpTrapOid, OID_LENGTH(snmpTrapOid))) { |
| /* |
| * Didn't find it! |
| * Let's look through the full list.... |
| */ |
| for ( vars = pdu->variables; vars; vars=vars->next_variable) { |
| if (!snmp_oid_compare(vars->name, vars->name_length, |
| snmpTrapOid, OID_LENGTH(snmpTrapOid))) |
| break; |
| } |
| if (!vars) { |
| /* |
| * Still can't find it! Give up. |
| */ |
| snmp_log(LOG_ERR, "Cannot find TrapOID in TRAP2 PDU\n"); |
| return 1; /* ??? */ |
| } |
| } |
| memcpy(trapOid, vars->val.objid, vars->val_len); |
| trapOidLen = vars->val_len /sizeof(oid); |
| break; |
| |
| default: |
| /* SHOULDN'T HAPPEN! */ |
| return 1; /* ??? */ |
| } |
| DEBUGMSGTL(( "snmptrapd", "Trap OID: ")); |
| DEBUGMSGOID(("snmptrapd", trapOid, trapOidLen)); |
| DEBUGMSG(( "snmptrapd", "\n")); |
| |
| |
| /* |
| * OK - We've found the Trap OID used to identify this trap. |
| * Call each of the various lists of handlers: |
| * a) authentication-related handlers, |
| * b) other handlers to be applied to all traps |
| * (*before* trap-specific handlers) |
| * c) the handler(s) specific to this trap |
| t * d) any other global handlers |
| * |
| * In each case, a particular trap handler can abort further |
| * processing - either just for that particular list, |
| * or for the trap completely. |
| * |
| * This is particularly designed for authentication-related |
| * handlers, but can also be used elsewhere. |
| * |
| * OK - Enough waffling, let's get to work..... |
| */ |
| |
| for( idx = 0; handlers[idx].descr; ++idx ) { |
| DEBUGMSGTL(("snmptrapd", "Running %s handlers\n", |
| handlers[idx].descr)); |
| if (NULL == handlers[idx].handler) /* specific */ |
| traph = netsnmp_get_traphandler(trapOid, trapOidLen); |
| else |
| traph = *handlers[idx].handler; |
| |
| for( ; traph; traph = traph->nexth) { |
| if (!netsnmp_trapd_check_auth(traph->authtypes)) |
| continue; /* we continue on and skip this one */ |
| |
| ret = (*(traph->handler))(pdu, transport, traph); |
| if(NETSNMPTRAPD_HANDLER_FINISH == ret) |
| return 1; |
| if (ret == NETSNMPTRAPD_HANDLER_BREAK) |
| break; /* move on to next type */ |
| } /* traph */ |
| } /* handlers */ |
| |
| |
| if (pdu->command == SNMP_MSG_INFORM) { |
| netsnmp_pdu *reply = snmp_clone_pdu(pdu); |
| if (!reply) { |
| snmp_log(LOG_ERR, "couldn't clone PDU for INFORM response\n"); |
| } else { |
| reply->command = SNMP_MSG_RESPONSE; |
| reply->errstat = 0; |
| reply->errindex = 0; |
| if (!snmp_send(session, reply)) { |
| snmp_sess_perror("snmptrapd: Couldn't respond to inform pdu", |
| session); |
| snmp_free_pdu(reply); |
| } |
| } |
| } |
| |
| break; |
| |
| case NETSNMP_CALLBACK_OP_TIMED_OUT: |
| snmp_log(LOG_ERR, "Timeout: This shouldn't happen!\n"); |
| break; |
| |
| case NETSNMP_CALLBACK_OP_SEND_FAILED: |
| snmp_log(LOG_ERR, "Send Failed: This shouldn't happen either!\n"); |
| break; |
| |
| case NETSNMP_CALLBACK_OP_CONNECT: |
| case NETSNMP_CALLBACK_OP_DISCONNECT: |
| /* Ignore silently */ |
| break; |
| |
| default: |
| snmp_log(LOG_ERR, "Unknown operation (%d): This shouldn't happen!\n", op); |
| break; |
| } |
| return 0; |
| } |
| |