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