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
62struct trapsink_list trapsink_list = TAILQ_HEAD_INITIALIZER(trapsink_list);
63
64/* List of target addresses */
65static struct target_addresslist target_addresslist =
66    SLIST_HEAD_INITIALIZER(target_addresslist);
67
68/* List of target parameters */
69static struct target_paramlist target_paramlist =
70    SLIST_HEAD_INITIALIZER(target_paramlist);
71
72/* List of notification targets */
73static struct target_notifylist target_notifylist =
74    SLIST_HEAD_INITIALIZER(target_notifylist);
75
76static const struct asn_oid oid_begemotTrapSinkTable =
77    OIDX_begemotTrapSinkTable;
78static const struct asn_oid oid_sysUpTime = OIDX_sysUpTime;
79static const struct asn_oid oid_snmpTrapOID = OIDX_snmpTrapOID;
80
81struct 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};
92enum {
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
102static int
103trapsink_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
164static void
165trapsink_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
173static int
174trapsink_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
210static int
211trapsink_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
223static int
224trapsink_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
233static int
234trapsink_undestroy(struct trapsink *t, struct trapsink_dep *tdep)
235{
236	t->status = tdep->rb_status;
237	return (SNMP_ERR_NOERROR);
238}
239
240static int
241trapsink_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
297int
298op_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
422static void
423snmp_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
439static void
440snmp_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
465static void
466snmp_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
523void
524snmp_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 */
647struct snmpd_target_stats *
648bsnmpd_get_target_stats(void)
649{
650	return (&snmpd_target_stats);
651}
652
653struct target_address *
654target_first_address(void)
655{
656	return (SLIST_FIRST(&target_addresslist));
657}
658
659struct target_address *
660target_next_address(struct target_address *addrs)
661{
662	if (addrs == NULL)
663		return (NULL);
664
665	return (SLIST_NEXT(addrs, ta));
666}
667
668struct target_address *
669target_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
711int
712target_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
743int
744target_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
754struct target_param *
755target_first_param(void)
756{
757	return (SLIST_FIRST(&target_paramlist));
758}
759
760struct target_param *
761target_next_param(struct target_param *param)
762{
763	if (param == NULL)
764		return (NULL);
765
766	return (SLIST_NEXT(param, tp));
767}
768
769struct target_param *
770target_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
810int
811target_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
819struct target_notify *
820target_first_notify(void)
821{
822	return (SLIST_FIRST(&target_notifylist));
823}
824
825struct target_notify *
826target_next_notify(struct target_notify *notify)
827{
828	if (notify == NULL)
829		return (NULL);
830
831	return (SLIST_NEXT(notify, tn));
832}
833
834struct target_notify *
835target_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
875int
876target_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
884void
885target_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