xref: /freebsd/contrib/bsnmp/snmpd/trap.c (revision f0adf7f5cdd241db2f2c817683191a6ef64a4e95)
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.7 2004/04/13 14:58:46 novo 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 
56 struct trapsink_list trapsink_list = TAILQ_HEAD_INITIALIZER(trapsink_list);
57 
58 static const struct asn_oid oid_begemotTrapSinkTable =
59     OIDX_begemotTrapSinkTable;
60 static const struct asn_oid oid_sysUpTime = OIDX_sysUpTime;
61 static const struct asn_oid oid_snmpTrapOID = OIDX_snmpTrapOID;
62 
63 struct 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 };
74 enum {
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 
84 static int
85 trapsink_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 
146 static void
147 trapsink_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 
155 static int
156 trapsink_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 
192 static int
193 trapsink_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 
205 static int
206 trapsink_destroy(struct snmp_context *ctx __unused, struct trapsink *t,
207     struct trapsink_dep *tdep)
208 {
209 	t->status = TRAPSINK_DESTROY;
210 	tdep->rb_status = t->status;
211 	tdep->rb |= TDEP_DESTROY;
212 	return (SNMP_ERR_NOERROR);
213 }
214 
215 static int
216 trapsink_undestroy(struct trapsink *t, struct trapsink_dep *tdep)
217 {
218 	t->status = tdep->rb_status;
219 	return (SNMP_ERR_NOERROR);
220 }
221 
222 static int
223 trapsink_dep(struct snmp_context *ctx, struct snmp_dependency *dep,
224     enum snmp_depop op)
225 {
226 	struct trapsink_dep *tdep = (struct trapsink_dep *)dep;
227 	struct trapsink *t;
228 
229 	t = FIND_OBJECT_OID(&trapsink_list, &dep->idx, 0);
230 
231 	switch (op) {
232 
233 	  case SNMP_DEPOP_COMMIT:
234 		if (tdep->set & TDEP_STATUS) {
235 			switch (tdep->status) {
236 
237 			  case 1:
238 			  case 2:
239 				if (t == NULL)
240 					return (SNMP_ERR_INCONS_VALUE);
241 				return (trapsink_modify(t, tdep));
242 
243 			  case 4:
244 			  case 5:
245 				if (t != NULL)
246 					return (SNMP_ERR_INCONS_VALUE);
247 				return (trapsink_create(tdep));
248 
249 			  case 6:
250 				if (t == NULL)
251 					return (SNMP_ERR_NOERROR);
252 				return (trapsink_destroy(ctx, t, tdep));
253 			}
254 		} else if (tdep->set != 0)
255 			return (trapsink_modify(t, tdep));
256 
257 		return (SNMP_ERR_NOERROR);
258 
259 	  case SNMP_DEPOP_ROLLBACK:
260 		if (tdep->rb & TDEP_CREATE) {
261 			trapsink_free(t);
262 			return (SNMP_ERR_NOERROR);
263 		}
264 		if (tdep->rb & TDEP_MODIFY)
265 			return (trapsink_unmodify(t, tdep));
266 		if(tdep->rb & TDEP_DESTROY)
267 			return (trapsink_undestroy(t, tdep));
268 		return (SNMP_ERR_NOERROR);
269 
270 	  case SNMP_DEPOP_FINISH:
271 		if ((tdep->rb & TDEP_DESTROY) && t != NULL &&
272 		    ctx->code == SNMP_RET_OK)
273 			trapsink_free(t);
274 		return (SNMP_ERR_NOERROR);
275 	}
276 	abort();
277 }
278 
279 int
280 op_trapsink(struct snmp_context *ctx, struct snmp_value *value,
281     u_int sub, u_int iidx, enum snmp_op op)
282 {
283 	struct trapsink *t;
284 	u_char ipa[4];
285 	int32_t port;
286 	struct asn_oid idx;
287 	struct trapsink_dep *tdep;
288 	u_char *p;
289 
290 	t = NULL;		/* gcc */
291 
292 	switch (op) {
293 
294 	  case SNMP_OP_GETNEXT:
295 		if ((t = NEXT_OBJECT_OID(&trapsink_list, &value->var, sub)) == NULL)
296 			return (SNMP_ERR_NOSUCHNAME);
297 		index_append(&value->var, sub, &t->index);
298 		break;
299 
300 	  case SNMP_OP_GET:
301 		if ((t = FIND_OBJECT_OID(&trapsink_list, &value->var, sub)) == NULL)
302 			return (SNMP_ERR_NOSUCHNAME);
303 		break;
304 
305 	  case SNMP_OP_SET:
306 		if (index_decode(&value->var, sub, iidx, ipa, &port) ||
307 		    port == 0 || port > 65535)
308 			return (SNMP_ERR_NO_CREATION);
309 		t = FIND_OBJECT_OID(&trapsink_list, &value->var, sub);
310 
311 		asn_slice_oid(&idx, &value->var, sub, value->var.len);
312 
313 		tdep = (struct trapsink_dep *)snmp_dep_lookup(ctx,
314 		    &oid_begemotTrapSinkTable, &idx,
315 		    sizeof(*tdep), trapsink_dep);
316 		if (tdep == NULL)
317 			return (SNMP_ERR_RES_UNAVAIL);
318 
319 		switch (value->var.subs[sub - 1]) {
320 
321 		  case LEAF_begemotTrapSinkStatus:
322 			if (tdep->set & TDEP_STATUS)
323 				return (SNMP_ERR_INCONS_VALUE);
324 			switch (value->v.integer) {
325 
326 			  case 1:
327 			  case 2:
328 				if (t == NULL)
329 					return (SNMP_ERR_INCONS_VALUE);
330 				break;
331 
332 			  case 4:
333 			  case 5:
334 				if (t != NULL)
335 					return (SNMP_ERR_INCONS_VALUE);
336 				break;
337 
338 			  case 6:
339 				break;
340 
341 			  default:
342 				return (SNMP_ERR_WRONG_VALUE);
343 			}
344 			tdep->status = value->v.integer;
345 			tdep->set |= TDEP_STATUS;
346 			return (SNMP_ERR_NOERROR);
347 
348 		  case LEAF_begemotTrapSinkComm:
349 			if (tdep->set & TDEP_COMM)
350 				return (SNMP_ERR_INCONS_VALUE);
351 			if (value->v.octetstring.len == 0 ||
352 			    value->v.octetstring.len > SNMP_COMMUNITY_MAXLEN)
353 				return (SNMP_ERR_WRONG_VALUE);
354 			for (p = value->v.octetstring.octets;
355 			     p < value->v.octetstring.octets + value->v.octetstring.len;
356 			     p++) {
357 				if (!isascii(*p) || !isprint(*p))
358 					return (SNMP_ERR_WRONG_VALUE);
359 			}
360 			tdep->set |= TDEP_COMM;
361 			strncpy(tdep->comm, value->v.octetstring.octets,
362 			    value->v.octetstring.len);
363 			tdep->comm[value->v.octetstring.len] = '\0';
364 			return (SNMP_ERR_NOERROR);
365 
366 		  case LEAF_begemotTrapSinkVersion:
367 			if (tdep->set & TDEP_VERSION)
368 				return (SNMP_ERR_INCONS_VALUE);
369 			if (value->v.integer != TRAPSINK_V1 &&
370 			    value->v.integer != TRAPSINK_V2)
371 				return (SNMP_ERR_WRONG_VALUE);
372 			tdep->version = value->v.integer;
373 			tdep->set |= TDEP_VERSION;
374 			return (SNMP_ERR_NOERROR);
375 		}
376 		if (t == NULL)
377 			return (SNMP_ERR_INCONS_NAME);
378 		else
379 			return (SNMP_ERR_NOT_WRITEABLE);
380 
381 
382 	  case SNMP_OP_ROLLBACK:
383 	  case SNMP_OP_COMMIT:
384 		return (SNMP_ERR_NOERROR);
385 	}
386 
387 	switch (value->var.subs[sub - 1]) {
388 
389 	  case LEAF_begemotTrapSinkStatus:
390 		value->v.integer = t->status;
391 		break;
392 
393 	  case LEAF_begemotTrapSinkComm:
394 		return (string_get(value, t->comm, -1));
395 
396 	  case LEAF_begemotTrapSinkVersion:
397 		value->v.integer = t->version;
398 		break;
399 
400 	}
401 	return (SNMP_ERR_NOERROR);
402 }
403 
404 void
405 snmp_send_trap(const struct asn_oid *trap_oid, ...)
406 {
407 	struct snmp_pdu pdu;
408 	struct trapsink *t;
409 	const struct snmp_value *v;
410 	va_list ap;
411 	u_char *sndbuf;
412 	size_t sndlen;
413 	ssize_t len;
414 
415 	TAILQ_FOREACH(t, &trapsink_list, link) {
416 		if (t->status != TRAPSINK_ACTIVE)
417 			continue;
418 		memset(&pdu, 0, sizeof(pdu));
419 		strcpy(pdu.community, t->comm);
420 		if (t->version == TRAPSINK_V1) {
421 			pdu.version = SNMP_V1;
422 			pdu.type = SNMP_PDU_TRAP;
423 			pdu.enterprise = systemg.object_id;
424 			memcpy(pdu.agent_addr, snmpd.trap1addr, 4);
425 			pdu.generic_trap = trap_oid->subs[trap_oid->len - 1] - 1;
426 			pdu.specific_trap = 0;
427 			pdu.time_stamp = get_ticks() - start_tick;
428 
429 			pdu.nbindings = 0;
430 		} else {
431 			pdu.version = SNMP_V2c;
432 			pdu.type = SNMP_PDU_TRAP2;
433 			pdu.request_id = reqid_next(trap_reqid);
434 			pdu.error_index = 0;
435 			pdu.error_status = SNMP_ERR_NOERROR;
436 
437 			pdu.bindings[0].var = oid_sysUpTime;
438 			pdu.bindings[0].var.subs[pdu.bindings[0].var.len++] = 0;
439 			pdu.bindings[0].syntax = SNMP_SYNTAX_TIMETICKS;
440 			pdu.bindings[0].v.uint32 = get_ticks() - start_tick;
441 
442 			pdu.bindings[1].var = oid_snmpTrapOID;
443 			pdu.bindings[1].var.subs[pdu.bindings[1].var.len++] = 0;
444 			pdu.bindings[1].syntax = SNMP_SYNTAX_OID;
445 			pdu.bindings[1].v.oid = *trap_oid;
446 
447 			pdu.nbindings = 2;
448 		}
449 
450 		va_start(ap, trap_oid);
451 		while ((v = va_arg(ap, const struct snmp_value *)) != NULL)
452 			pdu.bindings[pdu.nbindings++] = *v;
453 		va_end(ap);
454 
455 		if ((sndbuf = buf_alloc(1)) == NULL) {
456 			syslog(LOG_ERR, "trap send buffer: %m");
457 			return;
458 		}
459 
460 		snmp_output(&pdu, sndbuf, &sndlen, "TRAP");
461 
462 		if ((len = send(t->socket, sndbuf, sndlen, 0)) == -1)
463 			syslog(LOG_ERR, "send: %m");
464 		else if ((size_t)len != sndlen)
465 			syslog(LOG_ERR, "send: short write %zu/%zu",
466 			    sndlen, (size_t)len);
467 
468 		free(sndbuf);
469 	}
470 }
471