trap.c revision 150920
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/sysctl.h>
35#include <sys/un.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <stdarg.h>
39#include <stdarg.h>
40#include <string.h>
41#include <ctype.h>
42#include <syslog.h>
43#include <unistd.h>
44#include <netinet/in.h>
45#include <arpa/inet.h>
46
47#include "snmpmod.h"
48#include "snmpd.h"
49#include "tree.h"
50#include "oid.h"
51
52struct trapsink_list trapsink_list = TAILQ_HEAD_INITIALIZER(trapsink_list);
53
54static const struct asn_oid oid_begemotTrapSinkTable =
55    OIDX_begemotTrapSinkTable;
56static const struct asn_oid oid_sysUpTime = OIDX_sysUpTime;
57static const struct asn_oid oid_snmpTrapOID = OIDX_snmpTrapOID;
58
59struct trapsink_dep {
60	struct snmp_dependency dep;
61	u_int	set;
62	u_int	status;
63	u_char	comm[SNMP_COMMUNITY_MAXLEN + 1];
64	u_int	version;
65	u_int	rb;
66	u_int	rb_status;
67	u_int	rb_version;
68	u_char	rb_comm[SNMP_COMMUNITY_MAXLEN + 1];
69};
70enum {
71	TDEP_STATUS	= 0x0001,
72	TDEP_COMM	= 0x0002,
73	TDEP_VERSION	= 0x0004,
74
75	TDEP_CREATE	= 0x0001,
76	TDEP_MODIFY	= 0x0002,
77	TDEP_DESTROY	= 0x0004,
78};
79
80static int
81trapsink_create(struct trapsink_dep *tdep)
82{
83	struct trapsink *t;
84	struct sockaddr_in sa;
85
86	if ((t = malloc(sizeof(*t))) == NULL)
87		return (SNMP_ERR_RES_UNAVAIL);
88
89	t->index = tdep->dep.idx;
90	t->status = TRAPSINK_NOT_READY;
91	t->comm[0] = '\0';
92	t->version = TRAPSINK_V2;
93
94	if ((t->socket = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
95		syslog(LOG_ERR, "socket(UDP): %m");
96		free(t);
97		return (SNMP_ERR_RES_UNAVAIL);
98	}
99	(void)shutdown(t->socket, SHUT_RD);
100
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]);
107
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);
112		free(t);
113		return (SNMP_ERR_GENERR);
114	}
115
116	if (tdep->set & TDEP_VERSION)
117		t->version = tdep->version;
118	if (tdep->set & TDEP_COMM)
119		strcpy(t->comm, tdep->comm);
120
121	if (t->comm[0] != '\0')
122		t->status = TRAPSINK_NOT_IN_SERVICE;
123
124	/* look whether we should activate */
125	if (tdep->status == 4) {
126		if (t->status == TRAPSINK_NOT_READY) {
127			if (t->socket != -1)
128				(void)close(t->socket);
129			free(t);
130			return (SNMP_ERR_INCONS_VALUE);
131		}
132		t->status = TRAPSINK_ACTIVE;
133	}
134
135	INSERT_OBJECT_OID(t, &trapsink_list);
136
137	tdep->rb |= TDEP_CREATE;
138
139	return (SNMP_ERR_NOERROR);
140}
141
142static void
143trapsink_free(struct trapsink *t)
144{
145	TAILQ_REMOVE(&trapsink_list, t, link);
146	if (t->socket != -1)
147		(void)close(t->socket);
148	free(t);
149}
150
151static int
152trapsink_modify(struct trapsink *t, struct trapsink_dep *tdep)
153{
154	tdep->rb_status = t->status;
155	tdep->rb_version = t->version;
156	strcpy(tdep->rb_comm, t->comm);
157
158	if (tdep->set & TDEP_STATUS) {
159		/* if we are active and should move to not_in_service do
160		 * this first */
161		if (tdep->status == 2 && tdep->rb_status == TRAPSINK_ACTIVE) {
162			t->status = TRAPSINK_NOT_IN_SERVICE;
163			tdep->rb |= TDEP_MODIFY;
164		}
165	}
166
167	if (tdep->set & TDEP_VERSION)
168		t->version = tdep->version;
169	if (tdep->set & TDEP_COMM)
170		strcpy(t->comm, tdep->comm);
171
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);
180			}
181			t->status = TRAPSINK_ACTIVE;
182			tdep->rb |= TDEP_MODIFY;
183		}
184	}
185	return (SNMP_ERR_NOERROR);
186}
187
188static int
189trapsink_unmodify(struct trapsink *t, struct trapsink_dep *tdep)
190{
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);
197
198	return (SNMP_ERR_NOERROR);
199}
200
201static int
202trapsink_destroy(struct snmp_context *ctx __unused, struct trapsink *t,
203    struct trapsink_dep *tdep)
204{
205	t->status = TRAPSINK_DESTROY;
206	tdep->rb_status = t->status;
207	tdep->rb |= TDEP_DESTROY;
208	return (SNMP_ERR_NOERROR);
209}
210
211static int
212trapsink_undestroy(struct trapsink *t, struct trapsink_dep *tdep)
213{
214	t->status = tdep->rb_status;
215	return (SNMP_ERR_NOERROR);
216}
217
218static int
219trapsink_dep(struct snmp_context *ctx, struct snmp_dependency *dep,
220    enum snmp_depop op)
221{
222	struct trapsink_dep *tdep = (struct trapsink_dep *)dep;
223	struct trapsink *t;
224
225	t = FIND_OBJECT_OID(&trapsink_list, &dep->idx, 0);
226
227	switch (op) {
228
229	  case SNMP_DEPOP_COMMIT:
230		if (tdep->set & TDEP_STATUS) {
231			switch (tdep->status) {
232
233			  case 1:
234			  case 2:
235				if (t == NULL)
236					return (SNMP_ERR_INCONS_VALUE);
237				return (trapsink_modify(t, tdep));
238
239			  case 4:
240			  case 5:
241				if (t != NULL)
242					return (SNMP_ERR_INCONS_VALUE);
243				return (trapsink_create(tdep));
244
245			  case 6:
246				if (t == NULL)
247					return (SNMP_ERR_NOERROR);
248				return (trapsink_destroy(ctx, t, tdep));
249			}
250		} else if (tdep->set != 0)
251			return (trapsink_modify(t, tdep));
252
253		return (SNMP_ERR_NOERROR);
254
255	  case SNMP_DEPOP_ROLLBACK:
256		if (tdep->rb & TDEP_CREATE) {
257			trapsink_free(t);
258			return (SNMP_ERR_NOERROR);
259		}
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);
265
266	  case SNMP_DEPOP_FINISH:
267		if ((tdep->rb & TDEP_DESTROY) && t != NULL &&
268		    ctx->code == SNMP_RET_OK)
269			trapsink_free(t);
270		return (SNMP_ERR_NOERROR);
271	}
272	abort();
273}
274
275int
276op_trapsink(struct snmp_context *ctx, struct snmp_value *value,
277    u_int sub, u_int iidx, enum snmp_op op)
278{
279	struct trapsink *t;
280	u_char ipa[4];
281	int32_t port;
282	struct asn_oid idx;
283	struct trapsink_dep *tdep;
284	u_char *p;
285
286	t = NULL;		/* gcc */
287
288	switch (op) {
289
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);
294		break;
295
296	  case SNMP_OP_GET:
297		if ((t = FIND_OBJECT_OID(&trapsink_list, &value->var, sub)) == NULL)
298			return (SNMP_ERR_NOSUCHNAME);
299		break;
300
301	  case SNMP_OP_SET:
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);
306
307		asn_slice_oid(&idx, &value->var, sub, value->var.len);
308
309		tdep = (struct trapsink_dep *)snmp_dep_lookup(ctx,
310		    &oid_begemotTrapSinkTable, &idx,
311		    sizeof(*tdep), trapsink_dep);
312		if (tdep == NULL)
313			return (SNMP_ERR_RES_UNAVAIL);
314
315		switch (value->var.subs[sub - 1]) {
316
317		  case LEAF_begemotTrapSinkStatus:
318			if (tdep->set & TDEP_STATUS)
319				return (SNMP_ERR_INCONS_VALUE);
320			switch (value->v.integer) {
321
322			  case 1:
323			  case 2:
324				if (t == NULL)
325					return (SNMP_ERR_INCONS_VALUE);
326				break;
327
328			  case 4:
329			  case 5:
330				if (t != NULL)
331					return (SNMP_ERR_INCONS_VALUE);
332				break;
333
334			  case 6:
335				break;
336
337			  default:
338				return (SNMP_ERR_WRONG_VALUE);
339			}
340			tdep->status = value->v.integer;
341			tdep->set |= TDEP_STATUS;
342			return (SNMP_ERR_NOERROR);
343
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;
352			     p++) {
353				if (!isascii(*p) || !isprint(*p))
354					return (SNMP_ERR_WRONG_VALUE);
355			}
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);
361
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);
371		}
372		if (t == NULL)
373			return (SNMP_ERR_INCONS_NAME);
374		else
375			return (SNMP_ERR_NOT_WRITEABLE);
376
377
378	  case SNMP_OP_ROLLBACK:
379	  case SNMP_OP_COMMIT:
380		return (SNMP_ERR_NOERROR);
381	}
382
383	switch (value->var.subs[sub - 1]) {
384
385	  case LEAF_begemotTrapSinkStatus:
386		value->v.integer = t->status;
387		break;
388
389	  case LEAF_begemotTrapSinkComm:
390		return (string_get(value, t->comm, -1));
391
392	  case LEAF_begemotTrapSinkVersion:
393		value->v.integer = t->version;
394		break;
395
396	}
397	return (SNMP_ERR_NOERROR);
398}
399
400void
401snmp_send_trap(const struct asn_oid *trap_oid, ...)
402{
403	struct snmp_pdu pdu;
404	struct trapsink *t;
405	const struct snmp_value *v;
406	va_list ap;
407	u_char *sndbuf;
408	size_t sndlen;
409	ssize_t len;
410
411	TAILQ_FOREACH(t, &trapsink_list, link) {
412		if (t->status != TRAPSINK_ACTIVE)
413			continue;
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;
424
425			pdu.nbindings = 0;
426		} else {
427			pdu.version = SNMP_V2c;
428			pdu.type = SNMP_PDU_TRAP2;
429			pdu.request_id = reqid_next(trap_reqid);
430			pdu.error_index = 0;
431			pdu.error_status = SNMP_ERR_NOERROR;
432
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;
437
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;
442
443			pdu.nbindings = 2;
444		}
445
446		va_start(ap, trap_oid);
447		while ((v = va_arg(ap, const struct snmp_value *)) != NULL)
448			pdu.bindings[pdu.nbindings++] = *v;
449		va_end(ap);
450
451		if ((sndbuf = buf_alloc(1)) == NULL) {
452			syslog(LOG_ERR, "trap send buffer: %m");
453			return;
454		}
455
456		snmp_output(&pdu, sndbuf, &sndlen, "TRAP");
457
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);
463
464		free(sndbuf);
465	}
466}
467