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