1 #include <net-snmp/net-snmp-config.h>
2 #include <net-snmp/net-snmp-features.h>
3
4 #include <errno.h>
5 #include <signal.h>
6 #include <string.h>
7 #ifdef HAVE_UNISTD_H
8 #include <unistd.h> /* optind, optarg and optopt */
9 #endif
10
11 #include <net-snmp/net-snmp-includes.h>
12 #include <net-snmp/agent/ds_agent.h>
13 #include "../agent_global_vars.h"
14
15 #include "../agent/mibgroup/agentx/agentx_config.h"
16 #include "../agent/mibgroup/agentx/client.h"
17 #include "../agent/mibgroup/agentx/protocol.h"
18 #include "../agent/mibgroup/agentx/subagent.h"
19
20 netsnmp_feature_require(snmp_split_pdu);
21 netsnmp_feature_require(snmp_reset_var_types);
22
23
24 static void
usage(const char * progname)25 usage(const char *progname)
26 {
27 fprintf(stderr,
28 "USAGE: %s [OPTIONS] TRAP-PARAMETERS\n"
29 "\n"
30 " Version: %s\n"
31 " Web: http://www.net-snmp.org/\n"
32 " Email: net-snmp-coders@lists.sourceforge.net\n"
33 "\n"
34 "OPTIONS:\n", progname, netsnmp_get_version());
35
36 fprintf(stderr,
37 " -h\t\t\tdisplay this help message\n"
38 " -V\t\t\tdisplay package version number\n"
39 " -m MIB[" ENV_SEPARATOR "...]\t\tload given list of MIBs (ALL loads everything)\n"
40 " -M DIR[" ENV_SEPARATOR "...]\t\tlook in given list of directories for MIBs\n"
41 " -D[TOKEN[,...]]\tturn on debugging output for the specified "
42 "TOKENs\n"
43 "\t\t\t (ALL gives extremely verbose debugging output)\n"
44 " -d\t\t\tdump all traffic\n");
45 #ifndef NETSNMP_DISABLE_MIB_LOADING
46 fprintf(stderr,
47 " -P MIBOPTS\t\tToggle various defaults controlling mib "
48 "parsing:\n");
49 snmp_mib_toggle_options_usage("\t\t\t ", stderr);
50 #endif /* NETSNMP_DISABLE_MIB_LOADING */
51 fprintf(stderr,
52 " -L LOGOPTS\t\tToggle various defaults controlling logging:\n");
53 snmp_log_options_usage("\t\t\t ", stderr);
54
55 fprintf(stderr,
56 " -c context\n"
57 " -U uptime\n"
58 " -x ADDRESS\t\tuse ADDRESS as AgentX address\n"
59 "\n"
60 "TRAP-PARAMETERS:\n"
61 " trapoid [OID TYPE VALUE] ...\n");
62 }
63
64 struct tState_s;
65 typedef const struct tState_s *tState;
66 struct tState_s {
67 void (*entry)(tState self);
68 /**<< State entry action */
69 void (*exit)(tState self);
70 /**<< State exit action */
71 /** Handler for AgentX-Response-PDU's */
72 void (*response)(tState self, netsnmp_pdu *res);
73 /** State to change to if an AgentX timeout occurs or the timer runs out */
74 tState timeout;
75 void (*disconnect)(tState self);
76 /**<< Handler for disconnect indications */
77 /** Handler for Close-PDU indications */
78 void (*close)(tState self, netsnmp_pdu *res);
79 const char *name; /**<< Name of the current state */
80 int is_open; /**<< If the connection is open in this state */
81 };
82
83 static tState state; /**<< Current state of the state machine */
84 static tState next_state; /**<< Next state of the state machine */
85 static const char *context; /**<< Context that delivers the trap */
86 static size_t contextLen; /**<< Length of eventual context */
87 static int result = 1; /**<< Program return value */
88 static netsnmp_pdu *pdu; /**<< The trap pdu that is to be sent */
89 /** The reference number of the next packet */
90 static long packetid;
91 /** The session id of the session to the master */
92 static long session;
93 static void *sessp; /**<< The current communication session */
94
95 #define STATE_CALL(method) \
96 if (!state->method) { \
97 snmp_log(LOG_ERR, "No " #method " method in %s, terminating\n", \
98 state->name); \
99 abort(); \
100 } else \
101 state->method
102
103 static void
change_state(tState new_state)104 change_state(tState new_state)
105 {
106 if (next_state && next_state != new_state)
107 DEBUGMSGTL(("process", "Ignore transition to %s\n",
108 next_state->name));
109 next_state = new_state;
110 }
111
112 static int
handle_agentx_response(int operation,netsnmp_session * sp,int reqid,netsnmp_pdu * act,void * magic)113 handle_agentx_response(int operation, netsnmp_session *sp, int reqid,
114 netsnmp_pdu *act, void *magic)
115 {
116 switch (operation) {
117 case NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE:
118 if (act->command == AGENTX_MSG_CLEANUPSET) {
119 /* Do nothing - no response and no action as nothing get
120 * allocated in any handler here
121 */
122 } else if (act->command != AGENTX_MSG_RESPONSE) {
123 /* Copy the head to a response */
124 netsnmp_pdu *res = snmp_split_pdu(act, 0, 0);
125 res->command = AGENTX_MSG_RESPONSE;
126 if (act->sessid != session || !state->is_open)
127 res->errstat = AGENTX_ERR_NOT_OPEN;
128 if (res->errstat == AGENTX_ERR_NOERROR)
129 switch (act->command) {
130 case AGENTX_MSG_GET:
131 res->variables = snmp_clone_varbind(act->variables);
132 snmp_reset_var_types(res->variables,
133 SNMP_NOSUCHOBJECT);
134 break;
135 case AGENTX_MSG_GETNEXT:
136 case AGENTX_MSG_GETBULK:
137 res->variables = snmp_clone_varbind(act->variables);
138 snmp_reset_var_types(res->variables,
139 SNMP_ENDOFMIBVIEW);
140 break;
141 case AGENTX_MSG_TESTSET:
142 res->errstat = SNMP_ERR_NOTWRITABLE;
143 res->errindex = 1;
144 break;
145 case AGENTX_MSG_COMMITSET:
146 res->errstat = SNMP_ERR_COMMITFAILED;
147 res->errindex = 1;
148 break;
149 case AGENTX_MSG_UNDOSET:
150 /* Success - could undo not setting any value :-) */
151 break;
152 case AGENTX_MSG_CLOSE:
153 /* Always let the master succeed! */
154 break;
155 default:
156 /* Unknown command */
157 res->errstat = AGENTX_ERR_PARSE_FAILED;
158 break;
159 }
160 if (snmp_send(sp, res) == 0)
161 snmp_free_pdu(res);
162 switch (act->command) {
163 case AGENTX_MSG_CLOSE:
164 /* Take action once the answer is sent! */
165 STATE_CALL(close)(state, act);
166 break;
167 default:
168 /* Do nothing */
169 break;
170 }
171 } else
172 /* RESPONSE act->time, act->errstat, act->errindex, varlist */
173 STATE_CALL(response)(state, act);
174 break;
175 case NETSNMP_CALLBACK_OP_TIMED_OUT:
176 change_state(state->timeout);
177 break;
178 case NETSNMP_CALLBACK_OP_DISCONNECT:
179 STATE_CALL(disconnect)(state);
180 break;
181 }
182 return 0;
183 }
184
185 static const struct tState_s Connecting;
186 static const struct tState_s Opening;
187 static const struct tState_s Notifying;
188 static const struct tState_s Closing;
189 static const struct tState_s Disconnecting;
190 static const struct tState_s Exit;
191
192 static void
StateDisconnect(tState self)193 StateDisconnect(tState self)
194 {
195 snmp_log(LOG_ERR, "Unexpected disconnect in state %s\n", self->name);
196 change_state(&Disconnecting);
197 }
198
199 static void
StateClose(tState self,netsnmp_pdu * act)200 StateClose(tState self, netsnmp_pdu *act)
201 {
202 snmp_log(LOG_ERR,
203 "Unexpected close with reason code %ld in state %s\n",
204 act->errstat, self->name);
205 change_state(&Disconnecting);
206 }
207
208 static void
ConnectingEntry(tState self)209 ConnectingEntry(tState self)
210 {
211 netsnmp_session init;
212 netsnmp_transport *t;
213 void *sess;
214
215 if (sessp) {
216 snmp_sess_close(sessp);
217 sessp = NULL;
218 }
219
220 snmp_sess_init(&init);
221 init.version = AGENTX_VERSION_1;
222 init.retries = 0; /* Retries are handled by the state machine */
223 init.timeout = SNMP_DEFAULT_TIMEOUT;
224 init.flags |= SNMP_FLAGS_STREAM_SOCKET;
225 init.callback = handle_agentx_response;
226 init.authenticator = NULL;
227
228 if (!(t = netsnmp_transport_open_client("agentx",
229 netsnmp_ds_get_string
230 (NETSNMP_DS_APPLICATION_ID,
231 NETSNMP_DS_AGENT_X_SOCKET)))) {
232 snmp_log(LOG_ERR, "Failed to connect to AgentX server\n");
233 change_state(&Exit);
234 } else if (!(sess = snmp_sess_add_ex(&init, t, NULL, agentx_parse, NULL,
235 NULL, agentx_realloc_build,
236 agentx_check_packet, NULL))) {
237 snmp_log(LOG_ERR, "Failed to create session\n");
238 change_state(&Exit);
239 } else {
240 sessp = sess;
241 change_state(&Opening);
242 }
243 }
244
245 static const struct tState_s Connecting = {
246 ConnectingEntry,
247 NULL,
248 NULL,
249 NULL,
250 StateDisconnect,
251 NULL,
252 "Connnecting",
253 0
254 };
255
256 static netsnmp_pdu *
pdu_create_opt_context(int command,const char * context,size_t len)257 pdu_create_opt_context(int command, const char *context, size_t len)
258 {
259 netsnmp_pdu *res = snmp_pdu_create(command);
260 if (res)
261 if (context) {
262 if (snmp_clone_mem((void **) &res->contextName, context, len)) {
263 snmp_free_pdu(res);
264 res = NULL;
265 } else
266 res->contextNameLen = len;
267 }
268 return res;
269 }
270
271 static void
OpeningEntry(tState self)272 OpeningEntry(tState self)
273 {
274 netsnmp_pdu *act =
275 pdu_create_opt_context(AGENTX_MSG_OPEN, context, contextLen);
276 if (act) {
277 act->sessid = 0;
278 act->transid = 0;
279 act->reqid = ++packetid;
280 act->time = 0;
281 snmp_pdu_add_variable(act, NULL, 0, ASN_OCTET_STR, NULL, 0);
282 if (snmp_sess_send(sessp, act) == 0)
283 snmp_free_pdu(act);
284 }
285 }
286
287 static void
OpeningRes(tState self,netsnmp_pdu * act)288 OpeningRes(tState self, netsnmp_pdu *act)
289 {
290 if (act->errstat == AGENTX_ERR_NOERROR) {
291 session = act->sessid;
292 change_state(&Notifying);
293 } else {
294 snmp_log(LOG_ERR, "Failed to open session");
295 change_state(&Exit);
296 }
297 }
298
299 static const struct tState_s Opening = {
300 OpeningEntry,
301 NULL,
302 OpeningRes,
303 &Disconnecting,
304 StateDisconnect,
305 StateClose,
306 "Opening",
307 0
308 };
309
310 static void
NotifyingEntry(tState self)311 NotifyingEntry(tState self)
312 {
313 netsnmp_pdu *act = snmp_clone_pdu(pdu);
314 if (act) {
315 act->sessid = session;
316 act->transid = 0;
317 act->reqid = ++packetid;
318 if (snmp_sess_send(sessp, act) == 0)
319 snmp_free_pdu(act);
320 }
321 }
322
323 static void
NotifyingRes(tState self,netsnmp_pdu * act)324 NotifyingRes(tState self, netsnmp_pdu *act)
325 {
326 if (act->errstat == AGENTX_ERR_NOERROR)
327 result = 0;
328 else
329 snmp_log(LOG_ERR, "Failed to send notification");
330 /** \todo: Retry handling --- ClosingReconnect??? */
331 change_state(&Closing);
332 }
333
334 static const struct tState_s Notifying = {
335 NotifyingEntry,
336 NULL,
337 NotifyingRes,
338 NULL, /** \todo: Retry handling? */
339 StateDisconnect, /** \todo: Retry handling? */
340 StateClose,
341 "Notifying",
342 1
343 };
344
345 static void
ClosingEntry(tState self)346 ClosingEntry(tState self)
347 {
348 /* CLOSE pdu->errstat */
349 netsnmp_pdu *act =
350 pdu_create_opt_context(AGENTX_MSG_CLOSE, context, contextLen);
351 if (act) {
352 act->sessid = session;
353 act->transid = 0;
354 act->reqid = ++packetid;
355 act->errstat = AGENTX_CLOSE_SHUTDOWN;
356 if (snmp_sess_send(sessp, act) == 0)
357 snmp_free_pdu(act);
358 }
359 }
360
361 static void
ClosingRes(tState self,netsnmp_pdu * act)362 ClosingRes(tState self, netsnmp_pdu *act)
363 {
364 if (act->errstat != AGENTX_ERR_NOERROR) {
365 snmp_log(LOG_ERR, "AgentX error status of %ld\n", act->errstat);
366 }
367 change_state(&Disconnecting);
368 }
369
370 static void
ClosingDisconnect(tState self)371 ClosingDisconnect(tState self)
372 {
373 change_state(&Disconnecting);
374 }
375
376 static void
ClosingClose(tState self,netsnmp_pdu * act)377 ClosingClose(tState self, netsnmp_pdu *act)
378 {
379 change_state(&Disconnecting);
380 }
381
382 static const struct tState_s Closing = {
383 ClosingEntry,
384 NULL,
385 ClosingRes,
386 &Disconnecting,
387 ClosingDisconnect,
388 ClosingClose,
389 "Closing",
390 1
391 };
392
393 static void
DisconnectingEntry(tState self)394 DisconnectingEntry(tState self)
395 {
396 snmp_sess_close(sessp);
397 sessp = NULL;
398 change_state(&Exit);
399 }
400
401 static const struct tState_s Disconnecting = {
402 DisconnectingEntry,
403 NULL,
404 NULL,
405 NULL,
406 NULL,
407 NULL,
408 "Disconnecting",
409 0
410 };
411
412 static const struct tState_s Exit = {
413 NULL,
414 NULL,
415 NULL,
416 NULL,
417 NULL,
418 NULL,
419 "Exit",
420 0
421 };
422
423 int
main(int argc,char * argv[])424 main(int argc, char *argv[])
425 {
426 int arg;
427 char *prognam;
428 char *cp = NULL;
429 const char *sysUpTime = NULL;
430
431 /* initialize tcpip, if necessary */
432 SOCK_STARTUP;
433
434 prognam = strrchr(argv[0], '/');
435 if (prognam)
436 ++prognam;
437 else
438 prognam = argv[0];
439
440 putenv(strdup("POSIXLY_CORRECT=1"));
441
442 netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID,
443 NETSNMP_DS_LIB_DISABLE_PERSISTENT_LOAD, 1);
444 netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID,
445 NETSNMP_DS_LIB_DISABLE_PERSISTENT_SAVE, 1);
446
447 while ((arg = getopt(argc, argv, ":Vhm:M:D:dP:L:U:c:x:")) != -1) {
448 switch (arg) {
449 case 'h':
450 usage(prognam);
451 result = 0;
452 goto out;
453 case 'm':
454 setenv("MIBS", optarg, 1);
455 break;
456 case 'M':
457 setenv("MIBDIRS", optarg, 1);
458 break;
459 case 'c':
460 context = optarg;
461 contextLen = strlen(context);
462 break;
463 case 'D':
464 debug_register_tokens(optarg);
465 snmp_set_do_debugging(1);
466 break;
467 case 'd':
468 netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID,
469 NETSNMP_DS_LIB_DUMP_PACKET, 1);
470 break;
471 case 'U':
472 sysUpTime = optarg;
473 break;
474 case 'V':
475 fprintf(stderr, "NET-SNMP version: %s\n",
476 netsnmp_get_version());
477 result = 0;
478 goto out;
479 #ifndef DISABLE_MIB_LOADING
480 case 'P':
481 cp = snmp_mib_toggle_options(optarg);
482 if (cp != NULL) {
483 fprintf(stderr, "Unknown parser option to -P: %c.\n", *cp);
484 usage(prognam);
485 goto out;
486 }
487 break;
488 #endif /* DISABLE_MIB_LOADING */
489 case 'L':
490 if (snmp_log_options(optarg, argc, argv) < 0)
491 goto out;
492 break;
493 case 'x':
494 if (optarg != NULL) {
495 netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID,
496 NETSNMP_DS_AGENT_X_SOCKET, optarg);
497 } else
498 usage(argv[0]);
499 break;
500
501 case ':':
502 fprintf(stderr, "Option -%c requires an operand\n", optopt);
503 usage(prognam);
504 goto out;
505 case '?':
506 fprintf(stderr, "Unrecognized option: -%c\n", optopt);
507 usage(prognam);
508 goto out;
509 }
510 }
511
512 arg = optind;
513
514 init_snmp(NETSNMP_APPLICATION_CONFIG_TYPE);
515 agentx_config_init();
516
517 /* NOTIFY varlist */
518 pdu = pdu_create_opt_context(AGENTX_MSG_NOTIFY, context, contextLen);
519
520 if (sysUpTime)
521 snmp_add_var(pdu, sysuptime_oid, sysuptime_oid_len, 't',
522 sysUpTime);
523
524 if (arg == argc) {
525 fprintf(stderr, "Missing trap-oid parameter\n");
526 usage(prognam);
527 goto out;
528 }
529
530 if (snmp_add_var(pdu, snmptrap_oid, snmptrap_oid_len, 'o', argv[arg])) {
531 snmp_perror(argv[arg]);
532 goto out;
533 }
534 ++arg;
535
536 while (arg < argc) {
537 oid name[MAX_OID_LEN];
538 size_t name_length = MAX_OID_LEN;
539 arg += 3;
540 if (arg > argc) {
541 fprintf(stderr, "%s: Missing type/value for variable\n",
542 argv[arg - 3]);
543 goto out;
544 }
545 if (!snmp_parse_oid(argv[arg - 3], name, &name_length)) {
546 snmp_perror(argv[arg - 3]);
547 goto out;
548 }
549 if (snmp_add_var(pdu, name, name_length, argv[arg - 2][0],
550 argv[arg - 1]) != 0) {
551 snmp_perror(argv[arg - 3]);
552 goto out;
553 }
554 }
555
556 packetid = 0;
557
558 state = &Connecting;
559 next_state = NULL;
560 if (state->entry)
561 state->entry(state);
562
563 /* main loop here... */
564 for (;;) {
565 int block = 1;
566 int numfds = 0;
567 int count;
568 fd_set fdset;
569 struct timeval timeout;
570 NETSNMP_SELECT_TIMEVAL timeout2;
571
572 while (next_state) {
573 if (state->exit)
574 state->exit(state);
575 DEBUGMSGTL(("process", "State transition: %s -> %s\n",
576 state->name, next_state->name));
577 state = next_state;
578 next_state = NULL;
579 if (state->entry)
580 state->entry(state);
581 }
582
583 if (state == &Exit)
584 break;
585
586 FD_ZERO(&fdset);
587 snmp_sess_select_info(sessp, &numfds, &fdset, &timeout, &block);
588 timeout2.tv_sec = timeout.tv_sec;
589 timeout2.tv_usec = timeout.tv_usec;
590 count = select(numfds, &fdset, NULL, NULL, !block ? &timeout2 : NULL);
591 if (count > 0)
592 snmp_sess_read(sessp, &fdset);
593 else if (count == 0)
594 snmp_sess_timeout(sessp);
595 else if (errno != EINTR) {
596 snmp_log(LOG_ERR, "select error [%s]\n", strerror(errno));
597 change_state(&Exit);
598 }
599 }
600
601 /* at shutdown time */
602 snmp_free_pdu(pdu);
603 pdu = NULL;
604
605 snmp_shutdown(NETSNMP_APPLICATION_CONFIG_TYPE);
606
607 out:
608 SOCK_CLEANUP;
609 return result;
610 }
611