597 lines
17 KiB
C
597 lines
17 KiB
C
#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_global_vars.h"
|
|
|
|
#include "../agent/mibgroup/agentx/agentx_config.h"
|
|
#include "../agent/mibgroup/agentx/client.h"
|
|
#include "../agent/mibgroup/agentx/protocol.h"
|
|
#include "../agent/mibgroup/agentx/subagent.h"
|
|
|
|
netsnmp_feature_require(snmp_split_pdu)
|
|
netsnmp_feature_require(snmp_reset_var_types)
|
|
|
|
|
|
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[" ENV_SEPARATOR "...]\t\tload given list of MIBs (ALL loads "
|
|
"everything)\n"
|
|
" -M DIR[" ENV_SEPARATOR "...]\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, int reqid,
|
|
netsnmp_pdu *act, 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(tState self)
|
|
{
|
|
snmp_log(LOG_ERR, "Unexpected disconnect in state %s\n", self->name);
|
|
change_state(&Disconnecting);
|
|
}
|
|
|
|
static void
|
|
StateClose(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(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_log(LOG_ERR, "Failed to connect to AgentX server\n");
|
|
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_log(LOG_ERR, "Failed to create session\n");
|
|
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(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(tState self, netsnmp_pdu *act)
|
|
{
|
|
if(act->errstat == AGENTX_ERR_NOERROR) {
|
|
session = act->sessid;
|
|
change_state(&Notifying);
|
|
} else {
|
|
snmp_log(LOG_ERR, "Failed to open session");
|
|
change_state(&Exit);
|
|
}
|
|
}
|
|
|
|
const struct tState_s Opening = {
|
|
OpeningEntry,
|
|
NULL,
|
|
OpeningRes,
|
|
&Disconnecting,
|
|
StateDisconnect,
|
|
StateClose,
|
|
"Opening",
|
|
0
|
|
};
|
|
|
|
static void
|
|
NotifyingEntry(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(tState self, netsnmp_pdu *act)
|
|
{
|
|
if(act->errstat == AGENTX_ERR_NOERROR)
|
|
result = 0;
|
|
else
|
|
snmp_log(LOG_ERR, "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(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(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(tState self)
|
|
{
|
|
change_state(&Disconnecting);
|
|
}
|
|
|
|
static void
|
|
ClosingClose(tState self, netsnmp_pdu *act)
|
|
{
|
|
change_state(&Disconnecting);
|
|
}
|
|
|
|
const struct tState_s Closing = {
|
|
ClosingEntry,
|
|
NULL,
|
|
ClosingRes,
|
|
&Disconnecting,
|
|
ClosingDisconnect,
|
|
ClosingClose,
|
|
"Closing",
|
|
1
|
|
};
|
|
|
|
static void
|
|
DisconnectingEntry(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;
|
|
|
|
/* initialize tcpip, if necessary */
|
|
SOCK_STARTUP;
|
|
|
|
prognam = strrchr(argv[0], '/');
|
|
if (prognam)
|
|
++prognam;
|
|
else
|
|
prognam = argv[0];
|
|
|
|
putenv(strdup("POSIXLY_CORRECT=1"));
|
|
|
|
netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID,
|
|
NETSNMP_DS_LIB_DISABLE_PERSISTENT_LOAD, 1);
|
|
netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID,
|
|
NETSNMP_DS_LIB_DISABLE_PERSISTENT_SAVE, 1);
|
|
|
|
while ((arg = getopt(argc, argv, ":Vhm:M:D:dP:L:U:c:x:")) != -1) {
|
|
switch (arg) {
|
|
case 'h':
|
|
usage(prognam);
|
|
result = 0;
|
|
goto out;
|
|
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());
|
|
result = 0;
|
|
goto out;
|
|
#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);
|
|
goto out;
|
|
}
|
|
break;
|
|
#endif /* DISABLE_MIB_LOADING */
|
|
case 'L':
|
|
if (snmp_log_options(optarg, argc, argv) < 0)
|
|
goto out;
|
|
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);
|
|
goto out;
|
|
case '?':
|
|
fprintf(stderr, "Unrecognized option: -%c\n", optopt);
|
|
usage(prognam);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
arg = optind;
|
|
|
|
init_snmp(NETSNMP_APPLICATION_CONFIG_TYPE);
|
|
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);
|
|
goto out;
|
|
}
|
|
|
|
if (snmp_add_var(pdu, snmptrap_oid, snmptrap_oid_len, 'o', argv[arg])) {
|
|
snmp_perror(argv[arg]);
|
|
goto out;
|
|
}
|
|
++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]);
|
|
goto out;
|
|
}
|
|
if (!snmp_parse_oid(argv[arg - 3], name, &name_length)) {
|
|
snmp_perror(argv[arg - 3]);
|
|
goto out;
|
|
}
|
|
if (snmp_add_var(pdu, name, name_length, argv[arg - 2][0],
|
|
argv[arg - 1]) != 0) {
|
|
snmp_perror(argv[arg - 3]);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
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(NETSNMP_APPLICATION_CONFIG_TYPE);
|
|
|
|
out:
|
|
SOCK_CLEANUP;
|
|
return result;
|
|
}
|