1122394Sharti/*
2122394Sharti * Copyright (c) 2001-2003
3122394Sharti *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4122394Sharti *	All rights reserved.
5122394Sharti *
6122394Sharti * Author: Harti Brandt <harti@freebsd.org>
7216594Ssyrinx *
8216594Ssyrinx * Copyright (c) 2010 The FreeBSD Foundation
9216594Ssyrinx * All rights reserved.
10216594Ssyrinx *
11216594Ssyrinx * Portions of this software were developed by Shteryana Sotirova Shopova
12216594Ssyrinx * under sponsorship from the FreeBSD Foundation.
13216594Ssyrinx *
14133211Sharti * Redistribution and use in source and binary forms, with or without
15133211Sharti * modification, are permitted provided that the following conditions
16133211Sharti * are met:
17133211Sharti * 1. Redistributions of source code must retain the above copyright
18133211Sharti *    notice, this list of conditions and the following disclaimer.
19122394Sharti * 2. Redistributions in binary form must reproduce the above copyright
20122394Sharti *    notice, this list of conditions and the following disclaimer in the
21122394Sharti *    documentation and/or other materials provided with the distribution.
22310901Sngie *
23133211Sharti * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24133211Sharti * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25133211Sharti * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26133211Sharti * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
27133211Sharti * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28133211Sharti * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29133211Sharti * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30133211Sharti * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31133211Sharti * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32133211Sharti * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33133211Sharti * SUCH DAMAGE.
34122394Sharti *
35150920Sharti * $Begemot: bsnmp/snmpd/trap.c,v 1.9 2005/10/04 11:21:39 brandt_h Exp $
36122394Sharti *
37122394Sharti * TrapSinkTable
38122394Sharti */
39122394Sharti#include <sys/types.h>
40216294Ssyrinx#include <sys/queue.h>
41122394Sharti#include <sys/sysctl.h>
42122394Sharti#include <sys/un.h>
43216594Ssyrinx#include <stdint.h>
44122394Sharti#include <stdio.h>
45122394Sharti#include <stdlib.h>
46122394Sharti#include <stdarg.h>
47122394Sharti#include <stdarg.h>
48122394Sharti#include <string.h>
49122394Sharti#include <ctype.h>
50122394Sharti#include <syslog.h>
51122394Sharti#include <unistd.h>
52122394Sharti#include <netinet/in.h>
53122394Sharti#include <arpa/inet.h>
54122394Sharti
55122394Sharti#include "snmpmod.h"
56122394Sharti#include "snmpd.h"
57122394Sharti#include "tree.h"
58122394Sharti#include "oid.h"
59122394Sharti
60122394Shartistruct trapsink_list trapsink_list = TAILQ_HEAD_INITIALIZER(trapsink_list);
61122394Sharti
62216594Ssyrinx/* List of target addresses */
63292815Sngiestatic struct target_addresslist target_addresslist =
64216594Ssyrinx    SLIST_HEAD_INITIALIZER(target_addresslist);
65216594Ssyrinx
66216594Ssyrinx/* List of target parameters */
67292815Sngiestatic struct target_paramlist target_paramlist =
68216594Ssyrinx    SLIST_HEAD_INITIALIZER(target_paramlist);
69216594Ssyrinx
70216594Ssyrinx/* List of notification targets */
71292815Sngiestatic struct target_notifylist target_notifylist =
72216594Ssyrinx    SLIST_HEAD_INITIALIZER(target_notifylist);
73216594Ssyrinx
74122394Shartistatic const struct asn_oid oid_begemotTrapSinkTable =
75122394Sharti    OIDX_begemotTrapSinkTable;
76122394Shartistatic const struct asn_oid oid_sysUpTime = OIDX_sysUpTime;
77122394Shartistatic const struct asn_oid oid_snmpTrapOID = OIDX_snmpTrapOID;
78122394Sharti
79122394Shartistruct trapsink_dep {
80122394Sharti	struct snmp_dependency dep;
81122394Sharti	u_int	set;
82122394Sharti	u_int	status;
83122394Sharti	u_char	comm[SNMP_COMMUNITY_MAXLEN + 1];
84122394Sharti	u_int	version;
85122394Sharti	u_int	rb;
86122394Sharti	u_int	rb_status;
87122394Sharti	u_int	rb_version;
88122394Sharti	u_char	rb_comm[SNMP_COMMUNITY_MAXLEN + 1];
89122394Sharti};
90122394Shartienum {
91122394Sharti	TDEP_STATUS	= 0x0001,
92122394Sharti	TDEP_COMM	= 0x0002,
93122394Sharti	TDEP_VERSION	= 0x0004,
94122394Sharti
95122394Sharti	TDEP_CREATE	= 0x0001,
96122394Sharti	TDEP_MODIFY	= 0x0002,
97122394Sharti	TDEP_DESTROY	= 0x0004,
98122394Sharti};
99122394Sharti
100122394Shartistatic int
101122394Shartitrapsink_create(struct trapsink_dep *tdep)
102122394Sharti{
103122394Sharti	struct trapsink *t;
104122394Sharti	struct sockaddr_in sa;
105122394Sharti
106122394Sharti	if ((t = malloc(sizeof(*t))) == NULL)
107122394Sharti		return (SNMP_ERR_RES_UNAVAIL);
108122394Sharti
109122394Sharti	t->index = tdep->dep.idx;
110122394Sharti	t->status = TRAPSINK_NOT_READY;
111122394Sharti	t->comm[0] = '\0';
112122394Sharti	t->version = TRAPSINK_V2;
113122394Sharti
114122394Sharti	if ((t->socket = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
115122394Sharti		syslog(LOG_ERR, "socket(UDP): %m");
116122394Sharti		free(t);
117122394Sharti		return (SNMP_ERR_RES_UNAVAIL);
118122394Sharti	}
119122394Sharti	(void)shutdown(t->socket, SHUT_RD);
120240191Skevlo	memset(&sa, 0, sizeof(sa));
121122394Sharti	sa.sin_len = sizeof(sa);
122122394Sharti	sa.sin_family = AF_INET;
123122394Sharti	sa.sin_addr.s_addr = htonl((t->index.subs[0] << 24) |
124122394Sharti	    (t->index.subs[1] << 16) | (t->index.subs[2] << 8) |
125122394Sharti	    (t->index.subs[3] << 0));
126122394Sharti	sa.sin_port = htons(t->index.subs[4]);
127122394Sharti
128122394Sharti	if (connect(t->socket, (struct sockaddr *)&sa, sa.sin_len) == -1) {
129122394Sharti		syslog(LOG_ERR, "connect(%s,%u): %m",
130150920Sharti		    inet_ntoa(sa.sin_addr), ntohs(sa.sin_port));
131122394Sharti		(void)close(t->socket);
132122394Sharti		free(t);
133122394Sharti		return (SNMP_ERR_GENERR);
134122394Sharti	}
135122394Sharti
136122394Sharti	if (tdep->set & TDEP_VERSION)
137122394Sharti		t->version = tdep->version;
138122394Sharti	if (tdep->set & TDEP_COMM)
139122394Sharti		strcpy(t->comm, tdep->comm);
140122394Sharti
141122394Sharti	if (t->comm[0] != '\0')
142122394Sharti		t->status = TRAPSINK_NOT_IN_SERVICE;
143122394Sharti
144122394Sharti	/* look whether we should activate */
145122394Sharti	if (tdep->status == 4) {
146122394Sharti		if (t->status == TRAPSINK_NOT_READY) {
147122394Sharti			if (t->socket != -1)
148122394Sharti				(void)close(t->socket);
149122394Sharti			free(t);
150122394Sharti			return (SNMP_ERR_INCONS_VALUE);
151122394Sharti		}
152122394Sharti		t->status = TRAPSINK_ACTIVE;
153122394Sharti	}
154122394Sharti
155122394Sharti	INSERT_OBJECT_OID(t, &trapsink_list);
156122394Sharti
157122394Sharti	tdep->rb |= TDEP_CREATE;
158122394Sharti
159122394Sharti	return (SNMP_ERR_NOERROR);
160122394Sharti}
161122394Sharti
162122394Shartistatic void
163122394Shartitrapsink_free(struct trapsink *t)
164122394Sharti{
165122394Sharti	TAILQ_REMOVE(&trapsink_list, t, link);
166122394Sharti	if (t->socket != -1)
167122394Sharti		(void)close(t->socket);
168122394Sharti	free(t);
169122394Sharti}
170122394Sharti
171122394Shartistatic int
172122394Shartitrapsink_modify(struct trapsink *t, struct trapsink_dep *tdep)
173122394Sharti{
174122394Sharti	tdep->rb_status = t->status;
175122394Sharti	tdep->rb_version = t->version;
176122394Sharti	strcpy(tdep->rb_comm, t->comm);
177122394Sharti
178122394Sharti	if (tdep->set & TDEP_STATUS) {
179122394Sharti		/* if we are active and should move to not_in_service do
180122394Sharti		 * this first */
181122394Sharti		if (tdep->status == 2 && tdep->rb_status == TRAPSINK_ACTIVE) {
182122394Sharti			t->status = TRAPSINK_NOT_IN_SERVICE;
183122394Sharti			tdep->rb |= TDEP_MODIFY;
184122394Sharti		}
185122394Sharti	}
186122394Sharti
187122394Sharti	if (tdep->set & TDEP_VERSION)
188122394Sharti		t->version = tdep->version;
189122394Sharti	if (tdep->set & TDEP_COMM)
190122394Sharti		strcpy(t->comm, tdep->comm);
191122394Sharti
192122394Sharti	if (tdep->set & TDEP_STATUS) {
193122394Sharti		/* if we were inactive and should go active - do this now */
194122394Sharti		if (tdep->status == 1 && tdep->rb_status != TRAPSINK_ACTIVE) {
195122394Sharti			if (t->comm[0] == '\0') {
196122394Sharti				t->status = tdep->rb_status;
197122394Sharti				t->version = tdep->rb_version;
198122394Sharti				strcpy(t->comm, tdep->rb_comm);
199122394Sharti				return (SNMP_ERR_INCONS_VALUE);
200122394Sharti			}
201122394Sharti			t->status = TRAPSINK_ACTIVE;
202122394Sharti			tdep->rb |= TDEP_MODIFY;
203122394Sharti		}
204122394Sharti	}
205122394Sharti	return (SNMP_ERR_NOERROR);
206122394Sharti}
207122394Sharti
208122394Shartistatic int
209122394Shartitrapsink_unmodify(struct trapsink *t, struct trapsink_dep *tdep)
210122394Sharti{
211122394Sharti	if (tdep->set & TDEP_STATUS)
212122394Sharti		t->status = tdep->rb_status;
213122394Sharti	if (tdep->set & TDEP_VERSION)
214122394Sharti		t->version = tdep->rb_version;
215122394Sharti	if (tdep->set & TDEP_COMM)
216122394Sharti		strcpy(t->comm, tdep->rb_comm);
217310730Sngie
218122394Sharti	return (SNMP_ERR_NOERROR);
219122394Sharti}
220122394Sharti
221122394Shartistatic int
222128237Shartitrapsink_destroy(struct snmp_context *ctx __unused, struct trapsink *t,
223122394Sharti    struct trapsink_dep *tdep)
224122394Sharti{
225122394Sharti	t->status = TRAPSINK_DESTROY;
226122394Sharti	tdep->rb_status = t->status;
227122394Sharti	tdep->rb |= TDEP_DESTROY;
228122394Sharti	return (SNMP_ERR_NOERROR);
229122394Sharti}
230122394Sharti
231122394Shartistatic int
232122394Shartitrapsink_undestroy(struct trapsink *t, struct trapsink_dep *tdep)
233122394Sharti{
234122394Sharti	t->status = tdep->rb_status;
235122394Sharti	return (SNMP_ERR_NOERROR);
236122394Sharti}
237122394Sharti
238122394Shartistatic int
239122394Shartitrapsink_dep(struct snmp_context *ctx, struct snmp_dependency *dep,
240122394Sharti    enum snmp_depop op)
241122394Sharti{
242122394Sharti	struct trapsink_dep *tdep = (struct trapsink_dep *)dep;
243122394Sharti	struct trapsink *t;
244122394Sharti
245122394Sharti	t = FIND_OBJECT_OID(&trapsink_list, &dep->idx, 0);
246122394Sharti
247122394Sharti	switch (op) {
248122394Sharti
249122394Sharti	  case SNMP_DEPOP_COMMIT:
250122394Sharti		if (tdep->set & TDEP_STATUS) {
251122394Sharti			switch (tdep->status) {
252122394Sharti
253122394Sharti			  case 1:
254122394Sharti			  case 2:
255122394Sharti				if (t == NULL)
256122394Sharti					return (SNMP_ERR_INCONS_VALUE);
257122394Sharti				return (trapsink_modify(t, tdep));
258122394Sharti
259122394Sharti			  case 4:
260122394Sharti			  case 5:
261122394Sharti				if (t != NULL)
262122394Sharti					return (SNMP_ERR_INCONS_VALUE);
263122394Sharti				return (trapsink_create(tdep));
264122394Sharti
265122394Sharti			  case 6:
266122394Sharti				if (t == NULL)
267122394Sharti					return (SNMP_ERR_NOERROR);
268122394Sharti				return (trapsink_destroy(ctx, t, tdep));
269122394Sharti			}
270122394Sharti		} else if (tdep->set != 0)
271122394Sharti			return (trapsink_modify(t, tdep));
272122394Sharti
273122394Sharti		return (SNMP_ERR_NOERROR);
274122394Sharti
275122394Sharti	  case SNMP_DEPOP_ROLLBACK:
276122394Sharti		if (tdep->rb & TDEP_CREATE) {
277122394Sharti			trapsink_free(t);
278122394Sharti			return (SNMP_ERR_NOERROR);
279122394Sharti		}
280122394Sharti		if (tdep->rb & TDEP_MODIFY)
281122394Sharti			return (trapsink_unmodify(t, tdep));
282122394Sharti		if(tdep->rb & TDEP_DESTROY)
283122394Sharti			return (trapsink_undestroy(t, tdep));
284122394Sharti		return (SNMP_ERR_NOERROR);
285128237Sharti
286128237Sharti	  case SNMP_DEPOP_FINISH:
287128237Sharti		if ((tdep->rb & TDEP_DESTROY) && t != NULL &&
288128237Sharti		    ctx->code == SNMP_RET_OK)
289128237Sharti			trapsink_free(t);
290128237Sharti		return (SNMP_ERR_NOERROR);
291122394Sharti	}
292122394Sharti	abort();
293122394Sharti}
294122394Sharti
295122394Shartiint
296122394Shartiop_trapsink(struct snmp_context *ctx, struct snmp_value *value,
297122394Sharti    u_int sub, u_int iidx, enum snmp_op op)
298122394Sharti{
299122394Sharti	struct trapsink *t;
300122394Sharti	u_char ipa[4];
301122394Sharti	int32_t port;
302122394Sharti	struct asn_oid idx;
303122394Sharti	struct trapsink_dep *tdep;
304122394Sharti	u_char *p;
305122394Sharti
306122394Sharti	t = NULL;		/* gcc */
307122394Sharti
308122394Sharti	switch (op) {
309122394Sharti
310122394Sharti	  case SNMP_OP_GETNEXT:
311122394Sharti		if ((t = NEXT_OBJECT_OID(&trapsink_list, &value->var, sub)) == NULL)
312122394Sharti			return (SNMP_ERR_NOSUCHNAME);
313122394Sharti		index_append(&value->var, sub, &t->index);
314122394Sharti		break;
315122394Sharti
316122394Sharti	  case SNMP_OP_GET:
317122394Sharti		if ((t = FIND_OBJECT_OID(&trapsink_list, &value->var, sub)) == NULL)
318122394Sharti			return (SNMP_ERR_NOSUCHNAME);
319122394Sharti		break;
320122394Sharti
321122394Sharti	  case SNMP_OP_SET:
322122394Sharti		if (index_decode(&value->var, sub, iidx, ipa, &port) ||
323122394Sharti		    port == 0 || port > 65535)
324122394Sharti			return (SNMP_ERR_NO_CREATION);
325122394Sharti		t = FIND_OBJECT_OID(&trapsink_list, &value->var, sub);
326122394Sharti
327122394Sharti		asn_slice_oid(&idx, &value->var, sub, value->var.len);
328122394Sharti
329122394Sharti		tdep = (struct trapsink_dep *)snmp_dep_lookup(ctx,
330122394Sharti		    &oid_begemotTrapSinkTable, &idx,
331122394Sharti		    sizeof(*tdep), trapsink_dep);
332122394Sharti		if (tdep == NULL)
333122394Sharti			return (SNMP_ERR_RES_UNAVAIL);
334122394Sharti
335122394Sharti		switch (value->var.subs[sub - 1]) {
336122394Sharti
337122394Sharti		  case LEAF_begemotTrapSinkStatus:
338122394Sharti			if (tdep->set & TDEP_STATUS)
339122394Sharti				return (SNMP_ERR_INCONS_VALUE);
340122394Sharti			switch (value->v.integer) {
341122394Sharti
342122394Sharti			  case 1:
343122394Sharti			  case 2:
344122394Sharti				if (t == NULL)
345122394Sharti					return (SNMP_ERR_INCONS_VALUE);
346122394Sharti				break;
347122394Sharti
348122394Sharti			  case 4:
349122394Sharti			  case 5:
350122394Sharti				if (t != NULL)
351122394Sharti					return (SNMP_ERR_INCONS_VALUE);
352122394Sharti				break;
353122394Sharti
354122394Sharti			  case 6:
355122394Sharti				break;
356122394Sharti
357122394Sharti			  default:
358122394Sharti				return (SNMP_ERR_WRONG_VALUE);
359122394Sharti			}
360122394Sharti			tdep->status = value->v.integer;
361122394Sharti			tdep->set |= TDEP_STATUS;
362122394Sharti			return (SNMP_ERR_NOERROR);
363122394Sharti
364122394Sharti		  case LEAF_begemotTrapSinkComm:
365122394Sharti			if (tdep->set & TDEP_COMM)
366122394Sharti				return (SNMP_ERR_INCONS_VALUE);
367122394Sharti			if (value->v.octetstring.len == 0 ||
368122394Sharti			    value->v.octetstring.len > SNMP_COMMUNITY_MAXLEN)
369122394Sharti				return (SNMP_ERR_WRONG_VALUE);
370122394Sharti			for (p = value->v.octetstring.octets;
371122394Sharti			     p < value->v.octetstring.octets + value->v.octetstring.len;
372122394Sharti			     p++) {
373122394Sharti				if (!isascii(*p) || !isprint(*p))
374122394Sharti					return (SNMP_ERR_WRONG_VALUE);
375122394Sharti			}
376122394Sharti			tdep->set |= TDEP_COMM;
377122394Sharti			strncpy(tdep->comm, value->v.octetstring.octets,
378122394Sharti			    value->v.octetstring.len);
379122394Sharti			tdep->comm[value->v.octetstring.len] = '\0';
380122394Sharti			return (SNMP_ERR_NOERROR);
381122394Sharti
382122394Sharti		  case LEAF_begemotTrapSinkVersion:
383122394Sharti			if (tdep->set & TDEP_VERSION)
384122394Sharti				return (SNMP_ERR_INCONS_VALUE);
385122394Sharti			if (value->v.integer != TRAPSINK_V1 &&
386122394Sharti			    value->v.integer != TRAPSINK_V2)
387122394Sharti				return (SNMP_ERR_WRONG_VALUE);
388122394Sharti			tdep->version = value->v.integer;
389122394Sharti			tdep->set |= TDEP_VERSION;
390122394Sharti			return (SNMP_ERR_NOERROR);
391122394Sharti		}
392122394Sharti		if (t == NULL)
393122394Sharti			return (SNMP_ERR_INCONS_NAME);
394122394Sharti		else
395122394Sharti			return (SNMP_ERR_NOT_WRITEABLE);
396122394Sharti
397122394Sharti
398122394Sharti	  case SNMP_OP_ROLLBACK:
399122394Sharti	  case SNMP_OP_COMMIT:
400122394Sharti		return (SNMP_ERR_NOERROR);
401122394Sharti	}
402122394Sharti
403122394Sharti	switch (value->var.subs[sub - 1]) {
404122394Sharti
405122394Sharti	  case LEAF_begemotTrapSinkStatus:
406122394Sharti		value->v.integer = t->status;
407122394Sharti		break;
408122394Sharti
409122394Sharti	  case LEAF_begemotTrapSinkComm:
410122394Sharti		return (string_get(value, t->comm, -1));
411122394Sharti
412122394Sharti	  case LEAF_begemotTrapSinkVersion:
413122394Sharti		value->v.integer = t->version;
414122394Sharti		break;
415122394Sharti
416122394Sharti	}
417122394Sharti	return (SNMP_ERR_NOERROR);
418122394Sharti}
419122394Sharti
420216594Ssyrinxstatic void
421216594Ssyrinxsnmp_create_v1_trap(struct snmp_pdu *pdu, char *com,
422216594Ssyrinx    const struct asn_oid *trap_oid)
423216594Ssyrinx{
424216594Ssyrinx	memset(pdu, 0, sizeof(*pdu));
425311596Sngie	strlcpy(pdu->community, com, sizeof(pdu->community));
426216594Ssyrinx
427216594Ssyrinx	pdu->version = SNMP_V1;
428216594Ssyrinx	pdu->type = SNMP_PDU_TRAP;
429216594Ssyrinx	pdu->enterprise = systemg.object_id;
430216594Ssyrinx	memcpy(pdu->agent_addr, snmpd.trap1addr, 4);
431216594Ssyrinx	pdu->generic_trap = trap_oid->subs[trap_oid->len - 1] - 1;
432216594Ssyrinx	pdu->specific_trap = 0;
433216594Ssyrinx	pdu->time_stamp = get_ticks() - start_tick;
434216594Ssyrinx	pdu->nbindings = 0;
435216594Ssyrinx}
436216594Ssyrinx
437216594Ssyrinxstatic void
438216594Ssyrinxsnmp_create_v2_trap(struct snmp_pdu *pdu, char *com,
439216594Ssyrinx    const struct asn_oid *trap_oid)
440216594Ssyrinx{
441216594Ssyrinx	memset(pdu, 0, sizeof(*pdu));
442311596Sngie	strlcpy(pdu->community, com, sizeof(pdu->community));
443216594Ssyrinx
444216594Ssyrinx	pdu->version = SNMP_V2c;
445216594Ssyrinx	pdu->type = SNMP_PDU_TRAP2;
446216594Ssyrinx	pdu->request_id = reqid_next(trap_reqid);
447216594Ssyrinx	pdu->error_index = 0;
448216594Ssyrinx	pdu->error_status = SNMP_ERR_NOERROR;
449216594Ssyrinx
450216594Ssyrinx	pdu->bindings[0].var = oid_sysUpTime;
451216594Ssyrinx	pdu->bindings[0].var.subs[pdu->bindings[0].var.len++] = 0;
452216594Ssyrinx	pdu->bindings[0].syntax = SNMP_SYNTAX_TIMETICKS;
453216594Ssyrinx	pdu->bindings[0].v.uint32 = get_ticks() - start_tick;
454216594Ssyrinx
455216594Ssyrinx	pdu->bindings[1].var = oid_snmpTrapOID;
456216594Ssyrinx	pdu->bindings[1].var.subs[pdu->bindings[1].var.len++] = 0;
457216594Ssyrinx	pdu->bindings[1].syntax = SNMP_SYNTAX_OID;
458216594Ssyrinx	pdu->bindings[1].v.oid = *trap_oid;
459216594Ssyrinx
460216594Ssyrinx	pdu->nbindings = 2;
461216594Ssyrinx}
462216594Ssyrinx
463216594Ssyrinxstatic void
464216594Ssyrinxsnmp_create_v3_trap(struct snmp_pdu *pdu, struct target_param *target,
465216594Ssyrinx    const struct asn_oid *trap_oid)
466216594Ssyrinx{
467216594Ssyrinx	struct usm_user *usmuser;
468216594Ssyrinx
469216594Ssyrinx	memset(pdu, 0, sizeof(*pdu));
470216594Ssyrinx
471216594Ssyrinx	pdu->version = SNMP_V3;
472216594Ssyrinx	pdu->type = SNMP_PDU_TRAP2;
473216594Ssyrinx	pdu->request_id = reqid_next(trap_reqid);
474216594Ssyrinx	pdu->error_index = 0;
475216594Ssyrinx	pdu->error_status = SNMP_ERR_NOERROR;
476216594Ssyrinx
477216594Ssyrinx	pdu->bindings[0].var = oid_sysUpTime;
478216594Ssyrinx	pdu->bindings[0].var.subs[pdu->bindings[0].var.len++] = 0;
479216594Ssyrinx	pdu->bindings[0].syntax = SNMP_SYNTAX_TIMETICKS;
480216594Ssyrinx	pdu->bindings[0].v.uint32 = get_ticks() - start_tick;
481216594Ssyrinx
482216594Ssyrinx	pdu->bindings[1].var = oid_snmpTrapOID;
483216594Ssyrinx	pdu->bindings[1].var.subs[pdu->bindings[1].var.len++] = 0;
484216594Ssyrinx	pdu->bindings[1].syntax = SNMP_SYNTAX_OID;
485216594Ssyrinx	pdu->bindings[1].v.oid = *trap_oid;
486216594Ssyrinx
487216594Ssyrinx	pdu->nbindings = 2;
488216594Ssyrinx
489310990Sngie	update_snmpd_engine_time();
490216594Ssyrinx
491216594Ssyrinx	memcpy(pdu->engine.engine_id, snmpd_engine.engine_id,
492216594Ssyrinx	    snmpd_engine.engine_len);
493216594Ssyrinx	pdu->engine.engine_len = snmpd_engine.engine_len;
494216594Ssyrinx	pdu->engine.engine_boots = snmpd_engine.engine_boots;
495216594Ssyrinx	pdu->engine.engine_time = snmpd_engine.engine_time;
496216594Ssyrinx	pdu->engine.max_msg_size = snmpd_engine.max_msg_size;
497216594Ssyrinx	strlcpy(pdu->user.sec_name, target->secname,
498216594Ssyrinx	    sizeof(pdu->user.sec_name));
499216594Ssyrinx	pdu->security_model = target->sec_model;
500216594Ssyrinx
501216594Ssyrinx	pdu->context_engine_len = snmpd_engine.engine_len;
502216594Ssyrinx	memcpy(pdu->context_engine, snmpd_engine.engine_id,
503216594Ssyrinx	    snmpd_engine.engine_len);
504216594Ssyrinx
505216594Ssyrinx	if (target->sec_model == SNMP_SECMODEL_USM &&
506216594Ssyrinx	    target->sec_level != SNMP_noAuthNoPriv) {
507216594Ssyrinx	    	usmuser = usm_find_user(pdu->engine.engine_id,
508216594Ssyrinx	    	   pdu->engine.engine_len, pdu->user.sec_name);
509216594Ssyrinx		if (usmuser != NULL) {
510216594Ssyrinx			pdu->user.auth_proto = usmuser->suser.auth_proto;
511216594Ssyrinx			pdu->user.priv_proto = usmuser->suser.priv_proto;
512216594Ssyrinx			memcpy(pdu->user.auth_key, usmuser->suser.auth_key,
513216594Ssyrinx			    sizeof(pdu->user.auth_key));
514216594Ssyrinx			memcpy(pdu->user.priv_key, usmuser->suser.priv_key,
515216594Ssyrinx			    sizeof(pdu->user.priv_key));
516216594Ssyrinx		}
517216594Ssyrinx		snmp_pdu_init_secparams(pdu);
518216594Ssyrinx	}
519216594Ssyrinx}
520216594Ssyrinx
521122394Shartivoid
522122394Shartisnmp_send_trap(const struct asn_oid *trap_oid, ...)
523122394Sharti{
524122394Sharti	struct snmp_pdu pdu;
525122394Sharti	struct trapsink *t;
526122394Sharti	const struct snmp_value *v;
527216594Ssyrinx	struct target_notify *n;
528216594Ssyrinx	struct target_address *ta;
529216594Ssyrinx	struct target_param *tp;
530216594Ssyrinx
531122394Sharti	va_list ap;
532122394Sharti	u_char *sndbuf;
533216594Ssyrinx	char *tag;
534122394Sharti	size_t sndlen;
535122394Sharti	ssize_t len;
536216594Ssyrinx	int32_t ip;
537122394Sharti
538122394Sharti	TAILQ_FOREACH(t, &trapsink_list, link) {
539122394Sharti		if (t->status != TRAPSINK_ACTIVE)
540122394Sharti			continue;
541310730Sngie
542216594Ssyrinx		if (t->version == TRAPSINK_V1)
543216594Ssyrinx			snmp_create_v1_trap(&pdu, t->comm, trap_oid);
544216594Ssyrinx		else
545216594Ssyrinx			snmp_create_v2_trap(&pdu, t->comm, trap_oid);
546122394Sharti
547216594Ssyrinx		va_start(ap, trap_oid);
548216594Ssyrinx		while ((v = va_arg(ap, const struct snmp_value *)) != NULL)
549216594Ssyrinx			pdu.bindings[pdu.nbindings++] = *v;
550216594Ssyrinx		va_end(ap);
551122394Sharti
552216594Ssyrinx		if (snmp_pdu_auth_access(&pdu, &ip) != SNMP_CODE_OK) {
553216594Ssyrinx			syslog(LOG_DEBUG, "send trap to %s failed: no access",
554216594Ssyrinx			    t->comm);
555216594Ssyrinx			continue;
556216594Ssyrinx		}
557122394Sharti
558216594Ssyrinx		if ((sndbuf = buf_alloc(1)) == NULL) {
559216594Ssyrinx			syslog(LOG_ERR, "trap send buffer: %m");
560216594Ssyrinx			return;
561216594Ssyrinx		}
562122394Sharti
563216594Ssyrinx		snmp_output(&pdu, sndbuf, &sndlen, "TRAP");
564216594Ssyrinx
565216594Ssyrinx		if ((len = send(t->socket, sndbuf, sndlen, 0)) == -1)
566216594Ssyrinx			syslog(LOG_ERR, "send: %m");
567216594Ssyrinx		else if ((size_t)len != sndlen)
568216594Ssyrinx			syslog(LOG_ERR, "send: short write %zu/%zu",
569216594Ssyrinx			    sndlen, (size_t)len);
570216594Ssyrinx
571216594Ssyrinx		free(sndbuf);
572216594Ssyrinx	}
573216594Ssyrinx
574216594Ssyrinx	SLIST_FOREACH(n, &target_notifylist, tn) {
575216594Ssyrinx		if (n->status != RowStatus_active || n->taglist[0] == '\0')
576216594Ssyrinx			continue;
577216594Ssyrinx
578216594Ssyrinx		SLIST_FOREACH(ta, &target_addresslist, ta)
579216594Ssyrinx			if ((tag = strstr(ta->taglist, n->taglist)) != NULL  &&
580216594Ssyrinx			    (tag[strlen(n->taglist)] == ' ' ||
581216594Ssyrinx			     tag[strlen(n->taglist)] == '\0' ||
582216594Ssyrinx			     tag[strlen(n->taglist)] == '\t' ||
583216594Ssyrinx			     tag[strlen(n->taglist)] == '\r' ||
584216594Ssyrinx			     tag[strlen(n->taglist)] == '\n') &&
585216594Ssyrinx			     ta->status == RowStatus_active)
586216594Ssyrinx				break;
587216594Ssyrinx		if (ta == NULL)
588216594Ssyrinx			continue;
589216594Ssyrinx
590216594Ssyrinx		SLIST_FOREACH(tp, &target_paramlist, tp)
591216594Ssyrinx			if (strcmp(tp->name, ta->paramname) == 0 &&
592216594Ssyrinx			    tp->status == 1)
593216594Ssyrinx				break;
594216594Ssyrinx		if (tp == NULL)
595216594Ssyrinx			continue;
596216594Ssyrinx
597216594Ssyrinx		switch (tp->mpmodel) {
598216594Ssyrinx		case SNMP_MPM_SNMP_V1:
599216594Ssyrinx			snmp_create_v1_trap(&pdu, tp->secname, trap_oid);
600216594Ssyrinx			break;
601216594Ssyrinx
602216594Ssyrinx		case SNMP_MPM_SNMP_V2c:
603216594Ssyrinx			snmp_create_v2_trap(&pdu, tp->secname, trap_oid);
604216594Ssyrinx			break;
605216594Ssyrinx
606216594Ssyrinx		case SNMP_MPM_SNMP_V3:
607216594Ssyrinx			snmp_create_v3_trap(&pdu, tp, trap_oid);
608216594Ssyrinx			break;
609216594Ssyrinx
610216594Ssyrinx		default:
611216594Ssyrinx			continue;
612122394Sharti		}
613122394Sharti
614122394Sharti		va_start(ap, trap_oid);
615122394Sharti		while ((v = va_arg(ap, const struct snmp_value *)) != NULL)
616122394Sharti			pdu.bindings[pdu.nbindings++] = *v;
617122394Sharti		va_end(ap);
618122394Sharti
619216594Ssyrinx		if (snmp_pdu_auth_access(&pdu, &ip) != SNMP_CODE_OK) {
620216594Ssyrinx			syslog(LOG_DEBUG, "send trap to %s failed: no access",
621216594Ssyrinx			    t->comm);
622216594Ssyrinx			continue;
623216594Ssyrinx		}
624216594Ssyrinx
625122394Sharti		if ((sndbuf = buf_alloc(1)) == NULL) {
626122394Sharti			syslog(LOG_ERR, "trap send buffer: %m");
627122394Sharti			return;
628122394Sharti		}
629122394Sharti
630122394Sharti		snmp_output(&pdu, sndbuf, &sndlen, "TRAP");
631122394Sharti
632216594Ssyrinx		if ((len = send(ta->socket, sndbuf, sndlen, 0)) == -1)
633122394Sharti			syslog(LOG_ERR, "send: %m");
634122394Sharti		else if ((size_t)len != sndlen)
635122394Sharti			syslog(LOG_ERR, "send: short write %zu/%zu",
636122394Sharti			    sndlen, (size_t)len);
637122394Sharti
638122394Sharti		free(sndbuf);
639122394Sharti	}
640122394Sharti}
641216594Ssyrinx
642216594Ssyrinx/*
643216594Ssyrinx * RFC 3413 SNMP Management Target MIB
644216594Ssyrinx */
645216594Ssyrinxstruct snmpd_target_stats *
646216594Ssyrinxbsnmpd_get_target_stats(void)
647216594Ssyrinx{
648216594Ssyrinx	return (&snmpd_target_stats);
649216594Ssyrinx}
650216594Ssyrinx
651216594Ssyrinxstruct target_address *
652216594Ssyrinxtarget_first_address(void)
653216594Ssyrinx{
654216594Ssyrinx	return (SLIST_FIRST(&target_addresslist));
655216594Ssyrinx}
656216594Ssyrinx
657216594Ssyrinxstruct target_address *
658216594Ssyrinxtarget_next_address(struct target_address *addrs)
659216594Ssyrinx{
660216594Ssyrinx	if (addrs == NULL)
661216594Ssyrinx		return (NULL);
662216594Ssyrinx
663216594Ssyrinx	return (SLIST_NEXT(addrs, ta));
664216594Ssyrinx}
665216594Ssyrinx
666216594Ssyrinxstruct target_address *
667216594Ssyrinxtarget_new_address(char *aname)
668216594Ssyrinx{
669216594Ssyrinx	int cmp;
670216594Ssyrinx	struct target_address *addrs, *temp, *prev;
671216594Ssyrinx
672216594Ssyrinx	SLIST_FOREACH(addrs, &target_addresslist, ta)
673216594Ssyrinx		if (strcmp(aname, addrs->name) == 0)
674216594Ssyrinx			return (NULL);
675216594Ssyrinx
676216594Ssyrinx	if ((addrs = (struct target_address *)malloc(sizeof(*addrs))) == NULL)
677216594Ssyrinx		return (NULL);
678216594Ssyrinx
679216594Ssyrinx	memset(addrs, 0, sizeof(*addrs));
680216594Ssyrinx	strlcpy(addrs->name, aname, sizeof(addrs->name));
681216594Ssyrinx	addrs->timeout = 150;
682216594Ssyrinx	addrs->retry = 3; /* XXX */
683216594Ssyrinx
684216594Ssyrinx	if ((prev = SLIST_FIRST(&target_addresslist)) == NULL ||
685216594Ssyrinx	    strcmp(aname, prev->name) < 0) {
686216594Ssyrinx		SLIST_INSERT_HEAD(&target_addresslist, addrs, ta);
687216594Ssyrinx		return (addrs);
688216594Ssyrinx	}
689216594Ssyrinx
690216594Ssyrinx	SLIST_FOREACH(temp, &target_addresslist, ta) {
691216594Ssyrinx		if ((cmp = strcmp(aname, temp->name)) <= 0)
692216594Ssyrinx			break;
693216594Ssyrinx		prev = temp;
694216594Ssyrinx	}
695216594Ssyrinx
696216594Ssyrinx	if (temp == NULL || cmp < 0)
697216594Ssyrinx		SLIST_INSERT_AFTER(prev, addrs, ta);
698216594Ssyrinx	else if (cmp > 0)
699216594Ssyrinx		SLIST_INSERT_AFTER(temp, addrs, ta);
700216594Ssyrinx	else {
701216594Ssyrinx		syslog(LOG_ERR, "Target address %s exists", addrs->name);
702216594Ssyrinx		free(addrs);
703216594Ssyrinx		return (NULL);
704216594Ssyrinx	}
705216594Ssyrinx
706216594Ssyrinx	return (addrs);
707216594Ssyrinx}
708216594Ssyrinx
709216594Ssyrinxint
710216594Ssyrinxtarget_activate_address(struct target_address *addrs)
711216594Ssyrinx{
712216594Ssyrinx	struct sockaddr_in sa;
713216594Ssyrinx
714216594Ssyrinx	if ((addrs->socket = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
715216594Ssyrinx		syslog(LOG_ERR, "socket(UDP): %m");
716216594Ssyrinx		return (SNMP_ERR_RES_UNAVAIL);
717216594Ssyrinx	}
718216594Ssyrinx
719216594Ssyrinx	(void)shutdown(addrs->socket, SHUT_RD);
720240191Skevlo	memset(&sa, 0, sizeof(sa));
721216594Ssyrinx	sa.sin_len = sizeof(sa);
722216594Ssyrinx	sa.sin_family = AF_INET;
723216594Ssyrinx
724216594Ssyrinx	sa.sin_addr.s_addr = htonl((addrs->address[0] << 24) |
725216594Ssyrinx	    (addrs->address[1] << 16) | (addrs->address[2] << 8) |
726216594Ssyrinx	    (addrs->address[3] << 0));
727346027Sae	sa.sin_port = htons(addrs->address[4] << 8 | addrs->address[5]);
728216594Ssyrinx
729216594Ssyrinx	if (connect(addrs->socket, (struct sockaddr *)&sa, sa.sin_len) == -1) {
730216594Ssyrinx		syslog(LOG_ERR, "connect(%s,%u): %m",
731216594Ssyrinx		    inet_ntoa(sa.sin_addr), ntohs(sa.sin_port));
732216594Ssyrinx		(void)close(addrs->socket);
733216594Ssyrinx		return (SNMP_ERR_GENERR);
734216594Ssyrinx	}
735216594Ssyrinx
736216594Ssyrinx	addrs->status = RowStatus_active;
737216594Ssyrinx
738216594Ssyrinx	return (SNMP_ERR_NOERROR);
739216594Ssyrinx}
740216594Ssyrinx
741216594Ssyrinxint
742216594Ssyrinxtarget_delete_address(struct target_address *addrs)
743216594Ssyrinx{
744216594Ssyrinx	SLIST_REMOVE(&target_addresslist, addrs, target_address, ta);
745216594Ssyrinx	if (addrs->status == RowStatus_active)
746216594Ssyrinx		close(addrs->socket);
747216594Ssyrinx	free(addrs);
748216594Ssyrinx
749216594Ssyrinx	return (0);
750216594Ssyrinx}
751216594Ssyrinx
752216594Ssyrinxstruct target_param *
753216594Ssyrinxtarget_first_param(void)
754216594Ssyrinx{
755216594Ssyrinx	return (SLIST_FIRST(&target_paramlist));
756216594Ssyrinx}
757216594Ssyrinx
758216594Ssyrinxstruct target_param *
759216594Ssyrinxtarget_next_param(struct target_param *param)
760216594Ssyrinx{
761216594Ssyrinx	if (param == NULL)
762216594Ssyrinx		return (NULL);
763216594Ssyrinx
764216594Ssyrinx	return (SLIST_NEXT(param, tp));
765216594Ssyrinx}
766216594Ssyrinx
767216594Ssyrinxstruct target_param *
768216594Ssyrinxtarget_new_param(char *pname)
769216594Ssyrinx{
770216594Ssyrinx	int cmp;
771216594Ssyrinx	struct target_param *param, *temp, *prev;
772216594Ssyrinx
773216594Ssyrinx	SLIST_FOREACH(param, &target_paramlist, tp)
774216594Ssyrinx		if (strcmp(pname, param->name) == 0)
775216594Ssyrinx			return (NULL);
776216594Ssyrinx
777216594Ssyrinx	if ((param = (struct target_param *)malloc(sizeof(*param))) == NULL)
778216594Ssyrinx		return (NULL);
779216594Ssyrinx
780216594Ssyrinx	memset(param, 0, sizeof(*param));
781216594Ssyrinx	strlcpy(param->name, pname, sizeof(param->name));
782216594Ssyrinx
783216594Ssyrinx	if ((prev = SLIST_FIRST(&target_paramlist)) == NULL ||
784216594Ssyrinx	    strcmp(pname, prev->name) < 0) {
785216594Ssyrinx		SLIST_INSERT_HEAD(&target_paramlist, param, tp);
786216594Ssyrinx		return (param);
787216594Ssyrinx	}
788216594Ssyrinx
789216594Ssyrinx	SLIST_FOREACH(temp, &target_paramlist, tp) {
790216594Ssyrinx		if ((cmp = strcmp(pname, temp->name)) <= 0)
791216594Ssyrinx			break;
792216594Ssyrinx		prev = temp;
793216594Ssyrinx	}
794216594Ssyrinx
795216594Ssyrinx	if (temp == NULL || cmp < 0)
796216594Ssyrinx		SLIST_INSERT_AFTER(prev, param, tp);
797216594Ssyrinx	else if (cmp > 0)
798216594Ssyrinx		SLIST_INSERT_AFTER(temp, param, tp);
799216594Ssyrinx	else {
800216594Ssyrinx		syslog(LOG_ERR, "Target parameter %s exists", param->name);
801216594Ssyrinx		free(param);
802216594Ssyrinx		return (NULL);
803216594Ssyrinx	}
804216594Ssyrinx
805216594Ssyrinx	return (param);
806216594Ssyrinx}
807216594Ssyrinx
808216594Ssyrinxint
809216594Ssyrinxtarget_delete_param(struct target_param *param)
810216594Ssyrinx{
811216594Ssyrinx	SLIST_REMOVE(&target_paramlist, param, target_param, tp);
812216594Ssyrinx	free(param);
813216594Ssyrinx
814216594Ssyrinx	return (0);
815216594Ssyrinx}
816216594Ssyrinx
817216594Ssyrinxstruct target_notify *
818216594Ssyrinxtarget_first_notify(void)
819216594Ssyrinx{
820216594Ssyrinx	return (SLIST_FIRST(&target_notifylist));
821216594Ssyrinx}
822216594Ssyrinx
823216594Ssyrinxstruct target_notify *
824216594Ssyrinxtarget_next_notify(struct target_notify *notify)
825216594Ssyrinx{
826216594Ssyrinx	if (notify == NULL)
827216594Ssyrinx		return (NULL);
828216594Ssyrinx
829216594Ssyrinx	return (SLIST_NEXT(notify, tn));
830216594Ssyrinx}
831216594Ssyrinx
832216594Ssyrinxstruct target_notify *
833216594Ssyrinxtarget_new_notify(char *nname)
834216594Ssyrinx{
835216594Ssyrinx	int cmp;
836216594Ssyrinx	struct target_notify *notify, *temp, *prev;
837216594Ssyrinx
838216594Ssyrinx	SLIST_FOREACH(notify, &target_notifylist, tn)
839216594Ssyrinx		if (strcmp(nname, notify->name) == 0)
840216594Ssyrinx			return (NULL);
841216594Ssyrinx
842216594Ssyrinx	if ((notify = (struct target_notify *)malloc(sizeof(*notify))) == NULL)
843216594Ssyrinx		return (NULL);
844216594Ssyrinx
845216594Ssyrinx	memset(notify, 0, sizeof(*notify));
846216594Ssyrinx	strlcpy(notify->name, nname, sizeof(notify->name));
847216594Ssyrinx
848216594Ssyrinx	if ((prev = SLIST_FIRST(&target_notifylist)) == NULL ||
849216594Ssyrinx	    strcmp(nname, prev->name) < 0) {
850216594Ssyrinx		SLIST_INSERT_HEAD(&target_notifylist, notify, tn);
851216594Ssyrinx		return (notify);
852216594Ssyrinx	}
853216594Ssyrinx
854216594Ssyrinx	SLIST_FOREACH(temp, &target_notifylist, tn) {
855216594Ssyrinx		if ((cmp = strcmp(nname, temp->name)) <= 0)
856216594Ssyrinx			break;
857216594Ssyrinx		prev = temp;
858216594Ssyrinx	}
859216594Ssyrinx
860216594Ssyrinx	if (temp == NULL || cmp < 0)
861216594Ssyrinx		SLIST_INSERT_AFTER(prev, notify, tn);
862216594Ssyrinx	else if (cmp > 0)
863216594Ssyrinx		SLIST_INSERT_AFTER(temp, notify, tn);
864216594Ssyrinx	else {
865216594Ssyrinx		syslog(LOG_ERR, "Notification target %s exists", notify->name);
866216594Ssyrinx		free(notify);
867216594Ssyrinx		return (NULL);
868216594Ssyrinx	}
869216594Ssyrinx
870216594Ssyrinx	return (notify);
871216594Ssyrinx}
872216594Ssyrinx
873216594Ssyrinxint
874216594Ssyrinxtarget_delete_notify(struct target_notify *notify)
875216594Ssyrinx{
876216594Ssyrinx	SLIST_REMOVE(&target_notifylist, notify, target_notify, tn);
877216594Ssyrinx	free(notify);
878216594Ssyrinx
879216594Ssyrinx	return (0);
880216594Ssyrinx}
881216594Ssyrinx
882216594Ssyrinxvoid
883216594Ssyrinxtarget_flush_all(void)
884216594Ssyrinx{
885216594Ssyrinx	struct target_address *addrs;
886216594Ssyrinx	struct target_param *param;
887216594Ssyrinx	struct target_notify *notify;
888216594Ssyrinx
889216594Ssyrinx	while ((addrs = SLIST_FIRST(&target_addresslist)) != NULL) {
890216594Ssyrinx		SLIST_REMOVE_HEAD(&target_addresslist, ta);
891216594Ssyrinx		if (addrs->status == RowStatus_active)
892216594Ssyrinx			close(addrs->socket);
893216594Ssyrinx		free(addrs);
894216594Ssyrinx	}
895216594Ssyrinx	SLIST_INIT(&target_addresslist);
896216594Ssyrinx
897216594Ssyrinx	while ((param = SLIST_FIRST(&target_paramlist)) != NULL) {
898216594Ssyrinx		SLIST_REMOVE_HEAD(&target_paramlist, tp);
899216594Ssyrinx		free(param);
900216594Ssyrinx	}
901216594Ssyrinx	SLIST_INIT(&target_paramlist);
902216594Ssyrinx
903216594Ssyrinx	while ((notify = SLIST_FIRST(&target_notifylist)) != NULL) {
904216594Ssyrinx		SLIST_REMOVE_HEAD(&target_notifylist, tn);
905216594Ssyrinx		free(notify);
906216594Ssyrinx	}
907216594Ssyrinx	SLIST_INIT(&target_notifylist);
908216594Ssyrinx}
909