/* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Begemot: bsnmp/snmpd/trap.c,v 1.8 2004/08/06 08:47:17 brandt Exp $ * * TrapSinkTable */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "snmpmod.h" #include "snmpd.h" #include "tree.h" #include "oid.h" struct trapsink_list trapsink_list = TAILQ_HEAD_INITIALIZER(trapsink_list); static const struct asn_oid oid_begemotTrapSinkTable = OIDX_begemotTrapSinkTable; static const struct asn_oid oid_sysUpTime = OIDX_sysUpTime; static const struct asn_oid oid_snmpTrapOID = OIDX_snmpTrapOID; struct trapsink_dep { struct snmp_dependency dep; u_int set; u_int status; u_char comm[SNMP_COMMUNITY_MAXLEN + 1]; u_int version; u_int rb; u_int rb_status; u_int rb_version; u_char rb_comm[SNMP_COMMUNITY_MAXLEN + 1]; }; enum { TDEP_STATUS = 0x0001, TDEP_COMM = 0x0002, TDEP_VERSION = 0x0004, TDEP_CREATE = 0x0001, TDEP_MODIFY = 0x0002, TDEP_DESTROY = 0x0004, }; static int trapsink_create(struct trapsink_dep *tdep) { struct trapsink *t; struct sockaddr_in sa; if ((t = malloc(sizeof(*t))) == NULL) return (SNMP_ERR_RES_UNAVAIL); t->index = tdep->dep.idx; t->status = TRAPSINK_NOT_READY; t->comm[0] = '\0'; t->version = TRAPSINK_V2; if ((t->socket = socket(PF_INET, SOCK_DGRAM, 0)) == -1) { syslog(LOG_ERR, "socket(UDP): %m"); free(t); return (SNMP_ERR_RES_UNAVAIL); } (void)shutdown(t->socket, SHUT_RD); sa.sin_len = sizeof(sa); sa.sin_family = AF_INET; sa.sin_addr.s_addr = htonl((t->index.subs[0] << 24) | (t->index.subs[1] << 16) | (t->index.subs[2] << 8) | (t->index.subs[3] << 0)); sa.sin_port = htons(t->index.subs[4]); if (connect(t->socket, (struct sockaddr *)&sa, sa.sin_len) == -1) { syslog(LOG_ERR, "connect(%s,%u): %m", inet_ntoa(sa.sin_addr), ntohl(sa.sin_port)); (void)close(t->socket); free(t); return (SNMP_ERR_GENERR); } if (tdep->set & TDEP_VERSION) t->version = tdep->version; if (tdep->set & TDEP_COMM) strcpy(t->comm, tdep->comm); if (t->comm[0] != '\0') t->status = TRAPSINK_NOT_IN_SERVICE; /* look whether we should activate */ if (tdep->status == 4) { if (t->status == TRAPSINK_NOT_READY) { if (t->socket != -1) (void)close(t->socket); free(t); return (SNMP_ERR_INCONS_VALUE); } t->status = TRAPSINK_ACTIVE; } INSERT_OBJECT_OID(t, &trapsink_list); tdep->rb |= TDEP_CREATE; return (SNMP_ERR_NOERROR); } static void trapsink_free(struct trapsink *t) { TAILQ_REMOVE(&trapsink_list, t, link); if (t->socket != -1) (void)close(t->socket); free(t); } static int trapsink_modify(struct trapsink *t, struct trapsink_dep *tdep) { tdep->rb_status = t->status; tdep->rb_version = t->version; strcpy(tdep->rb_comm, t->comm); if (tdep->set & TDEP_STATUS) { /* if we are active and should move to not_in_service do * this first */ if (tdep->status == 2 && tdep->rb_status == TRAPSINK_ACTIVE) { t->status = TRAPSINK_NOT_IN_SERVICE; tdep->rb |= TDEP_MODIFY; } } if (tdep->set & TDEP_VERSION) t->version = tdep->version; if (tdep->set & TDEP_COMM) strcpy(t->comm, tdep->comm); if (tdep->set & TDEP_STATUS) { /* if we were inactive and should go active - do this now */ if (tdep->status == 1 && tdep->rb_status != TRAPSINK_ACTIVE) { if (t->comm[0] == '\0') { t->status = tdep->rb_status; t->version = tdep->rb_version; strcpy(t->comm, tdep->rb_comm); return (SNMP_ERR_INCONS_VALUE); } t->status = TRAPSINK_ACTIVE; tdep->rb |= TDEP_MODIFY; } } return (SNMP_ERR_NOERROR); } static int trapsink_unmodify(struct trapsink *t, struct trapsink_dep *tdep) { if (tdep->set & TDEP_STATUS) t->status = tdep->rb_status; if (tdep->set & TDEP_VERSION) t->version = tdep->rb_version; if (tdep->set & TDEP_COMM) strcpy(t->comm, tdep->rb_comm); return (SNMP_ERR_NOERROR); } static int trapsink_destroy(struct snmp_context *ctx __unused, struct trapsink *t, struct trapsink_dep *tdep) { t->status = TRAPSINK_DESTROY; tdep->rb_status = t->status; tdep->rb |= TDEP_DESTROY; return (SNMP_ERR_NOERROR); } static int trapsink_undestroy(struct trapsink *t, struct trapsink_dep *tdep) { t->status = tdep->rb_status; return (SNMP_ERR_NOERROR); } static int trapsink_dep(struct snmp_context *ctx, struct snmp_dependency *dep, enum snmp_depop op) { struct trapsink_dep *tdep = (struct trapsink_dep *)dep; struct trapsink *t; t = FIND_OBJECT_OID(&trapsink_list, &dep->idx, 0); switch (op) { case SNMP_DEPOP_COMMIT: if (tdep->set & TDEP_STATUS) { switch (tdep->status) { case 1: case 2: if (t == NULL) return (SNMP_ERR_INCONS_VALUE); return (trapsink_modify(t, tdep)); case 4: case 5: if (t != NULL) return (SNMP_ERR_INCONS_VALUE); return (trapsink_create(tdep)); case 6: if (t == NULL) return (SNMP_ERR_NOERROR); return (trapsink_destroy(ctx, t, tdep)); } } else if (tdep->set != 0) return (trapsink_modify(t, tdep)); return (SNMP_ERR_NOERROR); case SNMP_DEPOP_ROLLBACK: if (tdep->rb & TDEP_CREATE) { trapsink_free(t); return (SNMP_ERR_NOERROR); } if (tdep->rb & TDEP_MODIFY) return (trapsink_unmodify(t, tdep)); if(tdep->rb & TDEP_DESTROY) return (trapsink_undestroy(t, tdep)); return (SNMP_ERR_NOERROR); case SNMP_DEPOP_FINISH: if ((tdep->rb & TDEP_DESTROY) && t != NULL && ctx->code == SNMP_RET_OK) trapsink_free(t); return (SNMP_ERR_NOERROR); } abort(); } int op_trapsink(struct snmp_context *ctx, struct snmp_value *value, u_int sub, u_int iidx, enum snmp_op op) { struct trapsink *t; u_char ipa[4]; int32_t port; struct asn_oid idx; struct trapsink_dep *tdep; u_char *p; t = NULL; /* gcc */ switch (op) { case SNMP_OP_GETNEXT: if ((t = NEXT_OBJECT_OID(&trapsink_list, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); index_append(&value->var, sub, &t->index); break; case SNMP_OP_GET: if ((t = FIND_OBJECT_OID(&trapsink_list, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: if (index_decode(&value->var, sub, iidx, ipa, &port) || port == 0 || port > 65535) return (SNMP_ERR_NO_CREATION); t = FIND_OBJECT_OID(&trapsink_list, &value->var, sub); asn_slice_oid(&idx, &value->var, sub, value->var.len); tdep = (struct trapsink_dep *)snmp_dep_lookup(ctx, &oid_begemotTrapSinkTable, &idx, sizeof(*tdep), trapsink_dep); if (tdep == NULL) return (SNMP_ERR_RES_UNAVAIL); switch (value->var.subs[sub - 1]) { case LEAF_begemotTrapSinkStatus: if (tdep->set & TDEP_STATUS) return (SNMP_ERR_INCONS_VALUE); switch (value->v.integer) { case 1: case 2: if (t == NULL) return (SNMP_ERR_INCONS_VALUE); break; case 4: case 5: if (t != NULL) return (SNMP_ERR_INCONS_VALUE); break; case 6: break; default: return (SNMP_ERR_WRONG_VALUE); } tdep->status = value->v.integer; tdep->set |= TDEP_STATUS; return (SNMP_ERR_NOERROR); case LEAF_begemotTrapSinkComm: if (tdep->set & TDEP_COMM) return (SNMP_ERR_INCONS_VALUE); if (value->v.octetstring.len == 0 || value->v.octetstring.len > SNMP_COMMUNITY_MAXLEN) return (SNMP_ERR_WRONG_VALUE); for (p = value->v.octetstring.octets; p < value->v.octetstring.octets + value->v.octetstring.len; p++) { if (!isascii(*p) || !isprint(*p)) return (SNMP_ERR_WRONG_VALUE); } tdep->set |= TDEP_COMM; strncpy(tdep->comm, value->v.octetstring.octets, value->v.octetstring.len); tdep->comm[value->v.octetstring.len] = '\0'; return (SNMP_ERR_NOERROR); case LEAF_begemotTrapSinkVersion: if (tdep->set & TDEP_VERSION) return (SNMP_ERR_INCONS_VALUE); if (value->v.integer != TRAPSINK_V1 && value->v.integer != TRAPSINK_V2) return (SNMP_ERR_WRONG_VALUE); tdep->version = value->v.integer; tdep->set |= TDEP_VERSION; return (SNMP_ERR_NOERROR); } if (t == NULL) return (SNMP_ERR_INCONS_NAME); else return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); } switch (value->var.subs[sub - 1]) { case LEAF_begemotTrapSinkStatus: value->v.integer = t->status; break; case LEAF_begemotTrapSinkComm: return (string_get(value, t->comm, -1)); case LEAF_begemotTrapSinkVersion: value->v.integer = t->version; break; } return (SNMP_ERR_NOERROR); } void snmp_send_trap(const struct asn_oid *trap_oid, ...) { struct snmp_pdu pdu; struct trapsink *t; const struct snmp_value *v; va_list ap; u_char *sndbuf; size_t sndlen; ssize_t len; TAILQ_FOREACH(t, &trapsink_list, link) { if (t->status != TRAPSINK_ACTIVE) continue; memset(&pdu, 0, sizeof(pdu)); strcpy(pdu.community, t->comm); if (t->version == TRAPSINK_V1) { pdu.version = SNMP_V1; pdu.type = SNMP_PDU_TRAP; pdu.enterprise = systemg.object_id; memcpy(pdu.agent_addr, snmpd.trap1addr, 4); pdu.generic_trap = trap_oid->subs[trap_oid->len - 1] - 1; pdu.specific_trap = 0; pdu.time_stamp = get_ticks() - start_tick; pdu.nbindings = 0; } else { pdu.version = SNMP_V2c; pdu.type = SNMP_PDU_TRAP2; pdu.request_id = reqid_next(trap_reqid); pdu.error_index = 0; pdu.error_status = SNMP_ERR_NOERROR; pdu.bindings[0].var = oid_sysUpTime; pdu.bindings[0].var.subs[pdu.bindings[0].var.len++] = 0; pdu.bindings[0].syntax = SNMP_SYNTAX_TIMETICKS; pdu.bindings[0].v.uint32 = get_ticks() - start_tick; pdu.bindings[1].var = oid_snmpTrapOID; pdu.bindings[1].var.subs[pdu.bindings[1].var.len++] = 0; pdu.bindings[1].syntax = SNMP_SYNTAX_OID; pdu.bindings[1].v.oid = *trap_oid; pdu.nbindings = 2; } va_start(ap, trap_oid); while ((v = va_arg(ap, const struct snmp_value *)) != NULL) pdu.bindings[pdu.nbindings++] = *v; va_end(ap); if ((sndbuf = buf_alloc(1)) == NULL) { syslog(LOG_ERR, "trap send buffer: %m"); return; } snmp_output(&pdu, sndbuf, &sndlen, "TRAP"); if ((len = send(t->socket, sndbuf, sndlen, 0)) == -1) syslog(LOG_ERR, "send: %m"); else if ((size_t)len != sndlen) syslog(LOG_ERR, "send: short write %zu/%zu", sndlen, (size_t)len); free(sndbuf); } }