#include #include #if HAVE_STDLIB_H #include #endif #if HAVE_UNISTD_H #include #endif #include #if HAVE_STRING_H #include #else #include #endif #include #include #if HAVE_NETINET_IN_H #include #endif #if HAVE_NETDB_H #include #endif #if HAVE_SYS_WAIT_H #include #endif #include #include #include #include #include #include #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); free(format); 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); free(format); 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; format = NULL; } } free(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); free(format); 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; } else { free(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; } ss->s_snmp_errno = SNMPERR_SUCCESS; if (!snmp_send( ss, pdu2 ) && ss->s_snmp_errno != SNMPERR_SUCCESS) { 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; }