2 * agent_trap.c: define trap generation routines for mib modules, etc,
6 #include <net-snmp/net-snmp-config.h>
22 #if TIME_WITH_SYS_TIME
24 # include <sys/timeb.h>
26 # include <sys/time.h>
31 # include <sys/time.h>
37 #include <sys/socket.h>
42 #include <netinet/in.h>
44 #include <net-snmp/utilities.h>
50 #include <net-snmp/net-snmp-includes.h>
51 #include <net-snmp/agent/agent_trap.h>
52 #include <net-snmp/agent/snmp_agent.h>
53 #include <net-snmp/agent/agent_callbacks.h>
55 #include <net-snmp/agent/mib_module_config.h>
58 netsnmp_session *sesp;
59 struct trap_sink *next;
64 struct trap_sink *sinks = NULL;
66 extern struct timeval starttime;
68 #ifdef BRCM_SNMP_MIB_SUPPORT
69 oid objid_enterprisetrap[] = { NOTIFICATION_MIB };
71 oid trap_version_id[] = { SYSTEM_MIB };
72 int enterprisetrap_len;
73 int trap_version_id_len;
75 #define SNMPV2_TRAPS_PREFIX SNMP_OID_SNMPMODULES,1,1,5
76 oid cold_start_oid[] = { SNMPV2_TRAPS_PREFIX, 1 }; /* SNMPv2-MIB */
77 oid warm_start_oid[] = { SNMPV2_TRAPS_PREFIX, 2 }; /* SNMPv2-MIB */
78 oid link_down_oid[] = { SNMPV2_TRAPS_PREFIX, 3 }; /* IF-MIB */
79 oid link_up_oid[] = { SNMPV2_TRAPS_PREFIX, 4 }; /* IF-MIB */
80 oid auth_fail_oid[] = { SNMPV2_TRAPS_PREFIX, 5 }; /* SNMPv2-MIB */
81 oid egp_xxx_oid[] = { SNMPV2_TRAPS_PREFIX, 99 }; /* ??? */
83 #define SNMPV2_TRAP_OBJS_PREFIX SNMP_OID_SNMPMODULES,1,1,4
84 oid snmptrap_oid[] = { SNMPV2_TRAP_OBJS_PREFIX, 1, 0 };
85 oid snmptrapenterprise_oid[] =
86 { SNMPV2_TRAP_OBJS_PREFIX, 3, 0 };
87 oid sysuptime_oid[] = { SNMP_OID_MIB2, 1, 3, 0 };
88 size_t snmptrap_oid_len;
89 size_t snmptrapenterprise_oid_len;
90 size_t sysuptime_oid_len;
93 #define SNMP_AUTHENTICATED_TRAPS_ENABLED 1
94 #define SNMP_AUTHENTICATED_TRAPS_DISABLED 2
96 int snmp_enableauthentraps = SNMP_AUTHENTICATED_TRAPS_DISABLED;
97 int snmp_enableauthentrapsset = 0;
98 char *snmp_trapcommunity = NULL;
104 * static int create_v1_trap_session (const char *, u_short, const char *);
105 * static int create_v2_trap_session (const char *, u_short, const char *);
106 * static int create_v2_inform_session (const char *, u_short, const char *);
107 * static void free_trap_session (struct trap_sink *sp);
108 * static void send_v1_trap (netsnmp_session *, int, int);
109 * static void send_v2_trap (netsnmp_session *, int, int, int);
115 * Trap session handling
122 #ifdef BRCM_SNMP_MIB_SUPPORT
123 enterprisetrap_len = OID_LENGTH(objid_enterprisetrap);
125 trap_version_id_len = OID_LENGTH(trap_version_id);
126 snmptrap_oid_len = OID_LENGTH(snmptrap_oid);
127 #ifdef BRCM_SNMP_MIB_SUPPORT
128 snmptrapenterprise_oid_len = OID_LENGTH(snmptrapenterprise_oid);
130 sysuptime_oid_len = OID_LENGTH(sysuptime_oid);
134 free_trap_session(struct trap_sink *sp)
136 snmp_close(sp->sesp);
141 add_trap_session(netsnmp_session * ss, int pdutype, int confirm,
144 if (snmp_callback_available(SNMP_CALLBACK_APPLICATION,
145 SNMPD_CALLBACK_REGISTER_NOTIFICATIONS) ==
148 * something else wants to handle notification registrations
150 struct agent_add_trap_args args;
151 DEBUGMSGTL(("trap", "adding callback trap sink\n"));
153 args.confirm = confirm;
154 snmp_call_callbacks(SNMP_CALLBACK_APPLICATION,
155 SNMPD_CALLBACK_REGISTER_NOTIFICATIONS,
159 * no other support exists, handle it ourselves.
161 struct trap_sink *new_sink;
163 DEBUGMSGTL(("trap", "adding internal trap sink\n"));
164 new_sink = (struct trap_sink *) malloc(sizeof(*new_sink));
165 if (new_sink == NULL)
169 new_sink->pdutype = pdutype;
170 new_sink->version = version;
171 new_sink->next = sinks;
178 remove_trap_session(netsnmp_session * ss)
180 struct trap_sink *sp = sinks, *prev = 0;
183 if (sp->sesp == ss) {
185 prev->next = sp->next;
190 * I don't believe you *really* want to close the session here;
191 * it may still be in use for other purposes. In particular this
192 * is awkward for AgentX, since we want to call this function
193 * from the session's callback. Let's just free the trapsink
194 * data structure. [jbpn]
197 * free_trap_session(sp);
209 create_trap_session(char *sink, u_short sinkport,
210 char *com, int version, int pdutype)
212 netsnmp_session session, *sesp;
213 char *peername = NULL;
215 if ((peername = malloc(strlen(sink) + 4 + 32)) == NULL) {
218 snprintf(peername, strlen(sink) + 4 + 32, "udp:%s:%hu", sink,
222 memset(&session, 0, sizeof(netsnmp_session));
223 session.peername = peername;
224 session.version = version;
226 session.community = (u_char *) com;
227 session.community_len = strlen(com);
229 sesp = snmp_open(&session);
233 return add_trap_session(sesp, pdutype,
234 (pdutype == SNMP_MSG_INFORM), version);
238 * diagnose snmp_open errors with the input netsnmp_session pointer
240 snmp_sess_perror("snmpd: create_trap_session", &session);
245 create_v1_trap_session(char *sink, u_short sinkport, char *com)
247 return create_trap_session(sink, sinkport, com,
248 SNMP_VERSION_1, SNMP_MSG_TRAP);
252 create_v2_trap_session(char *sink, u_short sinkport, char *com)
254 return create_trap_session(sink, sinkport, com,
255 SNMP_VERSION_2c, SNMP_MSG_TRAP2);
258 #ifdef BRCM_SNMP_CONFIG_SUPPORT
260 create_v2_inform_session(char *sink, u_short sinkport, char *com)
262 return create_trap_session(sink, sinkport, com,
263 SNMP_VERSION_2c, SNMP_MSG_INFORM);
265 #endif /* BRCM_SNMP_CONFIG_SUPPORT */
268 snmpd_free_trapsinks(void)
270 struct trap_sink *sp = sinks;
273 free_trap_session(sp);
285 convert_v2_to_v1(netsnmp_variable_list * vars, netsnmp_pdu *template_pdu)
287 netsnmp_variable_list *v, *trap_v = NULL, *ent_v = NULL;
288 oid trap_prefix[] = { SNMPV2_TRAPS_PREFIX };
291 for (v = vars; v; v = v->next_variable) {
292 if (netsnmp_oid_equals(v->name, v->name_length,
293 snmptrap_oid, OID_LENGTH(snmptrap_oid)) == 0)
295 if (netsnmp_oid_equals(v->name, v->name_length,
296 snmptrapenterprise_oid,
297 OID_LENGTH(snmptrapenterprise_oid)) == 0)
302 return; /* Can't find v2 snmpTrapOID varbind */
305 * Is this a 'standard' trap?
306 * Or at least, does it have the correct prefix?
308 if (netsnmp_oid_equals(trap_v->val.objid, OID_LENGTH(trap_prefix),
309 trap_prefix, OID_LENGTH(trap_prefix)) == 0) {
310 template_pdu->trap_type =
311 trap_v->val.objid[OID_LENGTH(trap_prefix)] - 1;
312 template_pdu->specific_type = 0;
314 len = trap_v->val_len / sizeof(oid);
315 template_pdu->trap_type = 6; /* enterprise specific */
316 template_pdu->specific_type = trap_v->val.objid[len - 1];
321 * Extract the appropriate enterprise value from 'ent_v'
322 * Remove uptime/trapOID varbinds from 'vars' list
328 send_enterprise_trap_vars(int trap,
330 oid * enterprise, int enterprise_length,
331 netsnmp_variable_list * vars)
333 netsnmp_variable_list uptime_var, snmptrap_var, enterprise_var;
334 netsnmp_variable_list *v2_vars, *last_var = NULL;
335 netsnmp_pdu *template_pdu;
337 in_addr_t *pdu_in_addr_t;
338 struct trap_sink *sink;
339 #ifdef BRCM_SNMP_MIB_SUPPORT
340 oid temp_oid[MAX_OID_LEN];
343 * Initialise SNMPv2 required variables
345 uptime = netsnmp_get_agent_uptime();
346 memset(&uptime_var, 0, sizeof(netsnmp_variable_list));
347 snmp_set_var_objid(&uptime_var, sysuptime_oid,
348 OID_LENGTH(sysuptime_oid));
349 snmp_set_var_value(&uptime_var, (u_char *) & uptime, sizeof(uptime));
350 uptime_var.type = ASN_TIMETICKS;
351 uptime_var.next_variable = &snmptrap_var;
353 memset(&snmptrap_var, 0, sizeof(netsnmp_variable_list));
354 snmp_set_var_objid(&snmptrap_var, snmptrap_oid,
355 OID_LENGTH(snmptrap_oid));
357 * value set later ....
359 snmptrap_var.type = ASN_OBJECT_ID;
361 snmptrap_var.next_variable = vars;
363 snmptrap_var.next_variable = &enterprise_var;
366 * find end of provided varbind list,
367 * ready to append the enterprise info if necessary
370 while (last_var && last_var->next_variable)
371 last_var = last_var->next_variable;
373 memset(&enterprise_var, 0, sizeof(netsnmp_variable_list));
374 snmp_set_var_objid(&enterprise_var,
375 snmptrapenterprise_oid,
376 OID_LENGTH(snmptrapenterprise_oid));
377 snmp_set_var_value(&enterprise_var, (u_char *) enterprise,
378 enterprise_length * sizeof(oid));
379 enterprise_var.type = ASN_OBJECT_ID;
380 enterprise_var.next_variable = NULL;
382 v2_vars = &uptime_var;
385 * Create a template PDU, ready for sending
387 template_pdu = snmp_pdu_create(SNMP_MSG_TRAP);
388 if (template_pdu == NULL) {
390 * Free memory if value stored dynamically
392 snmp_set_var_value(&enterprise_var, NULL, 0);
395 template_pdu->trap_type = trap;
396 template_pdu->specific_type = specific;
397 if (snmp_clone_mem((void **) &template_pdu->enterprise,
398 enterprise, enterprise_length * sizeof(oid))) {
399 snmp_free_pdu(template_pdu);
400 snmp_set_var_value(&enterprise_var, NULL, 0);
403 template_pdu->enterprise_length = enterprise_length;
404 template_pdu->flags |= UCD_MSG_FLAG_FORCE_PDU_COPY;
406 pdu_in_addr_t = (in_addr_t *) template_pdu->agent_addr;
407 *pdu_in_addr_t = get_myaddr();
408 template_pdu->time = uptime;
411 * Now use the parameters to determine
412 * which v2 variables are needed,
413 * and what values they should take.
418 * Check to see whether the variables provided
419 * are sufficient for SNMPv2 notifications
421 if (vars && netsnmp_oid_equals(vars->name, vars->name_length,
423 OID_LENGTH(sysuptime_oid)) == 0)
425 else if (vars && netsnmp_oid_equals(vars->name, vars->name_length,
427 OID_LENGTH(snmptrap_oid)) == 0)
428 uptime_var.next_variable = vars;
431 * Hmmm... we don't seem to have a value - oops!
433 snmptrap_var.next_variable = vars;
435 last_var = NULL; /* Don't need enterprise info */
436 convert_v2_to_v1(vars, template_pdu);
440 * "Standard" SNMPv1 traps
443 case SNMP_TRAP_COLDSTART:
444 snmp_set_var_value(&snmptrap_var,
445 (u_char *) cold_start_oid,
446 sizeof(cold_start_oid));
448 case SNMP_TRAP_WARMSTART:
449 snmp_set_var_value(&snmptrap_var,
450 (u_char *) warm_start_oid,
451 sizeof(warm_start_oid));
453 case SNMP_TRAP_LINKDOWN:
454 snmp_set_var_value(&snmptrap_var,
455 (u_char *) link_down_oid,
456 sizeof(link_down_oid));
458 case SNMP_TRAP_LINKUP:
459 snmp_set_var_value(&snmptrap_var,
460 (u_char *) link_up_oid, sizeof(link_up_oid));
462 case SNMP_TRAP_AUTHFAIL:
463 if (snmp_enableauthentraps == SNMP_AUTHENTICATED_TRAPS_DISABLED) {
464 snmp_free_pdu(template_pdu);
465 snmp_set_var_value(&enterprise_var, NULL, 0);
468 snmp_set_var_value(&snmptrap_var,
469 (u_char *) auth_fail_oid,
470 sizeof(auth_fail_oid));
472 case SNMP_TRAP_EGPNEIGHBORLOSS:
473 snmp_set_var_value(&snmptrap_var,
474 (u_char *) egp_xxx_oid, sizeof(egp_xxx_oid));
477 #ifdef BRCM_SNMP_MIB_SUPPORT
478 case SNMP_TRAP_ENTERPRISESPECIFIC:
480 (char *) enterprise, (enterprise_length) * sizeof(oid));
481 temp_oid[enterprise_length] = 0;
482 temp_oid[enterprise_length + 1] = specific;
483 snmp_set_var_value(&snmptrap_var,
485 (enterprise_length + 2) * sizeof(oid));
486 snmptrap_var.next_variable = vars;
487 last_var = NULL; /* Don't need version info */
489 #endif /* BRCM_SNMP_MIB_SUPPORT */
494 * Now loop through the list of trap sinks,
495 * sending an appropriately formatted PDU to each
497 for (sink = sinks; sink; sink = sink->next) {
498 if (sink->version == SNMP_VERSION_1 && trap == -1)
499 continue; /* Skip v1 sinks for v2 only traps */
500 template_pdu->command = sink->pdutype;
502 if (sink->version != SNMP_VERSION_1) {
503 template_pdu->variables = v2_vars;
505 last_var->next_variable = &enterprise_var;
507 template_pdu->variables = vars;
509 send_trap_to_sess(sink->sesp, template_pdu);
511 if (sink->version != SNMP_VERSION_1 && last_var)
512 last_var->next_variable = NULL;
516 * send stuff to registered callbacks
521 template_pdu->variables = v2_vars;
523 last_var->next_variable = &enterprise_var;
525 snmp_call_callbacks(SNMP_CALLBACK_APPLICATION,
526 SNMPD_CALLBACK_SEND_TRAP2, template_pdu);
529 last_var->next_variable = NULL;
534 template_pdu->command = SNMP_MSG_TRAP;
535 template_pdu->variables = vars;
537 snmp_call_callbacks(SNMP_CALLBACK_APPLICATION,
538 SNMPD_CALLBACK_SEND_TRAP1, template_pdu);
541 * Free memory if values stored dynamically
543 snmp_set_var_value(&enterprise_var, NULL, 0);
544 snmp_set_var_value(&snmptrap_var, NULL, 0);
546 * Ensure we don't free anything we shouldn't
549 last_var->next_variable = NULL;
550 template_pdu->variables = NULL;
551 snmp_free_pdu(template_pdu);
555 * send_trap_to_sess: sends a trap to a session but assumes that the
556 * pdu is constructed correctly for the session type.
559 send_trap_to_sess(netsnmp_session * sess, netsnmp_pdu *template_pdu)
563 if (!sess || !template_pdu)
566 DEBUGMSGTL(("trap", "sending trap type=%d, version=%d\n",
567 template_pdu->command, sess->version));
569 if (sess->version == SNMP_VERSION_1 &&
570 (template_pdu->command == SNMP_MSG_TRAP2 ||
571 template_pdu->command == SNMP_MSG_INFORM))
572 return; /* Skip v1 sinks for v2 only traps */
573 template_pdu->version = sess->version;
574 pdu = snmp_clone_pdu(template_pdu);
575 pdu->sessid = sess->sessid; /* AgentX only ? */
576 if (snmp_send(sess, pdu) == 0) {
577 snmp_sess_perror("snmpd: send_trap", sess);
580 snmp_increment_statistic(STAT_SNMPOUTTRAPS);
581 snmp_increment_statistic(STAT_SNMPOUTPKTS);
586 send_trap_vars(int trap, int specific, netsnmp_variable_list * vars)
588 #ifdef BRCM_SNMP_MIB_SUPPORT
589 if (trap == SNMP_TRAP_ENTERPRISESPECIFIC)
590 send_enterprise_trap_vars(trap, specific, objid_enterprisetrap,
591 OID_LENGTH(objid_enterprisetrap), vars);
594 send_enterprise_trap_vars(trap, specific, trap_version_id,
595 OID_LENGTH(trap_version_id), vars);
599 send_easy_trap(int trap, int specific)
601 send_trap_vars(trap, specific, NULL);
605 send_v2trap(netsnmp_variable_list * vars)
607 send_trap_vars(-1, -1, vars);
611 send_trap_pdu(netsnmp_pdu *pdu)
613 send_trap_vars(-1, -1, pdu->variables);
617 #ifdef BRCM_SNMP_CONFIG_SUPPORT
620 * Config file handling
625 snmpd_parse_config_authtrap(const char *token, char *cptr)
631 if (strcmp(cptr, "enable") == 0) {
632 i = SNMP_AUTHENTICATED_TRAPS_ENABLED;
633 } else if (strcmp(cptr, "disable") == 0) {
634 i = SNMP_AUTHENTICATED_TRAPS_DISABLED;
637 if (i < 1 || i > 2) {
638 config_perror("authtrapenable must be 1 or 2");
640 if (strcmp(token, "pauthtrapenable") == 0) {
641 if (snmp_enableauthentrapsset < 0) {
643 * This is bogus (and shouldn't happen anyway) -- the value
644 * of snmpEnableAuthenTraps.0 is already configured
647 snmp_log(LOG_WARNING,
648 "ignoring attempted override of read-only snmpEnableAuthenTraps.0\n");
651 snmp_enableauthentrapsset++;
654 if (snmp_enableauthentrapsset > 0) {
656 * This is bogus (and shouldn't happen anyway) -- we already
657 * read a persistent value of snmpEnableAuthenTraps.0, which
658 * we should ignore in favour of this one.
660 snmp_log(LOG_WARNING,
661 "ignoring attempted override of read-only snmpEnableAuthenTraps.0\n");
663 * Fall through and copy in this value.
666 snmp_enableauthentrapsset = -1;
668 snmp_enableauthentraps = i;
671 #endif /* BRCM_SNMP_CONFIG_SUPPORT */
674 snmpd_parse_config_trapsink(const char *token, char *cptr)
677 char *sp, *cp, *pp = NULL;
680 if (!snmp_trapcommunity)
681 snmp_trapcommunity = strdup("public");
682 sp = strtok(cptr, " \t\n");
683 cp = strtok(NULL, " \t\n");
685 pp = strtok(NULL, " \t\n");
688 if ((sinkport < 1) || (sinkport > 0xffff)) {
689 config_perror("trapsink port out of range");
690 sinkport = SNMP_TRAP_PORT;
693 sinkport = SNMP_TRAP_PORT;
695 if (create_v1_trap_session(sp, sinkport,
696 cp ? cp : snmp_trapcommunity) == 0) {
697 snprintf(tmpbuf, sizeof(tmpbuf), "cannot create trapsink: %s", cptr);
698 tmpbuf[sizeof(tmpbuf)-1] = '\0';
699 config_perror(tmpbuf);
705 snmpd_parse_config_trap2sink(const char *word, char *cptr)
708 char *sp, *cp, *pp = NULL;
711 if (!snmp_trapcommunity)
712 snmp_trapcommunity = strdup("public");
713 sp = strtok(cptr, " \t\n");
714 cp = strtok(NULL, " \t\n");
716 pp = strtok(NULL, " \t\n");
719 if ((sinkport < 1) || (sinkport > 0xffff)) {
720 config_perror("trapsink port out of range");
721 sinkport = SNMP_TRAP_PORT;
724 sinkport = SNMP_TRAP_PORT;
726 if (create_v2_trap_session(sp, sinkport,
727 cp ? cp : snmp_trapcommunity) == 0) {
728 snprintf(tmpbuf, sizeof(tmpbuf), "cannot create trap2sink: %s", cptr);
729 tmpbuf[sizeof(tmpbuf)-1] = '\0';
730 config_perror(tmpbuf);
734 #ifdef BRCM_SNMP_CONFIG_SUPPORT
736 snmpd_parse_config_informsink(const char *word, char *cptr)
739 char *sp, *cp, *pp = NULL;
742 if (!snmp_trapcommunity)
743 snmp_trapcommunity = strdup("public");
744 sp = strtok(cptr, " \t\n");
745 cp = strtok(NULL, " \t\n");
747 pp = strtok(NULL, " \t\n");
750 if ((sinkport < 1) || (sinkport > 0xffff)) {
751 config_perror("trapsink port out of range");
752 sinkport = SNMP_TRAP_PORT;
755 sinkport = SNMP_TRAP_PORT;
757 if (create_v2_inform_session(sp, sinkport,
758 cp ? cp : snmp_trapcommunity) == 0) {
759 snprintf(tmpbuf, sizeof(tmpbuf), "cannot create informsink: %s", cptr);
760 tmpbuf[sizeof(tmpbuf)-1] = '\0';
761 config_perror(tmpbuf);
766 * this must be standardized somewhere, right?
773 trapOptProc(int argc, char *const *argv, int opt)
780 traptype = SNMP_MSG_INFORM;
783 config_perror("unknown argument passed to -C");
792 snmpd_parse_config_trapsess(const char *word, char *cptr)
794 char *argv[MAX_ARGS], *cp = cptr, tmp[SPRINT_MAX_LEN];
796 netsnmp_session session, *ss;
799 * inform or trap? default to trap
801 traptype = SNMP_MSG_TRAP2;
804 * create the argv[] like array
806 argv[0] = strdup("snmpd-trapsess"); /* bogus entry for getopt() */
807 for (argn = 1; cp && argn < MAX_ARGS; argn++) {
808 cp = copy_nword(cp, tmp, SPRINT_MAX_LEN);
809 argv[argn] = strdup(tmp);
812 arg = snmp_parse_args(argn, argv, &session, "C:", trapOptProc);
813 ss = snmp_open(&session);
815 for (; argn > 0; argn--) {
816 free(argv[argn - 1]);
821 ("snmpd: failed to parse this line or the remote trap receiver is down. Possible cause:");
822 snmp_sess_perror("snmpd: snmpd_parse_config_trapsess()", &session);
826 if (ss->version == SNMP_VERSION_1) {
827 add_trap_session(ss, SNMP_MSG_TRAP, 0, SNMP_VERSION_1);
829 add_trap_session(ss, traptype, (traptype == SNMP_MSG_INFORM),
833 #endif /* BRCM_SNMP_CONFIG_SUPPORT */
836 snmpd_parse_config_trapcommunity(const char *word, char *cptr)
838 if (snmp_trapcommunity != NULL) {
839 free(snmp_trapcommunity);
841 snmp_trapcommunity = (char *) malloc(strlen(cptr) + 1);
842 if (snmp_trapcommunity != NULL) {
843 copy_nword(cptr, snmp_trapcommunity, strlen(cptr) + 1);
848 snmpd_free_trapcommunity(void)
850 if (snmp_trapcommunity) {
851 free(snmp_trapcommunity);
852 snmp_trapcommunity = NULL;