xref: /freebsd/contrib/bsnmp/snmpd/trap.c (revision aa0a1e58)
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  * Copyright (c) 2010 The FreeBSD Foundation
9  * All rights reserved.
10  *
11  * Portions of this software were developed by Shteryana Sotirova Shopova
12  * under sponsorship from the FreeBSD Foundation.
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  * 1. Redistributions of source code must retain the above copyright
18  *    notice, this list of conditions and the following disclaimer.
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  *
23  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  * $Begemot: bsnmp/snmpd/trap.c,v 1.9 2005/10/04 11:21:39 brandt_h Exp $
36  *
37  * TrapSinkTable
38  */
39 #include <sys/types.h>
40 #include <sys/queue.h>
41 #include <sys/sysctl.h>
42 #include <sys/un.h>
43 #include <stdint.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <stdarg.h>
47 #include <stdarg.h>
48 #include <string.h>
49 #include <ctype.h>
50 #include <syslog.h>
51 #include <unistd.h>
52 #include <netinet/in.h>
53 #include <arpa/inet.h>
54 
55 #include "snmpmod.h"
56 #include "snmpd.h"
57 #include "tree.h"
58 #include "oid.h"
59 
60 struct trapsink_list trapsink_list = TAILQ_HEAD_INITIALIZER(trapsink_list);
61 
62 /* List of target addresses */
63 struct target_addresslist target_addresslist =
64     SLIST_HEAD_INITIALIZER(target_addresslist);
65 
66 /* List of target parameters */
67 struct target_paramlist target_paramlist =
68     SLIST_HEAD_INITIALIZER(target_paramlist);
69 
70 /* List of notification targets */
71 struct target_notifylist target_notifylist =
72     SLIST_HEAD_INITIALIZER(target_notifylist);
73 
74 static const struct asn_oid oid_begemotTrapSinkTable =
75     OIDX_begemotTrapSinkTable;
76 static const struct asn_oid oid_sysUpTime = OIDX_sysUpTime;
77 static const struct asn_oid oid_snmpTrapOID = OIDX_snmpTrapOID;
78 
79 struct trapsink_dep {
80 	struct snmp_dependency dep;
81 	u_int	set;
82 	u_int	status;
83 	u_char	comm[SNMP_COMMUNITY_MAXLEN + 1];
84 	u_int	version;
85 	u_int	rb;
86 	u_int	rb_status;
87 	u_int	rb_version;
88 	u_char	rb_comm[SNMP_COMMUNITY_MAXLEN + 1];
89 };
90 enum {
91 	TDEP_STATUS	= 0x0001,
92 	TDEP_COMM	= 0x0002,
93 	TDEP_VERSION	= 0x0004,
94 
95 	TDEP_CREATE	= 0x0001,
96 	TDEP_MODIFY	= 0x0002,
97 	TDEP_DESTROY	= 0x0004,
98 };
99 
100 static int
101 trapsink_create(struct trapsink_dep *tdep)
102 {
103 	struct trapsink *t;
104 	struct sockaddr_in sa;
105 
106 	if ((t = malloc(sizeof(*t))) == NULL)
107 		return (SNMP_ERR_RES_UNAVAIL);
108 
109 	t->index = tdep->dep.idx;
110 	t->status = TRAPSINK_NOT_READY;
111 	t->comm[0] = '\0';
112 	t->version = TRAPSINK_V2;
113 
114 	if ((t->socket = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
115 		syslog(LOG_ERR, "socket(UDP): %m");
116 		free(t);
117 		return (SNMP_ERR_RES_UNAVAIL);
118 	}
119 	(void)shutdown(t->socket, SHUT_RD);
120 
121 	sa.sin_len = sizeof(sa);
122 	sa.sin_family = AF_INET;
123 	sa.sin_addr.s_addr = htonl((t->index.subs[0] << 24) |
124 	    (t->index.subs[1] << 16) | (t->index.subs[2] << 8) |
125 	    (t->index.subs[3] << 0));
126 	sa.sin_port = htons(t->index.subs[4]);
127 
128 	if (connect(t->socket, (struct sockaddr *)&sa, sa.sin_len) == -1) {
129 		syslog(LOG_ERR, "connect(%s,%u): %m",
130 		    inet_ntoa(sa.sin_addr), ntohs(sa.sin_port));
131 		(void)close(t->socket);
132 		free(t);
133 		return (SNMP_ERR_GENERR);
134 	}
135 
136 	if (tdep->set & TDEP_VERSION)
137 		t->version = tdep->version;
138 	if (tdep->set & TDEP_COMM)
139 		strcpy(t->comm, tdep->comm);
140 
141 	if (t->comm[0] != '\0')
142 		t->status = TRAPSINK_NOT_IN_SERVICE;
143 
144 	/* look whether we should activate */
145 	if (tdep->status == 4) {
146 		if (t->status == TRAPSINK_NOT_READY) {
147 			if (t->socket != -1)
148 				(void)close(t->socket);
149 			free(t);
150 			return (SNMP_ERR_INCONS_VALUE);
151 		}
152 		t->status = TRAPSINK_ACTIVE;
153 	}
154 
155 	INSERT_OBJECT_OID(t, &trapsink_list);
156 
157 	tdep->rb |= TDEP_CREATE;
158 
159 	return (SNMP_ERR_NOERROR);
160 }
161 
162 static void
163 trapsink_free(struct trapsink *t)
164 {
165 	TAILQ_REMOVE(&trapsink_list, t, link);
166 	if (t->socket != -1)
167 		(void)close(t->socket);
168 	free(t);
169 }
170 
171 static int
172 trapsink_modify(struct trapsink *t, struct trapsink_dep *tdep)
173 {
174 	tdep->rb_status = t->status;
175 	tdep->rb_version = t->version;
176 	strcpy(tdep->rb_comm, t->comm);
177 
178 	if (tdep->set & TDEP_STATUS) {
179 		/* if we are active and should move to not_in_service do
180 		 * this first */
181 		if (tdep->status == 2 && tdep->rb_status == TRAPSINK_ACTIVE) {
182 			t->status = TRAPSINK_NOT_IN_SERVICE;
183 			tdep->rb |= TDEP_MODIFY;
184 		}
185 	}
186 
187 	if (tdep->set & TDEP_VERSION)
188 		t->version = tdep->version;
189 	if (tdep->set & TDEP_COMM)
190 		strcpy(t->comm, tdep->comm);
191 
192 	if (tdep->set & TDEP_STATUS) {
193 		/* if we were inactive and should go active - do this now */
194 		if (tdep->status == 1 && tdep->rb_status != TRAPSINK_ACTIVE) {
195 			if (t->comm[0] == '\0') {
196 				t->status = tdep->rb_status;
197 				t->version = tdep->rb_version;
198 				strcpy(t->comm, tdep->rb_comm);
199 				return (SNMP_ERR_INCONS_VALUE);
200 			}
201 			t->status = TRAPSINK_ACTIVE;
202 			tdep->rb |= TDEP_MODIFY;
203 		}
204 	}
205 	return (SNMP_ERR_NOERROR);
206 }
207 
208 static int
209 trapsink_unmodify(struct trapsink *t, struct trapsink_dep *tdep)
210 {
211 	if (tdep->set & TDEP_STATUS)
212 		t->status = tdep->rb_status;
213 	if (tdep->set & TDEP_VERSION)
214 		t->version = tdep->rb_version;
215 	if (tdep->set & TDEP_COMM)
216 		strcpy(t->comm, tdep->rb_comm);
217 
218 	return (SNMP_ERR_NOERROR);
219 }
220 
221 static int
222 trapsink_destroy(struct snmp_context *ctx __unused, struct trapsink *t,
223     struct trapsink_dep *tdep)
224 {
225 	t->status = TRAPSINK_DESTROY;
226 	tdep->rb_status = t->status;
227 	tdep->rb |= TDEP_DESTROY;
228 	return (SNMP_ERR_NOERROR);
229 }
230 
231 static int
232 trapsink_undestroy(struct trapsink *t, struct trapsink_dep *tdep)
233 {
234 	t->status = tdep->rb_status;
235 	return (SNMP_ERR_NOERROR);
236 }
237 
238 static int
239 trapsink_dep(struct snmp_context *ctx, struct snmp_dependency *dep,
240     enum snmp_depop op)
241 {
242 	struct trapsink_dep *tdep = (struct trapsink_dep *)dep;
243 	struct trapsink *t;
244 
245 	t = FIND_OBJECT_OID(&trapsink_list, &dep->idx, 0);
246 
247 	switch (op) {
248 
249 	  case SNMP_DEPOP_COMMIT:
250 		if (tdep->set & TDEP_STATUS) {
251 			switch (tdep->status) {
252 
253 			  case 1:
254 			  case 2:
255 				if (t == NULL)
256 					return (SNMP_ERR_INCONS_VALUE);
257 				return (trapsink_modify(t, tdep));
258 
259 			  case 4:
260 			  case 5:
261 				if (t != NULL)
262 					return (SNMP_ERR_INCONS_VALUE);
263 				return (trapsink_create(tdep));
264 
265 			  case 6:
266 				if (t == NULL)
267 					return (SNMP_ERR_NOERROR);
268 				return (trapsink_destroy(ctx, t, tdep));
269 			}
270 		} else if (tdep->set != 0)
271 			return (trapsink_modify(t, tdep));
272 
273 		return (SNMP_ERR_NOERROR);
274 
275 	  case SNMP_DEPOP_ROLLBACK:
276 		if (tdep->rb & TDEP_CREATE) {
277 			trapsink_free(t);
278 			return (SNMP_ERR_NOERROR);
279 		}
280 		if (tdep->rb & TDEP_MODIFY)
281 			return (trapsink_unmodify(t, tdep));
282 		if(tdep->rb & TDEP_DESTROY)
283 			return (trapsink_undestroy(t, tdep));
284 		return (SNMP_ERR_NOERROR);
285 
286 	  case SNMP_DEPOP_FINISH:
287 		if ((tdep->rb & TDEP_DESTROY) && t != NULL &&
288 		    ctx->code == SNMP_RET_OK)
289 			trapsink_free(t);
290 		return (SNMP_ERR_NOERROR);
291 	}
292 	abort();
293 }
294 
295 int
296 op_trapsink(struct snmp_context *ctx, struct snmp_value *value,
297     u_int sub, u_int iidx, enum snmp_op op)
298 {
299 	struct trapsink *t;
300 	u_char ipa[4];
301 	int32_t port;
302 	struct asn_oid idx;
303 	struct trapsink_dep *tdep;
304 	u_char *p;
305 
306 	t = NULL;		/* gcc */
307 
308 	switch (op) {
309 
310 	  case SNMP_OP_GETNEXT:
311 		if ((t = NEXT_OBJECT_OID(&trapsink_list, &value->var, sub)) == NULL)
312 			return (SNMP_ERR_NOSUCHNAME);
313 		index_append(&value->var, sub, &t->index);
314 		break;
315 
316 	  case SNMP_OP_GET:
317 		if ((t = FIND_OBJECT_OID(&trapsink_list, &value->var, sub)) == NULL)
318 			return (SNMP_ERR_NOSUCHNAME);
319 		break;
320 
321 	  case SNMP_OP_SET:
322 		if (index_decode(&value->var, sub, iidx, ipa, &port) ||
323 		    port == 0 || port > 65535)
324 			return (SNMP_ERR_NO_CREATION);
325 		t = FIND_OBJECT_OID(&trapsink_list, &value->var, sub);
326 
327 		asn_slice_oid(&idx, &value->var, sub, value->var.len);
328 
329 		tdep = (struct trapsink_dep *)snmp_dep_lookup(ctx,
330 		    &oid_begemotTrapSinkTable, &idx,
331 		    sizeof(*tdep), trapsink_dep);
332 		if (tdep == NULL)
333 			return (SNMP_ERR_RES_UNAVAIL);
334 
335 		switch (value->var.subs[sub - 1]) {
336 
337 		  case LEAF_begemotTrapSinkStatus:
338 			if (tdep->set & TDEP_STATUS)
339 				return (SNMP_ERR_INCONS_VALUE);
340 			switch (value->v.integer) {
341 
342 			  case 1:
343 			  case 2:
344 				if (t == NULL)
345 					return (SNMP_ERR_INCONS_VALUE);
346 				break;
347 
348 			  case 4:
349 			  case 5:
350 				if (t != NULL)
351 					return (SNMP_ERR_INCONS_VALUE);
352 				break;
353 
354 			  case 6:
355 				break;
356 
357 			  default:
358 				return (SNMP_ERR_WRONG_VALUE);
359 			}
360 			tdep->status = value->v.integer;
361 			tdep->set |= TDEP_STATUS;
362 			return (SNMP_ERR_NOERROR);
363 
364 		  case LEAF_begemotTrapSinkComm:
365 			if (tdep->set & TDEP_COMM)
366 				return (SNMP_ERR_INCONS_VALUE);
367 			if (value->v.octetstring.len == 0 ||
368 			    value->v.octetstring.len > SNMP_COMMUNITY_MAXLEN)
369 				return (SNMP_ERR_WRONG_VALUE);
370 			for (p = value->v.octetstring.octets;
371 			     p < value->v.octetstring.octets + value->v.octetstring.len;
372 			     p++) {
373 				if (!isascii(*p) || !isprint(*p))
374 					return (SNMP_ERR_WRONG_VALUE);
375 			}
376 			tdep->set |= TDEP_COMM;
377 			strncpy(tdep->comm, value->v.octetstring.octets,
378 			    value->v.octetstring.len);
379 			tdep->comm[value->v.octetstring.len] = '\0';
380 			return (SNMP_ERR_NOERROR);
381 
382 		  case LEAF_begemotTrapSinkVersion:
383 			if (tdep->set & TDEP_VERSION)
384 				return (SNMP_ERR_INCONS_VALUE);
385 			if (value->v.integer != TRAPSINK_V1 &&
386 			    value->v.integer != TRAPSINK_V2)
387 				return (SNMP_ERR_WRONG_VALUE);
388 			tdep->version = value->v.integer;
389 			tdep->set |= TDEP_VERSION;
390 			return (SNMP_ERR_NOERROR);
391 		}
392 		if (t == NULL)
393 			return (SNMP_ERR_INCONS_NAME);
394 		else
395 			return (SNMP_ERR_NOT_WRITEABLE);
396 
397 
398 	  case SNMP_OP_ROLLBACK:
399 	  case SNMP_OP_COMMIT:
400 		return (SNMP_ERR_NOERROR);
401 	}
402 
403 	switch (value->var.subs[sub - 1]) {
404 
405 	  case LEAF_begemotTrapSinkStatus:
406 		value->v.integer = t->status;
407 		break;
408 
409 	  case LEAF_begemotTrapSinkComm:
410 		return (string_get(value, t->comm, -1));
411 
412 	  case LEAF_begemotTrapSinkVersion:
413 		value->v.integer = t->version;
414 		break;
415 
416 	}
417 	return (SNMP_ERR_NOERROR);
418 }
419 
420 static void
421 snmp_create_v1_trap(struct snmp_pdu *pdu, char *com,
422     const struct asn_oid *trap_oid)
423 {
424 	memset(pdu, 0, sizeof(*pdu));
425 	strcpy(pdu->community, com);
426 
427 	pdu->version = SNMP_V1;
428 	pdu->type = SNMP_PDU_TRAP;
429 	pdu->enterprise = systemg.object_id;
430 	memcpy(pdu->agent_addr, snmpd.trap1addr, 4);
431 	pdu->generic_trap = trap_oid->subs[trap_oid->len - 1] - 1;
432 	pdu->specific_trap = 0;
433 	pdu->time_stamp = get_ticks() - start_tick;
434 	pdu->nbindings = 0;
435 }
436 
437 static void
438 snmp_create_v2_trap(struct snmp_pdu *pdu, char *com,
439     const struct asn_oid *trap_oid)
440 {
441 	memset(pdu, 0, sizeof(*pdu));
442 	strcpy(pdu->community, com);
443 
444 	pdu->version = SNMP_V2c;
445 	pdu->type = SNMP_PDU_TRAP2;
446 	pdu->request_id = reqid_next(trap_reqid);
447 	pdu->error_index = 0;
448 	pdu->error_status = SNMP_ERR_NOERROR;
449 
450 	pdu->bindings[0].var = oid_sysUpTime;
451 	pdu->bindings[0].var.subs[pdu->bindings[0].var.len++] = 0;
452 	pdu->bindings[0].syntax = SNMP_SYNTAX_TIMETICKS;
453 	pdu->bindings[0].v.uint32 = get_ticks() - start_tick;
454 
455 	pdu->bindings[1].var = oid_snmpTrapOID;
456 	pdu->bindings[1].var.subs[pdu->bindings[1].var.len++] = 0;
457 	pdu->bindings[1].syntax = SNMP_SYNTAX_OID;
458 	pdu->bindings[1].v.oid = *trap_oid;
459 
460 	pdu->nbindings = 2;
461 }
462 
463 static void
464 snmp_create_v3_trap(struct snmp_pdu *pdu, struct target_param *target,
465     const struct asn_oid *trap_oid)
466 {
467 	uint64_t etime;
468 	struct usm_user *usmuser;
469 
470 	memset(pdu, 0, sizeof(*pdu));
471 
472 	pdu->version = SNMP_V3;
473 	pdu->type = SNMP_PDU_TRAP2;
474 	pdu->request_id = reqid_next(trap_reqid);
475 	pdu->error_index = 0;
476 	pdu->error_status = SNMP_ERR_NOERROR;
477 
478 	pdu->bindings[0].var = oid_sysUpTime;
479 	pdu->bindings[0].var.subs[pdu->bindings[0].var.len++] = 0;
480 	pdu->bindings[0].syntax = SNMP_SYNTAX_TIMETICKS;
481 	pdu->bindings[0].v.uint32 = get_ticks() - start_tick;
482 
483 	pdu->bindings[1].var = oid_snmpTrapOID;
484 	pdu->bindings[1].var.subs[pdu->bindings[1].var.len++] = 0;
485 	pdu->bindings[1].syntax = SNMP_SYNTAX_OID;
486 	pdu->bindings[1].v.oid = *trap_oid;
487 
488 	pdu->nbindings = 2;
489 
490 	etime = (get_ticks() - start_tick)  / 100ULL;
491 	if (etime < INT32_MAX)
492 		snmpd_engine.engine_time = etime;
493 	else {
494 		start_tick = get_ticks();
495 		set_snmpd_engine();
496 		snmpd_engine.engine_time = start_tick;
497 	}
498 
499 	memcpy(pdu->engine.engine_id, snmpd_engine.engine_id,
500 	    snmpd_engine.engine_len);
501 	pdu->engine.engine_len = snmpd_engine.engine_len;
502 	pdu->engine.engine_boots = snmpd_engine.engine_boots;
503 	pdu->engine.engine_time = snmpd_engine.engine_time;
504 	pdu->engine.max_msg_size = snmpd_engine.max_msg_size;
505 	strlcpy(pdu->user.sec_name, target->secname,
506 	    sizeof(pdu->user.sec_name));
507 	pdu->security_model = target->sec_model;
508 
509 	pdu->context_engine_len = snmpd_engine.engine_len;
510 	memcpy(pdu->context_engine, snmpd_engine.engine_id,
511 	    snmpd_engine.engine_len);
512 
513 	if (target->sec_model == SNMP_SECMODEL_USM &&
514 	    target->sec_level != SNMP_noAuthNoPriv) {
515 	    	usmuser = usm_find_user(pdu->engine.engine_id,
516 	    	   pdu->engine.engine_len, pdu->user.sec_name);
517 		if (usmuser != NULL) {
518 			pdu->user.auth_proto = usmuser->suser.auth_proto;
519 			pdu->user.priv_proto = usmuser->suser.priv_proto;
520 			memcpy(pdu->user.auth_key, usmuser->suser.auth_key,
521 			    sizeof(pdu->user.auth_key));
522 			memcpy(pdu->user.priv_key, usmuser->suser.priv_key,
523 			    sizeof(pdu->user.priv_key));
524 		}
525 		snmp_pdu_init_secparams(pdu);
526 	}
527 }
528 
529 void
530 snmp_send_trap(const struct asn_oid *trap_oid, ...)
531 {
532 	struct snmp_pdu pdu;
533 	struct trapsink *t;
534 	const struct snmp_value *v;
535 	struct target_notify *n;
536 	struct target_address *ta;
537 	struct target_param *tp;
538 
539 	va_list ap;
540 	u_char *sndbuf;
541 	char *tag;
542 	size_t sndlen;
543 	ssize_t len;
544 	int32_t ip;
545 
546 	TAILQ_FOREACH(t, &trapsink_list, link) {
547 		if (t->status != TRAPSINK_ACTIVE)
548 			continue;
549 
550 		if (t->version == TRAPSINK_V1)
551 			snmp_create_v1_trap(&pdu, t->comm, trap_oid);
552 		else
553 			snmp_create_v2_trap(&pdu, t->comm, trap_oid);
554 
555 		va_start(ap, trap_oid);
556 		while ((v = va_arg(ap, const struct snmp_value *)) != NULL)
557 			pdu.bindings[pdu.nbindings++] = *v;
558 		va_end(ap);
559 
560 		if (snmp_pdu_auth_access(&pdu, &ip) != SNMP_CODE_OK) {
561 			syslog(LOG_DEBUG, "send trap to %s failed: no access",
562 			    t->comm);
563 			continue;
564 		}
565 
566 		if ((sndbuf = buf_alloc(1)) == NULL) {
567 			syslog(LOG_ERR, "trap send buffer: %m");
568 			return;
569 		}
570 
571 		snmp_output(&pdu, sndbuf, &sndlen, "TRAP");
572 
573 		if ((len = send(t->socket, sndbuf, sndlen, 0)) == -1)
574 			syslog(LOG_ERR, "send: %m");
575 		else if ((size_t)len != sndlen)
576 			syslog(LOG_ERR, "send: short write %zu/%zu",
577 			    sndlen, (size_t)len);
578 
579 		free(sndbuf);
580 	}
581 
582 	SLIST_FOREACH(n, &target_notifylist, tn) {
583 		if (n->status != RowStatus_active || n->taglist[0] == '\0')
584 			continue;
585 
586 		SLIST_FOREACH(ta, &target_addresslist, ta)
587 			if ((tag = strstr(ta->taglist, n->taglist)) != NULL  &&
588 			    (tag[strlen(n->taglist)] == ' ' ||
589 			     tag[strlen(n->taglist)] == '\0' ||
590 			     tag[strlen(n->taglist)] == '\t' ||
591 			     tag[strlen(n->taglist)] == '\r' ||
592 			     tag[strlen(n->taglist)] == '\n') &&
593 			     ta->status == RowStatus_active)
594 				break;
595 		if (ta == NULL)
596 			continue;
597 
598 		SLIST_FOREACH(tp, &target_paramlist, tp)
599 			if (strcmp(tp->name, ta->paramname) == 0 &&
600 			    tp->status == 1)
601 				break;
602 		if (tp == NULL)
603 			continue;
604 
605 		switch (tp->mpmodel) {
606 		case SNMP_MPM_SNMP_V1:
607 			snmp_create_v1_trap(&pdu, tp->secname, trap_oid);
608 			break;
609 
610 		case SNMP_MPM_SNMP_V2c:
611 			snmp_create_v2_trap(&pdu, tp->secname, trap_oid);
612 			break;
613 
614 		case SNMP_MPM_SNMP_V3:
615 			snmp_create_v3_trap(&pdu, tp, trap_oid);
616 			break;
617 
618 		default:
619 			continue;
620 		}
621 
622 		va_start(ap, trap_oid);
623 		while ((v = va_arg(ap, const struct snmp_value *)) != NULL)
624 			pdu.bindings[pdu.nbindings++] = *v;
625 		va_end(ap);
626 
627 		if (snmp_pdu_auth_access(&pdu, &ip) != SNMP_CODE_OK) {
628 			syslog(LOG_DEBUG, "send trap to %s failed: no access",
629 			    t->comm);
630 			continue;
631 		}
632 
633 		if ((sndbuf = buf_alloc(1)) == NULL) {
634 			syslog(LOG_ERR, "trap send buffer: %m");
635 			return;
636 		}
637 
638 		snmp_output(&pdu, sndbuf, &sndlen, "TRAP");
639 
640 		if ((len = send(ta->socket, sndbuf, sndlen, 0)) == -1)
641 			syslog(LOG_ERR, "send: %m");
642 		else if ((size_t)len != sndlen)
643 			syslog(LOG_ERR, "send: short write %zu/%zu",
644 			    sndlen, (size_t)len);
645 
646 		free(sndbuf);
647 	}
648 }
649 
650 /*
651  * RFC 3413 SNMP Management Target MIB
652  */
653 struct snmpd_target_stats *
654 bsnmpd_get_target_stats(void)
655 {
656 	return (&snmpd_target_stats);
657 }
658 
659 struct target_address *
660 target_first_address(void)
661 {
662 	return (SLIST_FIRST(&target_addresslist));
663 }
664 
665 struct target_address *
666 target_next_address(struct target_address *addrs)
667 {
668 	if (addrs == NULL)
669 		return (NULL);
670 
671 	return (SLIST_NEXT(addrs, ta));
672 }
673 
674 struct target_address *
675 target_new_address(char *aname)
676 {
677 	int cmp;
678 	struct target_address *addrs, *temp, *prev;
679 
680 	SLIST_FOREACH(addrs, &target_addresslist, ta)
681 		if (strcmp(aname, addrs->name) == 0)
682 			return (NULL);
683 
684 	if ((addrs = (struct target_address *)malloc(sizeof(*addrs))) == NULL)
685 		return (NULL);
686 
687 	memset(addrs, 0, sizeof(*addrs));
688 	strlcpy(addrs->name, aname, sizeof(addrs->name));
689 	addrs->timeout = 150;
690 	addrs->retry = 3; /* XXX */
691 
692 	if ((prev = SLIST_FIRST(&target_addresslist)) == NULL ||
693 	    strcmp(aname, prev->name) < 0) {
694 		SLIST_INSERT_HEAD(&target_addresslist, addrs, ta);
695 		return (addrs);
696 	}
697 
698 	SLIST_FOREACH(temp, &target_addresslist, ta) {
699 		if ((cmp = strcmp(aname, temp->name)) <= 0)
700 			break;
701 		prev = temp;
702 	}
703 
704 	if (temp == NULL || cmp < 0)
705 		SLIST_INSERT_AFTER(prev, addrs, ta);
706 	else if (cmp > 0)
707 		SLIST_INSERT_AFTER(temp, addrs, ta);
708 	else {
709 		syslog(LOG_ERR, "Target address %s exists", addrs->name);
710 		free(addrs);
711 		return (NULL);
712 	}
713 
714 	return (addrs);
715 }
716 
717 int
718 target_activate_address(struct target_address *addrs)
719 {
720 	struct sockaddr_in sa;
721 
722 	if ((addrs->socket = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
723 		syslog(LOG_ERR, "socket(UDP): %m");
724 		return (SNMP_ERR_RES_UNAVAIL);
725 	}
726 
727 	(void)shutdown(addrs->socket, SHUT_RD);
728 	sa.sin_len = sizeof(sa);
729 	sa.sin_family = AF_INET;
730 
731 	sa.sin_addr.s_addr = htonl((addrs->address[0] << 24) |
732 	    (addrs->address[1] << 16) | (addrs->address[2] << 8) |
733 	    (addrs->address[3] << 0));
734 	sa.sin_port = htons(addrs->address[4]) << 8 |
735 	     htons(addrs->address[5]) << 0;
736 
737 	if (connect(addrs->socket, (struct sockaddr *)&sa, sa.sin_len) == -1) {
738 		syslog(LOG_ERR, "connect(%s,%u): %m",
739 		    inet_ntoa(sa.sin_addr), ntohs(sa.sin_port));
740 		(void)close(addrs->socket);
741 		return (SNMP_ERR_GENERR);
742 	}
743 
744 	addrs->status = RowStatus_active;
745 
746 	return (SNMP_ERR_NOERROR);
747 }
748 
749 int
750 target_delete_address(struct target_address *addrs)
751 {
752 	SLIST_REMOVE(&target_addresslist, addrs, target_address, ta);
753 	if (addrs->status == RowStatus_active)
754 		close(addrs->socket);
755 	free(addrs);
756 
757 	return (0);
758 }
759 
760 struct target_param *
761 target_first_param(void)
762 {
763 	return (SLIST_FIRST(&target_paramlist));
764 }
765 
766 struct target_param *
767 target_next_param(struct target_param *param)
768 {
769 	if (param == NULL)
770 		return (NULL);
771 
772 	return (SLIST_NEXT(param, tp));
773 }
774 
775 struct target_param *
776 target_new_param(char *pname)
777 {
778 	int cmp;
779 	struct target_param *param, *temp, *prev;
780 
781 	SLIST_FOREACH(param, &target_paramlist, tp)
782 		if (strcmp(pname, param->name) == 0)
783 			return (NULL);
784 
785 	if ((param = (struct target_param *)malloc(sizeof(*param))) == NULL)
786 		return (NULL);
787 
788 	memset(param, 0, sizeof(*param));
789 	strlcpy(param->name, pname, sizeof(param->name));
790 
791 	if ((prev = SLIST_FIRST(&target_paramlist)) == NULL ||
792 	    strcmp(pname, prev->name) < 0) {
793 		SLIST_INSERT_HEAD(&target_paramlist, param, tp);
794 		return (param);
795 	}
796 
797 	SLIST_FOREACH(temp, &target_paramlist, tp) {
798 		if ((cmp = strcmp(pname, temp->name)) <= 0)
799 			break;
800 		prev = temp;
801 	}
802 
803 	if (temp == NULL || cmp < 0)
804 		SLIST_INSERT_AFTER(prev, param, tp);
805 	else if (cmp > 0)
806 		SLIST_INSERT_AFTER(temp, param, tp);
807 	else {
808 		syslog(LOG_ERR, "Target parameter %s exists", param->name);
809 		free(param);
810 		return (NULL);
811 	}
812 
813 	return (param);
814 }
815 
816 int
817 target_delete_param(struct target_param *param)
818 {
819 	SLIST_REMOVE(&target_paramlist, param, target_param, tp);
820 	free(param);
821 
822 	return (0);
823 }
824 
825 struct target_notify *
826 target_first_notify(void)
827 {
828 	return (SLIST_FIRST(&target_notifylist));
829 }
830 
831 struct target_notify *
832 target_next_notify(struct target_notify *notify)
833 {
834 	if (notify == NULL)
835 		return (NULL);
836 
837 	return (SLIST_NEXT(notify, tn));
838 }
839 
840 struct target_notify *
841 target_new_notify(char *nname)
842 {
843 	int cmp;
844 	struct target_notify *notify, *temp, *prev;
845 
846 	SLIST_FOREACH(notify, &target_notifylist, tn)
847 		if (strcmp(nname, notify->name) == 0)
848 			return (NULL);
849 
850 	if ((notify = (struct target_notify *)malloc(sizeof(*notify))) == NULL)
851 		return (NULL);
852 
853 	memset(notify, 0, sizeof(*notify));
854 	strlcpy(notify->name, nname, sizeof(notify->name));
855 
856 	if ((prev = SLIST_FIRST(&target_notifylist)) == NULL ||
857 	    strcmp(nname, prev->name) < 0) {
858 		SLIST_INSERT_HEAD(&target_notifylist, notify, tn);
859 		return (notify);
860 	}
861 
862 	SLIST_FOREACH(temp, &target_notifylist, tn) {
863 		if ((cmp = strcmp(nname, temp->name)) <= 0)
864 			break;
865 		prev = temp;
866 	}
867 
868 	if (temp == NULL || cmp < 0)
869 		SLIST_INSERT_AFTER(prev, notify, tn);
870 	else if (cmp > 0)
871 		SLIST_INSERT_AFTER(temp, notify, tn);
872 	else {
873 		syslog(LOG_ERR, "Notification target %s exists", notify->name);
874 		free(notify);
875 		return (NULL);
876 	}
877 
878 	return (notify);
879 }
880 
881 int
882 target_delete_notify(struct target_notify *notify)
883 {
884 	SLIST_REMOVE(&target_notifylist, notify, target_notify, tn);
885 	free(notify);
886 
887 	return (0);
888 }
889 
890 void
891 target_flush_all(void)
892 {
893 	struct target_address *addrs;
894 	struct target_param *param;
895 	struct target_notify *notify;
896 
897 	while ((addrs = SLIST_FIRST(&target_addresslist)) != NULL) {
898 		SLIST_REMOVE_HEAD(&target_addresslist, ta);
899 		if (addrs->status == RowStatus_active)
900 			close(addrs->socket);
901 		free(addrs);
902 	}
903 	SLIST_INIT(&target_addresslist);
904 
905 	while ((param = SLIST_FIRST(&target_paramlist)) != NULL) {
906 		SLIST_REMOVE_HEAD(&target_paramlist, tp);
907 		free(param);
908 	}
909 	SLIST_INIT(&target_paramlist);
910 
911 	while ((notify = SLIST_FIRST(&target_notifylist)) != NULL) {
912 		SLIST_REMOVE_HEAD(&target_notifylist, tn);
913 		free(notify);
914 	}
915 	SLIST_INIT(&target_notifylist);
916 }
917