trap.c revision 124861
1/*
2 * Copyright (c) 2001-2003
3 *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4 *	All rights reserved.
5 *
6 * Author: Harti Brandt <harti@freebsd.org>
7 *
8 * Redistribution of this software and documentation and use in source and
9 * binary forms, with or without modification, are permitted provided that
10 * the following conditions are met:
11 *
12 * 1. Redistributions of source code or documentation must retain the above
13 *    copyright notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
22 * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
23 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
24 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
25 * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS  BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
28 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
31 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 * $Begemot: bsnmp/snmpd/trap.c,v 1.6 2003/12/03 10:08:47 hbb Exp $
34 *
35 * TrapSinkTable
36 */
37#include <sys/types.h>
38#include <sys/sysctl.h>
39#include <sys/un.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <stdarg.h>
43#include <stdarg.h>
44#include <string.h>
45#include <ctype.h>
46#include <syslog.h>
47#include <unistd.h>
48#include <netinet/in.h>
49#include <arpa/inet.h>
50
51#include "snmpmod.h"
52#include "snmpd.h"
53#include "tree.h"
54#include "oid.h"
55
56struct trapsink_list trapsink_list = TAILQ_HEAD_INITIALIZER(trapsink_list);
57
58static const struct asn_oid oid_begemotTrapSinkTable =
59    OIDX_begemotTrapSinkTable;
60static const struct asn_oid oid_sysUpTime = OIDX_sysUpTime;
61static const struct asn_oid oid_snmpTrapOID = OIDX_snmpTrapOID;
62
63struct trapsink_dep {
64	struct snmp_dependency dep;
65	u_int	set;
66	u_int	status;
67	u_char	comm[SNMP_COMMUNITY_MAXLEN + 1];
68	u_int	version;
69	u_int	rb;
70	u_int	rb_status;
71	u_int	rb_version;
72	u_char	rb_comm[SNMP_COMMUNITY_MAXLEN + 1];
73};
74enum {
75	TDEP_STATUS	= 0x0001,
76	TDEP_COMM	= 0x0002,
77	TDEP_VERSION	= 0x0004,
78
79	TDEP_CREATE	= 0x0001,
80	TDEP_MODIFY	= 0x0002,
81	TDEP_DESTROY	= 0x0004,
82};
83
84static int
85trapsink_create(struct trapsink_dep *tdep)
86{
87	struct trapsink *t;
88	struct sockaddr_in sa;
89
90	if ((t = malloc(sizeof(*t))) == NULL)
91		return (SNMP_ERR_RES_UNAVAIL);
92
93	t->index = tdep->dep.idx;
94	t->status = TRAPSINK_NOT_READY;
95	t->comm[0] = '\0';
96	t->version = TRAPSINK_V2;
97
98	if ((t->socket = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
99		syslog(LOG_ERR, "socket(UDP): %m");
100		free(t);
101		return (SNMP_ERR_RES_UNAVAIL);
102	}
103	(void)shutdown(t->socket, SHUT_RD);
104
105	sa.sin_len = sizeof(sa);
106	sa.sin_family = AF_INET;
107	sa.sin_addr.s_addr = htonl((t->index.subs[0] << 24) |
108	    (t->index.subs[1] << 16) | (t->index.subs[2] << 8) |
109	    (t->index.subs[3] << 0));
110	sa.sin_port = htons(t->index.subs[4]);
111
112	if (connect(t->socket, (struct sockaddr *)&sa, sa.sin_len) == -1) {
113		syslog(LOG_ERR, "connect(%s,%u): %m",
114		    inet_ntoa(sa.sin_addr), ntohl(sa.sin_port));
115		(void)close(t->socket);
116		free(t);
117		return (SNMP_ERR_GENERR);
118	}
119
120	if (tdep->set & TDEP_VERSION)
121		t->version = tdep->version;
122	if (tdep->set & TDEP_COMM)
123		strcpy(t->comm, tdep->comm);
124
125	if (t->comm[0] != '\0')
126		t->status = TRAPSINK_NOT_IN_SERVICE;
127
128	/* look whether we should activate */
129	if (tdep->status == 4) {
130		if (t->status == TRAPSINK_NOT_READY) {
131			if (t->socket != -1)
132				(void)close(t->socket);
133			free(t);
134			return (SNMP_ERR_INCONS_VALUE);
135		}
136		t->status = TRAPSINK_ACTIVE;
137	}
138
139	INSERT_OBJECT_OID(t, &trapsink_list);
140
141	tdep->rb |= TDEP_CREATE;
142
143	return (SNMP_ERR_NOERROR);
144}
145
146static void
147trapsink_free(struct trapsink *t)
148{
149	TAILQ_REMOVE(&trapsink_list, t, link);
150	if (t->socket != -1)
151		(void)close(t->socket);
152	free(t);
153}
154
155static int
156trapsink_modify(struct trapsink *t, struct trapsink_dep *tdep)
157{
158	tdep->rb_status = t->status;
159	tdep->rb_version = t->version;
160	strcpy(tdep->rb_comm, t->comm);
161
162	if (tdep->set & TDEP_STATUS) {
163		/* if we are active and should move to not_in_service do
164		 * this first */
165		if (tdep->status == 2 && tdep->rb_status == TRAPSINK_ACTIVE) {
166			t->status = TRAPSINK_NOT_IN_SERVICE;
167			tdep->rb |= TDEP_MODIFY;
168		}
169	}
170
171	if (tdep->set & TDEP_VERSION)
172		t->version = tdep->version;
173	if (tdep->set & TDEP_COMM)
174		strcpy(t->comm, tdep->comm);
175
176	if (tdep->set & TDEP_STATUS) {
177		/* if we were inactive and should go active - do this now */
178		if (tdep->status == 1 && tdep->rb_status != TRAPSINK_ACTIVE) {
179			if (t->comm[0] == '\0') {
180				t->status = tdep->rb_status;
181				t->version = tdep->rb_version;
182				strcpy(t->comm, tdep->rb_comm);
183				return (SNMP_ERR_INCONS_VALUE);
184			}
185			t->status = TRAPSINK_ACTIVE;
186			tdep->rb |= TDEP_MODIFY;
187		}
188	}
189	return (SNMP_ERR_NOERROR);
190}
191
192static int
193trapsink_unmodify(struct trapsink *t, struct trapsink_dep *tdep)
194{
195	if (tdep->set & TDEP_STATUS)
196		t->status = tdep->rb_status;
197	if (tdep->set & TDEP_VERSION)
198		t->version = tdep->rb_version;
199	if (tdep->set & TDEP_COMM)
200		strcpy(t->comm, tdep->rb_comm);
201
202	return (SNMP_ERR_NOERROR);
203}
204
205static void
206trapsink_finish(struct snmp_context *ctx __unused, int fail, void *arg)
207{
208	struct trapsink *t = arg;
209
210	if (!fail)
211		trapsink_free(t);
212}
213
214static int
215trapsink_destroy(struct snmp_context *ctx, struct trapsink *t,
216    struct trapsink_dep *tdep)
217{
218	if (snmp_set_atfinish(ctx, trapsink_finish, t))
219		return (SNMP_ERR_RES_UNAVAIL);
220	t->status = TRAPSINK_DESTROY;
221	tdep->rb_status = t->status;
222	tdep->rb |= TDEP_DESTROY;
223	return (SNMP_ERR_NOERROR);
224}
225
226static int
227trapsink_undestroy(struct trapsink *t, struct trapsink_dep *tdep)
228{
229	t->status = tdep->rb_status;
230	return (SNMP_ERR_NOERROR);
231}
232
233static int
234trapsink_dep(struct snmp_context *ctx, struct snmp_dependency *dep,
235    enum snmp_depop op)
236{
237	struct trapsink_dep *tdep = (struct trapsink_dep *)dep;
238	struct trapsink *t;
239
240	t = FIND_OBJECT_OID(&trapsink_list, &dep->idx, 0);
241
242	switch (op) {
243
244	  case SNMP_DEPOP_COMMIT:
245		if (tdep->set & TDEP_STATUS) {
246			switch (tdep->status) {
247
248			  case 1:
249			  case 2:
250				if (t == NULL)
251					return (SNMP_ERR_INCONS_VALUE);
252				return (trapsink_modify(t, tdep));
253
254			  case 4:
255			  case 5:
256				if (t != NULL)
257					return (SNMP_ERR_INCONS_VALUE);
258				return (trapsink_create(tdep));
259
260			  case 6:
261				if (t == NULL)
262					return (SNMP_ERR_NOERROR);
263				return (trapsink_destroy(ctx, t, tdep));
264			}
265		} else if (tdep->set != 0)
266			return (trapsink_modify(t, tdep));
267
268		return (SNMP_ERR_NOERROR);
269
270	  case SNMP_DEPOP_ROLLBACK:
271		if (tdep->rb & TDEP_CREATE) {
272			trapsink_free(t);
273			return (SNMP_ERR_NOERROR);
274		}
275		if (tdep->rb & TDEP_MODIFY)
276			return (trapsink_unmodify(t, tdep));
277		if(tdep->rb & TDEP_DESTROY)
278			return (trapsink_undestroy(t, tdep));
279		return (SNMP_ERR_NOERROR);
280	}
281	abort();
282}
283
284int
285op_trapsink(struct snmp_context *ctx, struct snmp_value *value,
286    u_int sub, u_int iidx, enum snmp_op op)
287{
288	struct trapsink *t;
289	u_char ipa[4];
290	int32_t port;
291	struct asn_oid idx;
292	struct trapsink_dep *tdep;
293	u_char *p;
294
295	t = NULL;		/* gcc */
296
297	switch (op) {
298
299	  case SNMP_OP_GETNEXT:
300		if ((t = NEXT_OBJECT_OID(&trapsink_list, &value->var, sub)) == NULL)
301			return (SNMP_ERR_NOSUCHNAME);
302		index_append(&value->var, sub, &t->index);
303		break;
304
305	  case SNMP_OP_GET:
306		if ((t = FIND_OBJECT_OID(&trapsink_list, &value->var, sub)) == NULL)
307			return (SNMP_ERR_NOSUCHNAME);
308		break;
309
310	  case SNMP_OP_SET:
311		if (index_decode(&value->var, sub, iidx, ipa, &port) ||
312		    port == 0 || port > 65535)
313			return (SNMP_ERR_NO_CREATION);
314		t = FIND_OBJECT_OID(&trapsink_list, &value->var, sub);
315
316		asn_slice_oid(&idx, &value->var, sub, value->var.len);
317
318		tdep = (struct trapsink_dep *)snmp_dep_lookup(ctx,
319		    &oid_begemotTrapSinkTable, &idx,
320		    sizeof(*tdep), trapsink_dep);
321		if (tdep == NULL)
322			return (SNMP_ERR_RES_UNAVAIL);
323
324		switch (value->var.subs[sub - 1]) {
325
326		  case LEAF_begemotTrapSinkStatus:
327			if (tdep->set & TDEP_STATUS)
328				return (SNMP_ERR_INCONS_VALUE);
329			switch (value->v.integer) {
330
331			  case 1:
332			  case 2:
333				if (t == NULL)
334					return (SNMP_ERR_INCONS_VALUE);
335				break;
336
337			  case 4:
338			  case 5:
339				if (t != NULL)
340					return (SNMP_ERR_INCONS_VALUE);
341				break;
342
343			  case 6:
344				break;
345
346			  default:
347				return (SNMP_ERR_WRONG_VALUE);
348			}
349			tdep->status = value->v.integer;
350			tdep->set |= TDEP_STATUS;
351			return (SNMP_ERR_NOERROR);
352
353		  case LEAF_begemotTrapSinkComm:
354			if (tdep->set & TDEP_COMM)
355				return (SNMP_ERR_INCONS_VALUE);
356			if (value->v.octetstring.len == 0 ||
357			    value->v.octetstring.len > SNMP_COMMUNITY_MAXLEN)
358				return (SNMP_ERR_WRONG_VALUE);
359			for (p = value->v.octetstring.octets;
360			     p < value->v.octetstring.octets + value->v.octetstring.len;
361			     p++) {
362				if (!isascii(*p) || !isprint(*p))
363					return (SNMP_ERR_WRONG_VALUE);
364			}
365			tdep->set |= TDEP_COMM;
366			strncpy(tdep->comm, value->v.octetstring.octets,
367			    value->v.octetstring.len);
368			tdep->comm[value->v.octetstring.len] = '\0';
369			return (SNMP_ERR_NOERROR);
370
371		  case LEAF_begemotTrapSinkVersion:
372			if (tdep->set & TDEP_VERSION)
373				return (SNMP_ERR_INCONS_VALUE);
374			if (value->v.integer != TRAPSINK_V1 &&
375			    value->v.integer != TRAPSINK_V2)
376				return (SNMP_ERR_WRONG_VALUE);
377			tdep->version = value->v.integer;
378			tdep->set |= TDEP_VERSION;
379			return (SNMP_ERR_NOERROR);
380		}
381		if (t == NULL)
382			return (SNMP_ERR_INCONS_NAME);
383		else
384			return (SNMP_ERR_NOT_WRITEABLE);
385
386
387	  case SNMP_OP_ROLLBACK:
388	  case SNMP_OP_COMMIT:
389		return (SNMP_ERR_NOERROR);
390	}
391
392	switch (value->var.subs[sub - 1]) {
393
394	  case LEAF_begemotTrapSinkStatus:
395		value->v.integer = t->status;
396		break;
397
398	  case LEAF_begemotTrapSinkComm:
399		return (string_get(value, t->comm, -1));
400
401	  case LEAF_begemotTrapSinkVersion:
402		value->v.integer = t->version;
403		break;
404
405	}
406	return (SNMP_ERR_NOERROR);
407}
408
409void
410snmp_send_trap(const struct asn_oid *trap_oid, ...)
411{
412	struct snmp_pdu pdu;
413	struct trapsink *t;
414	const struct snmp_value *v;
415	va_list ap;
416	u_char *sndbuf;
417	size_t sndlen;
418	ssize_t len;
419
420	TAILQ_FOREACH(t, &trapsink_list, link) {
421		if (t->status != TRAPSINK_ACTIVE)
422			continue;
423		memset(&pdu, 0, sizeof(pdu));
424		strcpy(pdu.community, t->comm);
425		if (t->version == TRAPSINK_V1) {
426			pdu.version = SNMP_V1;
427			pdu.type = SNMP_PDU_TRAP;
428			pdu.enterprise = systemg.object_id;
429			memcpy(pdu.agent_addr, snmpd.trap1addr, 4);
430			pdu.generic_trap = trap_oid->subs[trap_oid->len - 1] - 1;
431			pdu.specific_trap = 0;
432			pdu.time_stamp = get_ticks() - start_tick;
433
434			pdu.nbindings = 0;
435		} else {
436			pdu.version = SNMP_V2c;
437			pdu.type = SNMP_PDU_TRAP2;
438			pdu.request_id = reqid_next(trap_reqid);
439			pdu.error_index = 0;
440			pdu.error_status = SNMP_ERR_NOERROR;
441
442			pdu.bindings[0].var = oid_sysUpTime;
443			pdu.bindings[0].var.subs[pdu.bindings[0].var.len++] = 0;
444			pdu.bindings[0].syntax = SNMP_SYNTAX_TIMETICKS;
445			pdu.bindings[0].v.uint32 = get_ticks() - start_tick;
446
447			pdu.bindings[1].var = oid_snmpTrapOID;
448			pdu.bindings[1].var.subs[pdu.bindings[1].var.len++] = 0;
449			pdu.bindings[1].syntax = SNMP_SYNTAX_OID;
450			pdu.bindings[1].v.oid = *trap_oid;
451
452			pdu.nbindings = 2;
453		}
454
455		va_start(ap, trap_oid);
456		while ((v = va_arg(ap, const struct snmp_value *)) != NULL)
457			pdu.bindings[pdu.nbindings++] = *v;
458		va_end(ap);
459
460		if ((sndbuf = buf_alloc(1)) == NULL) {
461			syslog(LOG_ERR, "trap send buffer: %m");
462			return;
463		}
464
465		snmp_output(&pdu, sndbuf, &sndlen, "TRAP");
466
467		if ((len = send(t->socket, sndbuf, sndlen, 0)) == -1)
468			syslog(LOG_ERR, "send: %m");
469		else if ((size_t)len != sndlen)
470			syslog(LOG_ERR, "send: short write %zu/%zu",
471			    sndlen, (size_t)len);
472
473		free(sndbuf);
474	}
475}
476