2 * Copyright (c) 2001-2003
3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
6 * Author: Harti Brandt <harti@freebsd.org>
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * $Begemot: bsnmp/snmpd/trap.c,v 1.9 2005/10/04 11:21:39 brandt_h Exp $
33 #include <sys/types.h>
34 #include <sys/sysctl.h>
44 #include <netinet/in.h>
45 #include <arpa/inet.h>
52 struct trapsink_list trapsink_list = TAILQ_HEAD_INITIALIZER(trapsink_list);
54 static const struct asn_oid oid_begemotTrapSinkTable =
55 OIDX_begemotTrapSinkTable;
56 static const struct asn_oid oid_sysUpTime = OIDX_sysUpTime;
57 static const struct asn_oid oid_snmpTrapOID = OIDX_snmpTrapOID;
60 struct snmp_dependency dep;
63 u_char comm[SNMP_COMMUNITY_MAXLEN + 1];
68 u_char rb_comm[SNMP_COMMUNITY_MAXLEN + 1];
73 TDEP_VERSION = 0x0004,
77 TDEP_DESTROY = 0x0004,
81 trapsink_create(struct trapsink_dep *tdep)
84 struct sockaddr_in sa;
86 if ((t = malloc(sizeof(*t))) == NULL)
87 return (SNMP_ERR_RES_UNAVAIL);
89 t->index = tdep->dep.idx;
90 t->status = TRAPSINK_NOT_READY;
92 t->version = TRAPSINK_V2;
94 if ((t->socket = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
95 syslog(LOG_ERR, "socket(UDP): %m");
97 return (SNMP_ERR_RES_UNAVAIL);
99 (void)shutdown(t->socket, SHUT_RD);
101 sa.sin_len = sizeof(sa);
102 sa.sin_family = AF_INET;
103 sa.sin_addr.s_addr = htonl((t->index.subs[0] << 24) |
104 (t->index.subs[1] << 16) | (t->index.subs[2] << 8) |
105 (t->index.subs[3] << 0));
106 sa.sin_port = htons(t->index.subs[4]);
108 if (connect(t->socket, (struct sockaddr *)&sa, sa.sin_len) == -1) {
109 syslog(LOG_ERR, "connect(%s,%u): %m",
110 inet_ntoa(sa.sin_addr), ntohs(sa.sin_port));
111 (void)close(t->socket);
113 return (SNMP_ERR_GENERR);
116 if (tdep->set & TDEP_VERSION)
117 t->version = tdep->version;
118 if (tdep->set & TDEP_COMM)
119 strcpy(t->comm, tdep->comm);
121 if (t->comm[0] != '\0')
122 t->status = TRAPSINK_NOT_IN_SERVICE;
124 /* look whether we should activate */
125 if (tdep->status == 4) {
126 if (t->status == TRAPSINK_NOT_READY) {
128 (void)close(t->socket);
130 return (SNMP_ERR_INCONS_VALUE);
132 t->status = TRAPSINK_ACTIVE;
135 INSERT_OBJECT_OID(t, &trapsink_list);
137 tdep->rb |= TDEP_CREATE;
139 return (SNMP_ERR_NOERROR);
143 trapsink_free(struct trapsink *t)
145 TAILQ_REMOVE(&trapsink_list, t, link);
147 (void)close(t->socket);
152 trapsink_modify(struct trapsink *t, struct trapsink_dep *tdep)
154 tdep->rb_status = t->status;
155 tdep->rb_version = t->version;
156 strcpy(tdep->rb_comm, t->comm);
158 if (tdep->set & TDEP_STATUS) {
159 /* if we are active and should move to not_in_service do
161 if (tdep->status == 2 && tdep->rb_status == TRAPSINK_ACTIVE) {
162 t->status = TRAPSINK_NOT_IN_SERVICE;
163 tdep->rb |= TDEP_MODIFY;
167 if (tdep->set & TDEP_VERSION)
168 t->version = tdep->version;
169 if (tdep->set & TDEP_COMM)
170 strcpy(t->comm, tdep->comm);
172 if (tdep->set & TDEP_STATUS) {
173 /* if we were inactive and should go active - do this now */
174 if (tdep->status == 1 && tdep->rb_status != TRAPSINK_ACTIVE) {
175 if (t->comm[0] == '\0') {
176 t->status = tdep->rb_status;
177 t->version = tdep->rb_version;
178 strcpy(t->comm, tdep->rb_comm);
179 return (SNMP_ERR_INCONS_VALUE);
181 t->status = TRAPSINK_ACTIVE;
182 tdep->rb |= TDEP_MODIFY;
185 return (SNMP_ERR_NOERROR);
189 trapsink_unmodify(struct trapsink *t, struct trapsink_dep *tdep)
191 if (tdep->set & TDEP_STATUS)
192 t->status = tdep->rb_status;
193 if (tdep->set & TDEP_VERSION)
194 t->version = tdep->rb_version;
195 if (tdep->set & TDEP_COMM)
196 strcpy(t->comm, tdep->rb_comm);
198 return (SNMP_ERR_NOERROR);
202 trapsink_destroy(struct snmp_context *ctx __unused, struct trapsink *t,
203 struct trapsink_dep *tdep)
205 t->status = TRAPSINK_DESTROY;
206 tdep->rb_status = t->status;
207 tdep->rb |= TDEP_DESTROY;
208 return (SNMP_ERR_NOERROR);
212 trapsink_undestroy(struct trapsink *t, struct trapsink_dep *tdep)
214 t->status = tdep->rb_status;
215 return (SNMP_ERR_NOERROR);
219 trapsink_dep(struct snmp_context *ctx, struct snmp_dependency *dep,
222 struct trapsink_dep *tdep = (struct trapsink_dep *)dep;
225 t = FIND_OBJECT_OID(&trapsink_list, &dep->idx, 0);
229 case SNMP_DEPOP_COMMIT:
230 if (tdep->set & TDEP_STATUS) {
231 switch (tdep->status) {
236 return (SNMP_ERR_INCONS_VALUE);
237 return (trapsink_modify(t, tdep));
242 return (SNMP_ERR_INCONS_VALUE);
243 return (trapsink_create(tdep));
247 return (SNMP_ERR_NOERROR);
248 return (trapsink_destroy(ctx, t, tdep));
250 } else if (tdep->set != 0)
251 return (trapsink_modify(t, tdep));
253 return (SNMP_ERR_NOERROR);
255 case SNMP_DEPOP_ROLLBACK:
256 if (tdep->rb & TDEP_CREATE) {
258 return (SNMP_ERR_NOERROR);
260 if (tdep->rb & TDEP_MODIFY)
261 return (trapsink_unmodify(t, tdep));
262 if(tdep->rb & TDEP_DESTROY)
263 return (trapsink_undestroy(t, tdep));
264 return (SNMP_ERR_NOERROR);
266 case SNMP_DEPOP_FINISH:
267 if ((tdep->rb & TDEP_DESTROY) && t != NULL &&
268 ctx->code == SNMP_RET_OK)
270 return (SNMP_ERR_NOERROR);
276 op_trapsink(struct snmp_context *ctx, struct snmp_value *value,
277 u_int sub, u_int iidx, enum snmp_op op)
283 struct trapsink_dep *tdep;
290 case SNMP_OP_GETNEXT:
291 if ((t = NEXT_OBJECT_OID(&trapsink_list, &value->var, sub)) == NULL)
292 return (SNMP_ERR_NOSUCHNAME);
293 index_append(&value->var, sub, &t->index);
297 if ((t = FIND_OBJECT_OID(&trapsink_list, &value->var, sub)) == NULL)
298 return (SNMP_ERR_NOSUCHNAME);
302 if (index_decode(&value->var, sub, iidx, ipa, &port) ||
303 port == 0 || port > 65535)
304 return (SNMP_ERR_NO_CREATION);
305 t = FIND_OBJECT_OID(&trapsink_list, &value->var, sub);
307 asn_slice_oid(&idx, &value->var, sub, value->var.len);
309 tdep = (struct trapsink_dep *)snmp_dep_lookup(ctx,
310 &oid_begemotTrapSinkTable, &idx,
311 sizeof(*tdep), trapsink_dep);
313 return (SNMP_ERR_RES_UNAVAIL);
315 switch (value->var.subs[sub - 1]) {
317 case LEAF_begemotTrapSinkStatus:
318 if (tdep->set & TDEP_STATUS)
319 return (SNMP_ERR_INCONS_VALUE);
320 switch (value->v.integer) {
325 return (SNMP_ERR_INCONS_VALUE);
331 return (SNMP_ERR_INCONS_VALUE);
338 return (SNMP_ERR_WRONG_VALUE);
340 tdep->status = value->v.integer;
341 tdep->set |= TDEP_STATUS;
342 return (SNMP_ERR_NOERROR);
344 case LEAF_begemotTrapSinkComm:
345 if (tdep->set & TDEP_COMM)
346 return (SNMP_ERR_INCONS_VALUE);
347 if (value->v.octetstring.len == 0 ||
348 value->v.octetstring.len > SNMP_COMMUNITY_MAXLEN)
349 return (SNMP_ERR_WRONG_VALUE);
350 for (p = value->v.octetstring.octets;
351 p < value->v.octetstring.octets + value->v.octetstring.len;
353 if (!isascii(*p) || !isprint(*p))
354 return (SNMP_ERR_WRONG_VALUE);
356 tdep->set |= TDEP_COMM;
357 strncpy(tdep->comm, value->v.octetstring.octets,
358 value->v.octetstring.len);
359 tdep->comm[value->v.octetstring.len] = '\0';
360 return (SNMP_ERR_NOERROR);
362 case LEAF_begemotTrapSinkVersion:
363 if (tdep->set & TDEP_VERSION)
364 return (SNMP_ERR_INCONS_VALUE);
365 if (value->v.integer != TRAPSINK_V1 &&
366 value->v.integer != TRAPSINK_V2)
367 return (SNMP_ERR_WRONG_VALUE);
368 tdep->version = value->v.integer;
369 tdep->set |= TDEP_VERSION;
370 return (SNMP_ERR_NOERROR);
373 return (SNMP_ERR_INCONS_NAME);
375 return (SNMP_ERR_NOT_WRITEABLE);
378 case SNMP_OP_ROLLBACK:
380 return (SNMP_ERR_NOERROR);
383 switch (value->var.subs[sub - 1]) {
385 case LEAF_begemotTrapSinkStatus:
386 value->v.integer = t->status;
389 case LEAF_begemotTrapSinkComm:
390 return (string_get(value, t->comm, -1));
392 case LEAF_begemotTrapSinkVersion:
393 value->v.integer = t->version;
397 return (SNMP_ERR_NOERROR);
401 snmp_send_trap(const struct asn_oid *trap_oid, ...)
405 const struct snmp_value *v;
411 TAILQ_FOREACH(t, &trapsink_list, link) {
412 if (t->status != TRAPSINK_ACTIVE)
414 memset(&pdu, 0, sizeof(pdu));
415 strcpy(pdu.community, t->comm);
416 if (t->version == TRAPSINK_V1) {
417 pdu.version = SNMP_V1;
418 pdu.type = SNMP_PDU_TRAP;
419 pdu.enterprise = systemg.object_id;
420 memcpy(pdu.agent_addr, snmpd.trap1addr, 4);
421 pdu.generic_trap = trap_oid->subs[trap_oid->len - 1] - 1;
422 pdu.specific_trap = 0;
423 pdu.time_stamp = get_ticks() - start_tick;
427 pdu.version = SNMP_V2c;
428 pdu.type = SNMP_PDU_TRAP2;
429 pdu.request_id = reqid_next(trap_reqid);
431 pdu.error_status = SNMP_ERR_NOERROR;
433 pdu.bindings[0].var = oid_sysUpTime;
434 pdu.bindings[0].var.subs[pdu.bindings[0].var.len++] = 0;
435 pdu.bindings[0].syntax = SNMP_SYNTAX_TIMETICKS;
436 pdu.bindings[0].v.uint32 = get_ticks() - start_tick;
438 pdu.bindings[1].var = oid_snmpTrapOID;
439 pdu.bindings[1].var.subs[pdu.bindings[1].var.len++] = 0;
440 pdu.bindings[1].syntax = SNMP_SYNTAX_OID;
441 pdu.bindings[1].v.oid = *trap_oid;
446 va_start(ap, trap_oid);
447 while ((v = va_arg(ap, const struct snmp_value *)) != NULL)
448 pdu.bindings[pdu.nbindings++] = *v;
451 if ((sndbuf = buf_alloc(1)) == NULL) {
452 syslog(LOG_ERR, "trap send buffer: %m");
456 snmp_output(&pdu, sndbuf, &sndlen, "TRAP");
458 if ((len = send(t->socket, sndbuf, sndlen, 0)) == -1)
459 syslog(LOG_ERR, "send: %m");
460 else if ((size_t)len != sndlen)
461 syslog(LOG_ERR, "send: short write %zu/%zu",
462 sndlen, (size_t)len);