summaryrefslogtreecommitdiff
path: root/apps/agentxtrap.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/agentxtrap.c')
-rw-r--r--apps/agentxtrap.c607
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);
+}