/* $OpenBSD: trap.c,v 1.22 2014/04/14 12:55:10 blambert Exp $ */ /* * Copyright (c) 2008 Reyk Floeter * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "snmpd.h" #include "mib.h" extern struct snmpd *env; void trap_init(void) { struct ber_oid trapoid = OID(MIB_coldStart); /* * Send a coldStart to notify that the daemon has been * started and re-initialized. */ trap_send(&trapoid, NULL); } int trap_agentx(struct agentx_handle *h, struct agentx_pdu *pdu, int *idx, char **varcpy, int *vcpylen) { struct agentx_varbind_hdr vbhdr; struct ber_oid o, oid; struct ber_oid uptime = OID(MIB_sysUpTime); struct ber_oid trapoid = OID(MIB_snmpTrapOID); u_int32_t d; u_int64_t l; char *str; int slen; struct ber_element *a, *ber, *varbind; int x = 0, state = 0; int ret = AGENTX_ERR_NONE; int seensysuptime, seentrapoid; size_t len = 0; pid_t pid = -1; char *v = NULL; *varcpy = NULL; ber = varbind = NULL; seensysuptime = seentrapoid = 0; if (pdu->hdr->flags & AGENTX_NON_DEFAULT_CONTEXT) { ret = AGENTX_ERR_UNSUPPORTED_CONTEXT; goto done; } if ((v = malloc(pdu->hdr->length)) == NULL || snmp_agentx_copy_raw(pdu, v, pdu->hdr->length) == -1) { ret = AGENTX_ERR_PROCESSING_ERROR; goto done; } while (pdu->datalen > sizeof(struct agentx_hdr)) { x++; if (snmp_agentx_read_vbhdr(pdu, &vbhdr) == -1 || snmp_agentx_read_oid(pdu, (struct snmp_oid *)&oid) == -1) { ret = AGENTX_ERR_PARSE_ERROR; goto done; } if (state < 2) { if (state == 0 && ber_oid_cmp(&oid, &uptime) == 0) { if (snmp_agentx_read_int(pdu, &d) == -1) { ret = AGENTX_ERR_PARSE_ERROR; goto done; } state = 1; continue; } else if (ber_oid_cmp(&oid, &trapoid) == 0) { if (snmp_agentx_read_oid(pdu, (struct snmp_oid *)&o) == -1) { ret = AGENTX_ERR_PARSE_ERROR; goto done; } state = 2; continue; } else { ret = AGENTX_ERR_PROCESSING_ERROR; goto done; } } ber = ber_add_sequence(ber); if (varbind == NULL) varbind = ber; a = ber_add_oid(ber, &oid); switch (vbhdr.type) { case AGENTX_NO_SUCH_OBJECT: case AGENTX_NO_SUCH_INSTANCE: case AGENTX_END_OF_MIB_VIEW: case AGENTX_NULL: a = ber_add_null(a); break; case AGENTX_IP_ADDRESS: case AGENTX_OPAQUE: case AGENTX_OCTET_STRING: str = snmp_agentx_read_octetstr(pdu, &slen); if (str == NULL) { ret = AGENTX_ERR_PARSE_ERROR; goto done; } a = ber_add_nstring(a, str, slen); break; case AGENTX_OBJECT_IDENTIFIER: if (snmp_agentx_read_oid(pdu, (struct snmp_oid *)&oid) == -1) { ret = AGENTX_ERR_PARSE_ERROR; goto done; } a = ber_add_oid(a, &oid); break; case AGENTX_INTEGER: case AGENTX_COUNTER32: case AGENTX_GAUGE32: case AGENTX_TIME_TICKS: if (snmp_agentx_read_int(pdu, &d) == -1) { ret = AGENTX_ERR_PARSE_ERROR; goto done; } a = ber_add_integer(a, d); break; case AGENTX_COUNTER64: if (snmp_agentx_read_int64(pdu, &l) == -1) { ret = AGENTX_ERR_PARSE_ERROR; goto done; } a = ber_add_integer(a, l); break; default: log_debug("unknown data type '%i'", vbhdr.type); ret = AGENTX_ERR_PARSE_ERROR; goto done; } /* AgentX types correspond to BER types */ switch (vbhdr.type) { case BER_TYPE_INTEGER: case BER_TYPE_BITSTRING: case BER_TYPE_OCTETSTRING: case BER_TYPE_NULL: case BER_TYPE_OBJECT: /* universal types */ break; default: /* application-specific types */ ber_set_header(a, BER_CLASS_APPLICATION, vbhdr.type); break; } } if (varbind != NULL) len = ber_calc_len(varbind); log_debug("trap_agentx: from pid %u len %d elements %d", pid, len, x); trap_send(&o, varbind); *varcpy = v; *vcpylen = pdu->hdr->length; return (AGENTX_ERR_NONE); done: if (varbind != NULL) ber_free_elements(varbind); if (v) free(v); *idx = x; return (ret); } int trap_send(struct ber_oid *oid, struct ber_element *elm) { int ret = 0, s; struct address *tr; struct ber_element *root, *b, *c, *trap; struct ber ber; char *cmn; ssize_t len; u_int8_t *ptr; struct ber_oid uptime = OID(MIB_sysUpTime); struct ber_oid trapoid = OID(MIB_snmpTrapOID); char ostr[SNMP_MAX_OID_STRLEN]; struct oid oa, ob; if (TAILQ_EMPTY(&env->sc_trapreceivers)) return (0); smi_scalar_oidlen(&uptime); smi_scalar_oidlen(&trapoid); smi_scalar_oidlen(oid); smi_oid2string(oid, ostr, sizeof(ostr), 0); log_debug("trap_send: oid %s", ostr); /* Setup OIDs to compare against the trap receiver MIB */ bzero(&oa, sizeof(oa)); bcopy(oid->bo_id, &oa.o_oid, sizeof(oa.o_oid)); oa.o_oidlen = oid->bo_n; bzero(&ob, sizeof(ob)); ob.o_flags = OID_TABLE; /* Add mandatory varbind elements */ trap = ber_add_sequence(NULL); c = ber_printf_elements(trap, "{Odt}{OO}", &uptime, smi_getticks(), BER_CLASS_APPLICATION, SNMP_T_TIMETICKS, &trapoid, oid); if (elm != NULL) ber_link_elements(c, elm); bzero(&ber, sizeof(ber)); ber.fd = -1; TAILQ_FOREACH(tr, &env->sc_trapreceivers, entry) { if (tr->sa_oid != NULL && tr->sa_oid->bo_n) { /* The trap receiver may want only a specified MIB */ bcopy(&tr->sa_oid->bo_id, &ob.o_oid, sizeof(ob.o_oid)); ob.o_oidlen = tr->sa_oid->bo_n; if (smi_oid_cmp(&oa, &ob) != 0) continue; } if ((s = snmpd_socket_af(&tr->ss, htons(tr->port))) == -1) { ret = -1; goto done; } cmn = tr->sa_community != NULL ? tr->sa_community : env->sc_trcommunity; /* SNMP header */ root = ber_add_sequence(NULL); b = ber_printf_elements(root, "ds{tddd", SNMP_V2, cmn, BER_CLASS_CONTEXT, SNMP_C_TRAPV2, arc4random(), 0, 0); ber_link_elements(b, trap); #ifdef DEBUG smi_debug_elements(root); #endif len = ber_write_elements(&ber, root); if (ber_get_writebuf(&ber, (void *)&ptr) > 0 && sendto(s, ptr, len, 0, (struct sockaddr *)&tr->ss, tr->ss.ss_len) != -1) { env->sc_stats.snmp_outpkts++; ret++; } close(s); ber_unlink_elements(b); ber_free_elements(root); } done: ber_free_elements(trap); ber_free(&ber); return (ret); }