xref: /freebsd/contrib/bsnmp/snmpd/trap.c (revision 2be1a816b9ff69588e55be0a84cbe2a31efc0f2f)
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 
52 struct trapsink_list trapsink_list = TAILQ_HEAD_INITIALIZER(trapsink_list);
53 
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;
58 
59 struct 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 };
70 enum {
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 
80 static int
81 trapsink_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 
142 static void
143 trapsink_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 
151 static int
152 trapsink_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 
188 static int
189 trapsink_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 
201 static int
202 trapsink_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 
211 static int
212 trapsink_undestroy(struct trapsink *t, struct trapsink_dep *tdep)
213 {
214 	t->status = tdep->rb_status;
215 	return (SNMP_ERR_NOERROR);
216 }
217 
218 static int
219 trapsink_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 
275 int
276 op_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 
400 void
401 snmp_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