diff options
Diffstat (limited to 'apps/agentxtrap.c')
-rw-r--r-- | apps/agentxtrap.c | 607 |
1 files changed, 607 insertions, 0 deletions
diff --git a/apps/agentxtrap.c b/apps/agentxtrap.c new file mode 100644 index 0000000..f366f61 --- /dev/null +++ b/apps/agentxtrap.c @@ -0,0 +1,607 @@ +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-features.h> + +#include <errno.h> +#include <signal.h> +#include <string.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> /* optind, optarg and optopt */ +#endif + +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/ds_agent.h> + +#include "../agent/mibgroup/agentx/agentx_config.h" +#include "../agent/mibgroup/agentx/client.h" +#include "../agent/mibgroup/agentx/protocol.h" + +netsnmp_feature_require(snmp_split_pdu) +netsnmp_feature_require(snmp_reset_var_types) + + +#ifdef __GNUC__ +#define UNUSED __attribute__((unused)) +#else +#define UNUSED +#endif + +extern const oid sysuptime_oid[]; +extern const size_t sysuptime_oid_len; +extern const oid snmptrap_oid[]; +extern const size_t snmptrap_oid_len; + +static void +usage(const char* progname) +{ + fprintf(stderr, + "USAGE: %s [OPTIONS] TRAP-PARAMETERS\n" + "\n" + " Version: %s\n" + " Web: http://www.net-snmp.org/\n" + " Email: net-snmp-coders@lists.sourceforge.net\n" + "\n" + "OPTIONS:\n", progname, netsnmp_get_version()); + + fprintf(stderr, + " -h\t\t\tdisplay this help message\n" + " -V\t\t\tdisplay package version number\n" + " -m MIB[:...]\t\tload given list of MIBs (ALL loads " + "everything)\n" + " -M DIR[:...]\t\tlook in given list of directories for MIBs\n" + " -D[TOKEN[,...]]\tturn on debugging output for the specified " + "TOKENs\n" + "\t\t\t (ALL gives extremely verbose debugging output)\n" + " -d\t\t\tdump all traffic\n"); +#ifndef NETSNMP_DISABLE_MIB_LOADING + fprintf(stderr, + " -P MIBOPTS\t\tToggle various defaults controlling mib " + "parsing:\n"); + snmp_mib_toggle_options_usage("\t\t\t ", stderr); +#endif /* NETSNMP_DISABLE_MIB_LOADING */ + fprintf(stderr, + " -L LOGOPTS\t\tToggle various defaults controlling logging:\n"); + snmp_log_options_usage("\t\t\t ", stderr); + + fprintf(stderr, + " -c context\n" + " -U uptime\n" + " -x ADDRESS\t\tuse ADDRESS as AgentX address\n" + "\n" + "TRAP-PARAMETERS:\n" + " trapoid [OID TYPE VALUE] ...\n"); +} + +struct tState_s; +typedef const struct tState_s* tState; +struct tState_s { + void (*entry)(tState self); /**<< State entry action */ + void (*exit)(tState self); /**<< State exit action */ + /** Handler for AgentX-Response-PDU's */ + void (*response)(tState self, netsnmp_pdu *res); + /** State to change to if an AgentX timeout occurs or the timer runs out */ + tState timeout; + void (*disconnect)(tState self); /**<< Handler for disconnect indications */ + /** Handler for Close-PDU indications */ + void (*close)(tState self, netsnmp_pdu *res); + const char* name; /**<< Name of the current state */ + int is_open; /**<< If the connection is open in this state */ +}; + +static tState state; /**<< Current state of the state machine */ +static tState next_state; /**<< Next state of the state machine */ + +static const char *context = NULL; /**<< Context that delivers the trap */ +static size_t contextLen; /**<< Length of eventual context */ +static int result = 1; /**<< Program return value */ +static netsnmp_pdu *pdu = NULL; /**<< The trap pdu that is to be sent */ +/** The reference number of the next packet */ +static long packetid = 0; +/** The session id of the session to the master */ +static long session; +static void *sessp = NULL; /**<< The current communication session */ + +#define STATE_CALL(method) \ + if(!state->method) { \ + snmp_log(LOG_ERR, "No " #method " method in %s, terminating\n", \ + state->name); \ + abort(); \ + } else \ + state->method + +static void +change_state(tState new_state) +{ + if (next_state && next_state != new_state) + DEBUGMSGTL(("process", "Ignore transition to %s\n", next_state->name)); + next_state = new_state; +} + +static int +handle_agentx_response(int operation, netsnmp_session *sp, UNUSED int reqid, + netsnmp_pdu *act, UNUSED void *magic) +{ + switch(operation) { + case NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE: + if(act->command == AGENTX_MSG_CLEANUPSET) { + /* Do nothing - no response and no action as nothing get + * allocated in any handler here + */ + } else if(act->command != AGENTX_MSG_RESPONSE) { + /* Copy the head to a response */ + netsnmp_pdu* res = snmp_split_pdu(act, 0, 0); + res->command = AGENTX_MSG_RESPONSE; + if (act->sessid != session || !state->is_open) + res->errstat = AGENTX_ERR_NOT_OPEN; + if(res->errstat == AGENTX_ERR_NOERROR) + switch(act->command) { + case AGENTX_MSG_GET: + res->variables = snmp_clone_varbind(act->variables); + snmp_reset_var_types(res->variables, SNMP_NOSUCHOBJECT); + break; + case AGENTX_MSG_GETNEXT: + case AGENTX_MSG_GETBULK: + res->variables = snmp_clone_varbind(act->variables); + snmp_reset_var_types(res->variables, SNMP_ENDOFMIBVIEW); + break; + case AGENTX_MSG_TESTSET: + res->errstat = SNMP_ERR_NOTWRITABLE; + res->errindex = 1; + break; + case AGENTX_MSG_COMMITSET: + res->errstat = SNMP_ERR_COMMITFAILED; + res->errindex = 1; + break; + case AGENTX_MSG_UNDOSET: + /* Success - could undo not setting any value :-) */ + break; + case AGENTX_MSG_CLOSE: + /* Always let the master succeed! */ + break; + default: + /* Unknown command */ + res->errstat = AGENTX_ERR_PARSE_FAILED; + break; + } + if(snmp_send(sp, res) == 0) + snmp_free_pdu(res); + switch(act->command) { + case AGENTX_MSG_CLOSE: + /* Take action once the answer is sent! */ + STATE_CALL(close)(state, act); + break; + default: + /* Do nothing */ + break; + } + } else + /* RESPONSE act->time, act->errstat, act->errindex, varlist */ + STATE_CALL(response)(state, act); + break; + case NETSNMP_CALLBACK_OP_TIMED_OUT: + change_state(state->timeout); + break; + case NETSNMP_CALLBACK_OP_DISCONNECT: + STATE_CALL(disconnect)(state); + break; + } + return 0; +} + +extern const struct tState_s Connecting; +extern const struct tState_s Opening; +extern const struct tState_s Notifying; +extern const struct tState_s Closing; +extern const struct tState_s Disconnecting; +extern const struct tState_s Exit; + +static void +StateDisconnect(UNUSED tState self) +{ + snmp_log(LOG_ERR, "Unexpected disconnect in state %s\n", self->name); + change_state(&Disconnecting); +} + +static void +StateClose(UNUSED tState self, netsnmp_pdu *act) +{ + snmp_log(LOG_ERR, "Unexpected close with reason code %ld in state %s\n", + act->errstat, self->name); + change_state(&Disconnecting); +} + +static void +ConnectingEntry(UNUSED tState self) +{ + netsnmp_session init; + netsnmp_transport* t; + void* sess; + + if(sessp) { + snmp_sess_close(sessp); + sessp = NULL; + } + + snmp_sess_init(&init); + init.version = AGENTX_VERSION_1; + init.retries = 0; /* Retries are handled by the state machine */ + init.timeout = SNMP_DEFAULT_TIMEOUT; + init.flags |= SNMP_FLAGS_STREAM_SOCKET; + init.callback = handle_agentx_response; + init.authenticator = NULL; + + if(!(t = netsnmp_transport_open_client( + "agentx", netsnmp_ds_get_string( + NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_X_SOCKET)))) { + snmp_perror("Failed to connect to AgentX server"); + change_state(&Exit); + } else if(!(sess = snmp_sess_add_ex( + &init, t, NULL, agentx_parse, NULL, NULL, + agentx_realloc_build, agentx_check_packet, NULL))) { + snmp_perror("Failed to create session"); + change_state(&Exit); + } else { + sessp = sess; + change_state(&Opening); + } +} + +const struct tState_s Connecting = { + ConnectingEntry, + NULL, + NULL, + NULL, + StateDisconnect, + NULL, + "Connnecting", + 0 +}; + +static netsnmp_pdu* +pdu_create_opt_context(int command, const char* context, size_t len) +{ + netsnmp_pdu* res = snmp_pdu_create(command); + if (res) + if (context) { + if (snmp_clone_mem((void**)&res->contextName, context, len)) { + snmp_free_pdu(res); + res = NULL; + } else + res->contextNameLen = len; + } + return res; +} + +static void +OpeningEntry(UNUSED tState self) +{ + netsnmp_pdu* act = + pdu_create_opt_context(AGENTX_MSG_OPEN, context, contextLen); + if(act) { + act->sessid = 0; + act->transid = 0; + act->reqid = ++packetid; + act->time = 0; + snmp_pdu_add_variable(act, NULL, 0, ASN_OCTET_STR, NULL, 0); + if(snmp_sess_send(sessp, act) == 0) + snmp_free_pdu(act); + } +} + +static void +OpeningRes(UNUSED tState self, netsnmp_pdu *act) +{ + if(act->errstat == AGENTX_ERR_NOERROR) { + session = act->sessid; + change_state(&Notifying); + } else { + snmp_perror("Failed to open session"); + change_state(&Exit); + } +} + +const struct tState_s Opening = { + OpeningEntry, + NULL, + OpeningRes, + &Disconnecting, + StateDisconnect, + StateClose, + "Opening", + 0 +}; + +static void +NotifyingEntry(UNUSED tState self) +{ + netsnmp_pdu* act = snmp_clone_pdu(pdu); + if(act) { + act->sessid = session; + act->transid = 0; + act->reqid = ++packetid; + if(snmp_sess_send(sessp, act) == 0) + snmp_free_pdu(act); + } +} + +static void +NotifyingRes(UNUSED tState self, netsnmp_pdu *act) +{ + if(act->errstat == AGENTX_ERR_NOERROR) + result = 0; + else + snmp_perror("Failed to send notification"); + /** \todo: Retry handling --- ClosingReconnect??? */ + change_state(&Closing); +} + +const struct tState_s Notifying = { + NotifyingEntry, + NULL, + NotifyingRes, + NULL, /** \todo: Retry handling? */ + StateDisconnect, /** \todo: Retry handling? */ + StateClose, + "Notifying", + 1 +}; + +static void +ClosingEntry(UNUSED tState self) +{ + /* CLOSE pdu->errstat */ + netsnmp_pdu* act = + pdu_create_opt_context(AGENTX_MSG_CLOSE, context, contextLen); + if(act) { + act->sessid = session; + act->transid = 0; + act->reqid = ++packetid; + act->errstat = AGENTX_CLOSE_SHUTDOWN; + if(snmp_sess_send(sessp, act) == 0) + snmp_free_pdu(act); + } +} + +static void +ClosingRes(UNUSED tState self, netsnmp_pdu *act) +{ + if(act->errstat != AGENTX_ERR_NOERROR) { + snmp_log(LOG_ERR, "AgentX error status of %ld\n", act->errstat); + } + change_state(&Disconnecting); +} + +static void +ClosingDisconnect(UNUSED tState self) +{ + change_state(&Disconnecting); +} + +static void +ClosingClose(UNUSED tState self, UNUSED netsnmp_pdu *act) +{ + change_state(&Disconnecting); +} + +const struct tState_s Closing = { + ClosingEntry, + NULL, + ClosingRes, + &Disconnecting, + ClosingDisconnect, + ClosingClose, + "Closing", + 1 +}; + +static void +DisconnectingEntry(UNUSED tState self) +{ + snmp_sess_close(sessp); + sessp = NULL; + change_state(&Exit); +} + +const struct tState_s Disconnecting = { + DisconnectingEntry, + NULL, + NULL, + NULL, + NULL, + NULL, + "Disconnecting", + 0 +}; + +const struct tState_s Exit = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "Exit", + 0 +}; + +int +main(int argc, char *argv[]) +{ + int arg; + char *prognam; + char *cp = NULL; + + const char* sysUpTime = NULL; + + prognam = strrchr(argv[0], '/'); + if (prognam) + ++prognam; + else + prognam = argv[0]; + + putenv(strdup("POSIXLY_CORRECT=1")); + + while ((arg = getopt(argc, argv, ":Vhm:M:D:dP:L:U:c:x:")) != -1) { + switch (arg) { + case 'h': + usage(prognam); + exit(0); + case 'm': + setenv("MIBS", optarg, 1); + break; + case 'M': + setenv("MIBDIRS", optarg, 1); + break; + case 'c': + context = optarg; + contextLen = strlen(context); + break; + case 'D': + debug_register_tokens(optarg); + snmp_set_do_debugging(1); + break; + case 'd': + netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_DUMP_PACKET, 1); + break; + case 'U': + sysUpTime = optarg; + break; + case 'V': + fprintf(stderr, "NET-SNMP version: %s\n", netsnmp_get_version()); + exit(0); + break; +#ifndef DISABLE_MIB_LOADING + case 'P': + cp = snmp_mib_toggle_options(optarg); + if (cp != NULL) { + fprintf(stderr, "Unknown parser option to -P: %c.\n", *cp); + usage(prognam); + exit(1); + } + break; +#endif /* DISABLE_MIB_LOADING */ + case 'L': + if (snmp_log_options(optarg, argc, argv) < 0) { + exit(1); + } + break; + case 'x': + if (optarg != NULL) { + netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, + NETSNMP_DS_AGENT_X_SOCKET, optarg); + } else + usage(argv[0]); + break; + + case ':': + fprintf(stderr, "Option -%c requires an operand\n", optopt); + usage(prognam); + exit(1); + break; + case '?': + fprintf(stderr, "Unrecognized option: -%c\n", optopt); + usage(prognam); + exit(1); + break; + } + } + + arg = optind; + + /* initialize tcpip, if necessary */ + SOCK_STARTUP; + + init_snmp("snmpapp"); + agentx_config_init(); + + /* NOTIFY varlist */ + pdu = pdu_create_opt_context(AGENTX_MSG_NOTIFY, context, contextLen); + + if (sysUpTime) + snmp_add_var(pdu, sysuptime_oid, sysuptime_oid_len, 't', sysUpTime); + + if (arg == argc) { + fprintf(stderr, "Missing trap-oid parameter\n"); + usage(prognam); + SOCK_CLEANUP; + exit(1); + } + + if (snmp_add_var(pdu, snmptrap_oid, snmptrap_oid_len, 'o', argv[arg])) { + snmp_perror(argv[arg]); + SOCK_CLEANUP; + exit(1); + } + ++arg; + + while (arg < argc) { + oid name[MAX_OID_LEN]; + size_t name_length = MAX_OID_LEN; + arg += 3; + if (arg > argc) { + fprintf(stderr, "%s: Missing type/value for variable\n", + argv[arg - 3]); + SOCK_CLEANUP; + exit(1); + } + if (!snmp_parse_oid(argv[arg - 3], name, &name_length)) { + snmp_perror(argv[arg - 3]); + SOCK_CLEANUP; + exit(1); + } + if (snmp_add_var(pdu, name, name_length, argv[arg - 2][0], + argv[arg - 1]) != 0) { + snmp_perror(argv[arg - 3]); + SOCK_CLEANUP; + exit(1); + } + } + + packetid = 0; + + state = &Connecting; + next_state = NULL; + if(state->entry) state->entry(state); + + /* main loop here... */ + for(;;) { + int block = 1; + int numfds = 0; + int count; + fd_set fdset; + struct timeval timeout; + + while(next_state) { + if(state->exit) state->exit(state); + DEBUGMSGTL(("process", "State transition: %s -> %s\n", + state->name, next_state->name)); + state = next_state; + next_state = NULL; + if(state->entry) state->entry(state); + } + + if(state == &Exit) + break; + + FD_ZERO(&fdset); + snmp_sess_select_info(sessp, &numfds, &fdset, &timeout, &block); + count = select(numfds, &fdset, NULL, NULL, !block ? &timeout : NULL); + if (count > 0) + snmp_sess_read(sessp, &fdset); + else if (count == 0) + snmp_sess_timeout(sessp); + else if (errno != EINTR) { + snmp_log(LOG_ERR, "select error [%s]\n", strerror(errno)); + change_state(&Exit); + } + } + + /* at shutdown time */ + snmp_free_pdu(pdu); + pdu = NULL; + + snmp_shutdown("snmpapp"); + + SOCK_CLEANUP; + exit(result); +} |