trap.c revision 310903
133965Sjdp/*
2104834Sobrien * Copyright (c) 2001-2003
3218822Sdim *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
433965Sjdp *	All rights reserved.
533965Sjdp *
633965Sjdp * Author: Harti Brandt <harti@freebsd.org>
733965Sjdp *
860484Sobrien * Copyright (c) 2010 The FreeBSD Foundation
933965Sjdp * All rights reserved.
1033965Sjdp *
1133965Sjdp * Portions of this software were developed by Shteryana Sotirova Shopova
1233965Sjdp * under sponsorship from the FreeBSD Foundation.
1333965Sjdp *
1433965Sjdp * Redistribution and use in source and binary forms, with or without
1533965Sjdp * modification, are permitted provided that the following conditions
1633965Sjdp * are met:
1733965Sjdp * 1. Redistributions of source code must retain the above copyright
1833965Sjdp *    notice, this list of conditions and the following disclaimer.
1933965Sjdp * 2. Redistributions in binary form must reproduce the above copyright
2033965Sjdp *    notice, this list of conditions and the following disclaimer in the
2133965Sjdp *    documentation and/or other materials provided with the distribution.
2233965Sjdp *
2333965Sjdp * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24218822Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25218822Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2633965Sjdp * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
27218822Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28218822Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29218822Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3033965Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31218822Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32218822Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33218822Sdim * SUCH DAMAGE.
34218822Sdim *
35218822Sdim * $Begemot: bsnmp/snmpd/trap.c,v 1.9 2005/10/04 11:21:39 brandt_h Exp $
36218822Sdim *
37218822Sdim * TrapSinkTable
38218822Sdim */
39218822Sdim#include <sys/types.h>
40218822Sdim#include <sys/queue.h>
41218822Sdim#include <sys/sysctl.h>
42218822Sdim#include <sys/un.h>
43218822Sdim#include <stdint.h>
44218822Sdim#include <stdio.h>
45218822Sdim#include <stdlib.h>
46218822Sdim#include <stdarg.h>
47218822Sdim#include <stdarg.h>
48218822Sdim#include <string.h>
49218822Sdim#include <ctype.h>
50218822Sdim#include <syslog.h>
5133965Sjdp#include <unistd.h>
5233965Sjdp#include <netinet/in.h>
5360484Sobrien#include <arpa/inet.h>
5460484Sobrien
5533965Sjdp#include "snmpmod.h"
5633965Sjdp#include "snmpd.h"
5733965Sjdp#include "tree.h"
5833965Sjdp#include "oid.h"
5933965Sjdp
6077298Sobrienstruct trapsink_list trapsink_list = TAILQ_HEAD_INITIALIZER(trapsink_list);
6133965Sjdp
6233965Sjdp/* List of target addresses */
63218822Sdimstatic struct target_addresslist target_addresslist =
6489857Sobrien    SLIST_HEAD_INITIALIZER(target_addresslist);
6533965Sjdp
66130561Sobrien/* List of target parameters */
6733965Sjdpstatic struct target_paramlist target_paramlist =
68218822Sdim    SLIST_HEAD_INITIALIZER(target_paramlist);
69218822Sdim
70218822Sdim/* List of notification targets */
71218822Sdimstatic struct target_notifylist target_notifylist =
7260484Sobrien    SLIST_HEAD_INITIALIZER(target_notifylist);
73130561Sobrien
74130561Sobrienstatic const struct asn_oid oid_begemotTrapSinkTable =
7533965Sjdp    OIDX_begemotTrapSinkTable;
7633965Sjdpstatic const struct asn_oid oid_sysUpTime = OIDX_sysUpTime;
7733965Sjdpstatic const struct asn_oid oid_snmpTrapOID = OIDX_snmpTrapOID;
7833965Sjdp
79130561Sobrienstruct trapsink_dep {
80130561Sobrien	struct snmp_dependency dep;
8133965Sjdp	u_int	set;
8233965Sjdp	u_int	status;
8333965Sjdp	u_char	comm[SNMP_COMMUNITY_MAXLEN + 1];
8489857Sobrien	u_int	version;
8533965Sjdp	u_int	rb;
8633965Sjdp	u_int	rb_status;
87130561Sobrien	u_int	rb_version;
88130561Sobrien	u_char	rb_comm[SNMP_COMMUNITY_MAXLEN + 1];
89130561Sobrien};
90130561Sobrienenum {
91130561Sobrien	TDEP_STATUS	= 0x0001,
92130561Sobrien	TDEP_COMM	= 0x0002,
93130561Sobrien	TDEP_VERSION	= 0x0004,
94130561Sobrien
95130561Sobrien	TDEP_CREATE	= 0x0001,
96130561Sobrien	TDEP_MODIFY	= 0x0002,
9733965Sjdp	TDEP_DESTROY	= 0x0004,
98130561Sobrien};
99130561Sobrien
10033965Sjdpstatic int
101218822Sdimtrapsink_create(struct trapsink_dep *tdep)
102218822Sdim{
10333965Sjdp	struct trapsink *t;
10433965Sjdp	struct sockaddr_in sa;
10533965Sjdp
10677298Sobrien	if ((t = malloc(sizeof(*t))) == NULL)
10733965Sjdp		return (SNMP_ERR_RES_UNAVAIL);
108218822Sdim
109218822Sdim	t->index = tdep->dep.idx;
11033965Sjdp	t->status = TRAPSINK_NOT_READY;
111104834Sobrien	t->comm[0] = '\0';
112218822Sdim	t->version = TRAPSINK_V2;
113218822Sdim
114218822Sdim	if ((t->socket = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
115218822Sdim		syslog(LOG_ERR, "socket(UDP): %m");
116218822Sdim		free(t);
117218822Sdim		return (SNMP_ERR_RES_UNAVAIL);
118218822Sdim	}
119218822Sdim	(void)shutdown(t->socket, SHUT_RD);
120218822Sdim	memset(&sa, 0, sizeof(sa));
121218822Sdim	sa.sin_len = sizeof(sa);
122218822Sdim	sa.sin_family = AF_INET;
123218822Sdim	sa.sin_addr.s_addr = htonl((t->index.subs[0] << 24) |
124218822Sdim	    (t->index.subs[1] << 16) | (t->index.subs[2] << 8) |
125218822Sdim	    (t->index.subs[3] << 0));
126218822Sdim	sa.sin_port = htons(t->index.subs[4]);
127218822Sdim
128218822Sdim	if (connect(t->socket, (struct sockaddr *)&sa, sa.sin_len) == -1) {
129218822Sdim		syslog(LOG_ERR, "connect(%s,%u): %m",
130218822Sdim		    inet_ntoa(sa.sin_addr), ntohs(sa.sin_port));
131218822Sdim		(void)close(t->socket);
132218822Sdim		free(t);
133218822Sdim		return (SNMP_ERR_GENERR);
134218822Sdim	}
13560484Sobrien
13689857Sobrien	if (tdep->set & TDEP_VERSION)
13789857Sobrien		t->version = tdep->version;
13889857Sobrien	if (tdep->set & TDEP_COMM)
13989857Sobrien		strcpy(t->comm, tdep->comm);
140104834Sobrien
141104834Sobrien	if (t->comm[0] != '\0')
142218822Sdim		t->status = TRAPSINK_NOT_IN_SERVICE;
14360484Sobrien
144104834Sobrien	/* look whether we should activate */
14589857Sobrien	if (tdep->status == 4) {
146130561Sobrien		if (t->status == TRAPSINK_NOT_READY) {
14733965Sjdp			if (t->socket != -1)
14833965Sjdp				(void)close(t->socket);
14933965Sjdp			free(t);
15033965Sjdp			return (SNMP_ERR_INCONS_VALUE);
15133965Sjdp		}
152130561Sobrien		t->status = TRAPSINK_ACTIVE;
15333965Sjdp	}
15433965Sjdp
15533965Sjdp	INSERT_OBJECT_OID(t, &trapsink_list);
15633965Sjdp
15733965Sjdp	tdep->rb |= TDEP_CREATE;
15838889Sjdp
15938889Sjdp	return (SNMP_ERR_NOERROR);
16033965Sjdp}
16138889Sjdp
16238889Sjdpstatic void
16377298Sobrientrapsink_free(struct trapsink *t)
16438889Sjdp{
16577298Sobrien	TAILQ_REMOVE(&trapsink_list, t, link);
16638889Sjdp	if (t->socket != -1)
16777298Sobrien		(void)close(t->socket);
16877298Sobrien	free(t);
16977298Sobrien}
17077298Sobrien
17138889Sjdpstatic int
17233965Sjdptrapsink_modify(struct trapsink *t, struct trapsink_dep *tdep)
17333965Sjdp{
17433965Sjdp	tdep->rb_status = t->status;
17533965Sjdp	tdep->rb_version = t->version;
17633965Sjdp	strcpy(tdep->rb_comm, t->comm);
17733965Sjdp
17877298Sobrien	if (tdep->set & TDEP_STATUS) {
17977298Sobrien		/* if we are active and should move to not_in_service do
18033965Sjdp		 * this first */
18177298Sobrien		if (tdep->status == 2 && tdep->rb_status == TRAPSINK_ACTIVE) {
18277298Sobrien			t->status = TRAPSINK_NOT_IN_SERVICE;
18333965Sjdp			tdep->rb |= TDEP_MODIFY;
18433965Sjdp		}
18533965Sjdp	}
186130561Sobrien
18733965Sjdp	if (tdep->set & TDEP_VERSION)
18877298Sobrien		t->version = tdep->version;
18933965Sjdp	if (tdep->set & TDEP_COMM)
19033965Sjdp		strcpy(t->comm, tdep->comm);
19177298Sobrien
19233965Sjdp	if (tdep->set & TDEP_STATUS) {
19333965Sjdp		/* if we were inactive and should go active - do this now */
19477298Sobrien		if (tdep->status == 1 && tdep->rb_status != TRAPSINK_ACTIVE) {
19533965Sjdp			if (t->comm[0] == '\0') {
19633965Sjdp				t->status = tdep->rb_status;
19777298Sobrien				t->version = tdep->rb_version;
19833965Sjdp				strcpy(t->comm, tdep->rb_comm);
19977298Sobrien				return (SNMP_ERR_INCONS_VALUE);
20077298Sobrien			}
20133965Sjdp			t->status = TRAPSINK_ACTIVE;
20233965Sjdp			tdep->rb |= TDEP_MODIFY;
20333965Sjdp		}
20477298Sobrien	}
20533965Sjdp	return (SNMP_ERR_NOERROR);
20633965Sjdp}
20733965Sjdp
20833965Sjdpstatic int
20933965Sjdptrapsink_unmodify(struct trapsink *t, struct trapsink_dep *tdep)
21033965Sjdp{
21133965Sjdp	if (tdep->set & TDEP_STATUS)
21233965Sjdp		t->status = tdep->rb_status;
21333965Sjdp	if (tdep->set & TDEP_VERSION)
21433965Sjdp		t->version = tdep->rb_version;
21533965Sjdp	if (tdep->set & TDEP_COMM)
21633965Sjdp		strcpy(t->comm, tdep->rb_comm);
21733965Sjdp
21833965Sjdp	return (SNMP_ERR_NOERROR);
21933965Sjdp}
22033965Sjdp
22133965Sjdpstatic int
22233965Sjdptrapsink_destroy(struct snmp_context *ctx __unused, struct trapsink *t,
22333965Sjdp    struct trapsink_dep *tdep)
22433965Sjdp{
22533965Sjdp	t->status = TRAPSINK_DESTROY;
22633965Sjdp	tdep->rb_status = t->status;
22733965Sjdp	tdep->rb |= TDEP_DESTROY;
22833965Sjdp	return (SNMP_ERR_NOERROR);
22933965Sjdp}
23033965Sjdp
23138889Sjdpstatic int
23233965Sjdptrapsink_undestroy(struct trapsink *t, struct trapsink_dep *tdep)
23338889Sjdp{
23433965Sjdp	t->status = tdep->rb_status;
23533965Sjdp	return (SNMP_ERR_NOERROR);
236130561Sobrien}
237130561Sobrien
23833965Sjdpstatic int
239130561Sobrientrapsink_dep(struct snmp_context *ctx, struct snmp_dependency *dep,
24077298Sobrien    enum snmp_depop op)
241130561Sobrien{
24260484Sobrien	struct trapsink_dep *tdep = (struct trapsink_dep *)dep;
24377298Sobrien	struct trapsink *t;
24477298Sobrien
245130561Sobrien	t = FIND_OBJECT_OID(&trapsink_list, &dep->idx, 0);
246130561Sobrien
24760484Sobrien	switch (op) {
248130561Sobrien
249130561Sobrien	  case SNMP_DEPOP_COMMIT:
25033965Sjdp		if (tdep->set & TDEP_STATUS) {
25177298Sobrien			switch (tdep->status) {
25233965Sjdp
25333965Sjdp			  case 1:
25433965Sjdp			  case 2:
25533965Sjdp				if (t == NULL)
25633965Sjdp					return (SNMP_ERR_INCONS_VALUE);
25733965Sjdp				return (trapsink_modify(t, tdep));
25833965Sjdp
25933965Sjdp			  case 4:
26033965Sjdp			  case 5:
26133965Sjdp				if (t != NULL)
26233965Sjdp					return (SNMP_ERR_INCONS_VALUE);
26333965Sjdp				return (trapsink_create(tdep));
26433965Sjdp
26533965Sjdp			  case 6:
26633965Sjdp				if (t == NULL)
26733965Sjdp					return (SNMP_ERR_NOERROR);
26833965Sjdp				return (trapsink_destroy(ctx, t, tdep));
26933965Sjdp			}
27033965Sjdp		} else if (tdep->set != 0)
27133965Sjdp			return (trapsink_modify(t, tdep));
27233965Sjdp
273130561Sobrien		return (SNMP_ERR_NOERROR);
27433965Sjdp
27533965Sjdp	  case SNMP_DEPOP_ROLLBACK:
27633965Sjdp		if (tdep->rb & TDEP_CREATE) {
277130561Sobrien			trapsink_free(t);
27833965Sjdp			return (SNMP_ERR_NOERROR);
27933965Sjdp		}
280130561Sobrien		if (tdep->rb & TDEP_MODIFY)
28133965Sjdp			return (trapsink_unmodify(t, tdep));
28233965Sjdp		if(tdep->rb & TDEP_DESTROY)
28333965Sjdp			return (trapsink_undestroy(t, tdep));
284130561Sobrien		return (SNMP_ERR_NOERROR);
28533965Sjdp
28633965Sjdp	  case SNMP_DEPOP_FINISH:
28733965Sjdp		if ((tdep->rb & TDEP_DESTROY) && t != NULL &&
28833965Sjdp		    ctx->code == SNMP_RET_OK)
28933965Sjdp			trapsink_free(t);
29033965Sjdp		return (SNMP_ERR_NOERROR);
291130561Sobrien	}
29233965Sjdp	abort();
29333965Sjdp}
294130561Sobrien
29533965Sjdpint
29633965Sjdpop_trapsink(struct snmp_context *ctx, struct snmp_value *value,
297130561Sobrien    u_int sub, u_int iidx, enum snmp_op op)
29833965Sjdp{
29933965Sjdp	struct trapsink *t;
30033965Sjdp	u_char ipa[4];
30133965Sjdp	int32_t port;
30233965Sjdp	struct asn_oid idx;
30333965Sjdp	struct trapsink_dep *tdep;
30433965Sjdp	u_char *p;
30533965Sjdp
30633965Sjdp	t = NULL;		/* gcc */
30733965Sjdp
30877298Sobrien	switch (op) {
30933965Sjdp
31033965Sjdp	  case SNMP_OP_GETNEXT:
31133965Sjdp		if ((t = NEXT_OBJECT_OID(&trapsink_list, &value->var, sub)) == NULL)
31233965Sjdp			return (SNMP_ERR_NOSUCHNAME);
31333965Sjdp		index_append(&value->var, sub, &t->index);
31433965Sjdp		break;
31533965Sjdp
31633965Sjdp	  case SNMP_OP_GET:
31733965Sjdp		if ((t = FIND_OBJECT_OID(&trapsink_list, &value->var, sub)) == NULL)
31833965Sjdp			return (SNMP_ERR_NOSUCHNAME);
319130561Sobrien		break;
32033965Sjdp
32133965Sjdp	  case SNMP_OP_SET:
32233965Sjdp		if (index_decode(&value->var, sub, iidx, ipa, &port) ||
32333965Sjdp		    port == 0 || port > 65535)
324130561Sobrien			return (SNMP_ERR_NO_CREATION);
32533965Sjdp		t = FIND_OBJECT_OID(&trapsink_list, &value->var, sub);
32633965Sjdp
327130561Sobrien		asn_slice_oid(&idx, &value->var, sub, value->var.len);
32833965Sjdp
32933965Sjdp		tdep = (struct trapsink_dep *)snmp_dep_lookup(ctx,
330130561Sobrien		    &oid_begemotTrapSinkTable, &idx,
33133965Sjdp		    sizeof(*tdep), trapsink_dep);
33233965Sjdp		if (tdep == NULL)
333130561Sobrien			return (SNMP_ERR_RES_UNAVAIL);
33433965Sjdp
33533965Sjdp		switch (value->var.subs[sub - 1]) {
33633965Sjdp
33733965Sjdp		  case LEAF_begemotTrapSinkStatus:
33833965Sjdp			if (tdep->set & TDEP_STATUS)
33933965Sjdp				return (SNMP_ERR_INCONS_VALUE);
34033965Sjdp			switch (value->v.integer) {
34133965Sjdp
34233965Sjdp			  case 1:
34333965Sjdp			  case 2:
34433965Sjdp				if (t == NULL)
34533965Sjdp					return (SNMP_ERR_INCONS_VALUE);
34633965Sjdp				break;
34733965Sjdp
34833965Sjdp			  case 4:
34933965Sjdp			  case 5:
35033965Sjdp				if (t != NULL)
35133965Sjdp					return (SNMP_ERR_INCONS_VALUE);
35233965Sjdp				break;
35333965Sjdp
35433965Sjdp			  case 6:
35533965Sjdp				break;
35660484Sobrien
35760484Sobrien			  default:
35877298Sobrien				return (SNMP_ERR_WRONG_VALUE);
35960484Sobrien			}
36060484Sobrien			tdep->status = value->v.integer;
36138889Sjdp			tdep->set |= TDEP_STATUS;
36238889Sjdp			return (SNMP_ERR_NOERROR);
36338889Sjdp
36438889Sjdp		  case LEAF_begemotTrapSinkComm:
36538889Sjdp			if (tdep->set & TDEP_COMM)
36638889Sjdp				return (SNMP_ERR_INCONS_VALUE);
36733965Sjdp			if (value->v.octetstring.len == 0 ||
36833965Sjdp			    value->v.octetstring.len > SNMP_COMMUNITY_MAXLEN)
369218822Sdim				return (SNMP_ERR_WRONG_VALUE);
370218822Sdim			for (p = value->v.octetstring.octets;
371218822Sdim			     p < value->v.octetstring.octets + value->v.octetstring.len;
37233965Sjdp			     p++) {
373218822Sdim				if (!isascii(*p) || !isprint(*p))
374218822Sdim					return (SNMP_ERR_WRONG_VALUE);
37533965Sjdp			}
37633965Sjdp			tdep->set |= TDEP_COMM;
37733965Sjdp			strncpy(tdep->comm, value->v.octetstring.octets,
37833965Sjdp			    value->v.octetstring.len);
37933965Sjdp			tdep->comm[value->v.octetstring.len] = '\0';
38033965Sjdp			return (SNMP_ERR_NOERROR);
38133965Sjdp
38233965Sjdp		  case LEAF_begemotTrapSinkVersion:
38333965Sjdp			if (tdep->set & TDEP_VERSION)
38433965Sjdp				return (SNMP_ERR_INCONS_VALUE);
38533965Sjdp			if (value->v.integer != TRAPSINK_V1 &&
38633965Sjdp			    value->v.integer != TRAPSINK_V2)
38733965Sjdp				return (SNMP_ERR_WRONG_VALUE);
388130561Sobrien			tdep->version = value->v.integer;
38933965Sjdp			tdep->set |= TDEP_VERSION;
39033965Sjdp			return (SNMP_ERR_NOERROR);
39133965Sjdp		}
39260484Sobrien		if (t == NULL)
39360484Sobrien			return (SNMP_ERR_INCONS_NAME);
39460484Sobrien		else
39560484Sobrien			return (SNMP_ERR_NOT_WRITEABLE);
39660484Sobrien
39760484Sobrien
39889857Sobrien	  case SNMP_OP_ROLLBACK:
39960484Sobrien	  case SNMP_OP_COMMIT:
40089857Sobrien		return (SNMP_ERR_NOERROR);
40160484Sobrien	}
40289857Sobrien
40389857Sobrien	switch (value->var.subs[sub - 1]) {
40460484Sobrien
405130561Sobrien	  case LEAF_begemotTrapSinkStatus:
406130561Sobrien		value->v.integer = t->status;
407218822Sdim		break;
408218822Sdim
409218822Sdim	  case LEAF_begemotTrapSinkComm:
410218822Sdim		return (string_get(value, t->comm, -1));
411218822Sdim
412218822Sdim	  case LEAF_begemotTrapSinkVersion:
413130561Sobrien		value->v.integer = t->version;
414130561Sobrien		break;
415130561Sobrien
416218822Sdim	}
417218822Sdim	return (SNMP_ERR_NOERROR);
418218822Sdim}
419218822Sdim
420218822Sdimstatic void
421218822Sdimsnmp_create_v1_trap(struct snmp_pdu *pdu, char *com,
422218822Sdim    const struct asn_oid *trap_oid)
423218822Sdim{
424218822Sdim	memset(pdu, 0, sizeof(*pdu));
425218822Sdim	strcpy(pdu->community, com);
426218822Sdim
427218822Sdim	pdu->version = SNMP_V1;
428218822Sdim	pdu->type = SNMP_PDU_TRAP;
429218822Sdim	pdu->enterprise = systemg.object_id;
430218822Sdim	memcpy(pdu->agent_addr, snmpd.trap1addr, 4);
431218822Sdim	pdu->generic_trap = trap_oid->subs[trap_oid->len - 1] - 1;
432218822Sdim	pdu->specific_trap = 0;
433218822Sdim	pdu->time_stamp = get_ticks() - start_tick;
434218822Sdim	pdu->nbindings = 0;
435218822Sdim}
436218822Sdim
437218822Sdimstatic void
438218822Sdimsnmp_create_v2_trap(struct snmp_pdu *pdu, char *com,
43989857Sobrien    const struct asn_oid *trap_oid)
44060484Sobrien{
44189857Sobrien	memset(pdu, 0, sizeof(*pdu));
44260484Sobrien	strcpy(pdu->community, com);
44360484Sobrien
44477298Sobrien	pdu->version = SNMP_V2c;
44589857Sobrien	pdu->type = SNMP_PDU_TRAP2;
44660484Sobrien	pdu->request_id = reqid_next(trap_reqid);
447218822Sdim	pdu->error_index = 0;
44860484Sobrien	pdu->error_status = SNMP_ERR_NOERROR;
44960484Sobrien
450130561Sobrien	pdu->bindings[0].var = oid_sysUpTime;
45189857Sobrien	pdu->bindings[0].var.subs[pdu->bindings[0].var.len++] = 0;
45260484Sobrien	pdu->bindings[0].syntax = SNMP_SYNTAX_TIMETICKS;
453218822Sdim	pdu->bindings[0].v.uint32 = get_ticks() - start_tick;
454218822Sdim
455218822Sdim	pdu->bindings[1].var = oid_snmpTrapOID;
456218822Sdim	pdu->bindings[1].var.subs[pdu->bindings[1].var.len++] = 0;
457218822Sdim	pdu->bindings[1].syntax = SNMP_SYNTAX_OID;
458218822Sdim	pdu->bindings[1].v.oid = *trap_oid;
459218822Sdim
460104834Sobrien	pdu->nbindings = 2;
461104834Sobrien}
462104834Sobrien
463218822Sdimstatic void
464218822Sdimsnmp_create_v3_trap(struct snmp_pdu *pdu, struct target_param *target,
46560484Sobrien    const struct asn_oid *trap_oid)
46660484Sobrien{
467218822Sdim	uint64_t etime;
46860484Sobrien	struct usm_user *usmuser;
46989857Sobrien
47089857Sobrien	memset(pdu, 0, sizeof(*pdu));
47189857Sobrien
47260484Sobrien	pdu->version = SNMP_V3;
47338889Sjdp	pdu->type = SNMP_PDU_TRAP2;
47438889Sjdp	pdu->request_id = reqid_next(trap_reqid);
475130561Sobrien	pdu->error_index = 0;
476130561Sobrien	pdu->error_status = SNMP_ERR_NOERROR;
47738889Sjdp
47838889Sjdp	pdu->bindings[0].var = oid_sysUpTime;
479130561Sobrien	pdu->bindings[0].var.subs[pdu->bindings[0].var.len++] = 0;
480130561Sobrien	pdu->bindings[0].syntax = SNMP_SYNTAX_TIMETICKS;
481130561Sobrien	pdu->bindings[0].v.uint32 = get_ticks() - start_tick;
48238889Sjdp
48377298Sobrien	pdu->bindings[1].var = oid_snmpTrapOID;
48438889Sjdp	pdu->bindings[1].var.subs[pdu->bindings[1].var.len++] = 0;
48538889Sjdp	pdu->bindings[1].syntax = SNMP_SYNTAX_OID;
48638889Sjdp	pdu->bindings[1].v.oid = *trap_oid;
48738889Sjdp
48838889Sjdp	pdu->nbindings = 2;
48938889Sjdp
49038889Sjdp	etime = (get_ticks() - start_tick)  / 100ULL;
49138889Sjdp	if (etime < INT32_MAX)
49238889Sjdp		snmpd_engine.engine_time = etime;
49338889Sjdp	else {
49438889Sjdp		start_tick = get_ticks();
49560484Sobrien		set_snmpd_engine();
49660484Sobrien		snmpd_engine.engine_time = start_tick;
497104834Sobrien	}
498104834Sobrien
499104834Sobrien	memcpy(pdu->engine.engine_id, snmpd_engine.engine_id,
500104834Sobrien	    snmpd_engine.engine_len);
50138889Sjdp	pdu->engine.engine_len = snmpd_engine.engine_len;
50238889Sjdp	pdu->engine.engine_boots = snmpd_engine.engine_boots;
50338889Sjdp	pdu->engine.engine_time = snmpd_engine.engine_time;
50438889Sjdp	pdu->engine.max_msg_size = snmpd_engine.max_msg_size;
50538889Sjdp	strlcpy(pdu->user.sec_name, target->secname,
50638889Sjdp	    sizeof(pdu->user.sec_name));
50738889Sjdp	pdu->security_model = target->sec_model;
50860484Sobrien
509104834Sobrien	pdu->context_engine_len = snmpd_engine.engine_len;
510104834Sobrien	memcpy(pdu->context_engine, snmpd_engine.engine_id,
51138889Sjdp	    snmpd_engine.engine_len);
51238889Sjdp
51360484Sobrien	if (target->sec_model == SNMP_SECMODEL_USM &&
51438889Sjdp	    target->sec_level != SNMP_noAuthNoPriv) {
51538889Sjdp	    	usmuser = usm_find_user(pdu->engine.engine_id,
516218822Sdim	    	   pdu->engine.engine_len, pdu->user.sec_name);
517218822Sdim		if (usmuser != NULL) {
518218822Sdim			pdu->user.auth_proto = usmuser->suser.auth_proto;
519218822Sdim			pdu->user.priv_proto = usmuser->suser.priv_proto;
520218822Sdim			memcpy(pdu->user.auth_key, usmuser->suser.auth_key,
521218822Sdim			    sizeof(pdu->user.auth_key));
522218822Sdim			memcpy(pdu->user.priv_key, usmuser->suser.priv_key,
523218822Sdim			    sizeof(pdu->user.priv_key));
524218822Sdim		}
525218822Sdim		snmp_pdu_init_secparams(pdu);
52633965Sjdp	}
527218822Sdim}
528218822Sdim
529218822Sdimvoid
53033965Sjdpsnmp_send_trap(const struct asn_oid *trap_oid, ...)
53189857Sobrien{
532218822Sdim	struct snmp_pdu pdu;
53389857Sobrien	struct trapsink *t;
53489857Sobrien	const struct snmp_value *v;
53589857Sobrien	struct target_notify *n;
53660484Sobrien	struct target_address *ta;
53789857Sobrien	struct target_param *tp;
53889857Sobrien
53989857Sobrien	va_list ap;
54089857Sobrien	u_char *sndbuf;
54189857Sobrien	char *tag;
54289857Sobrien	size_t sndlen;
54389857Sobrien	ssize_t len;
54489857Sobrien	int32_t ip;
545218822Sdim
54689857Sobrien	TAILQ_FOREACH(t, &trapsink_list, link) {
54789857Sobrien		if (t->status != TRAPSINK_ACTIVE)
54889857Sobrien			continue;
54989857Sobrien
550218822Sdim		if (t->version == TRAPSINK_V1)
55189857Sobrien			snmp_create_v1_trap(&pdu, t->comm, trap_oid);
55289857Sobrien		else
55389857Sobrien			snmp_create_v2_trap(&pdu, t->comm, trap_oid);
55489857Sobrien
55589857Sobrien		va_start(ap, trap_oid);
55689857Sobrien		while ((v = va_arg(ap, const struct snmp_value *)) != NULL)
55789857Sobrien			pdu.bindings[pdu.nbindings++] = *v;
55860484Sobrien		va_end(ap);
559218822Sdim
560218822Sdim		if (snmp_pdu_auth_access(&pdu, &ip) != SNMP_CODE_OK) {
561218822Sdim			syslog(LOG_DEBUG, "send trap to %s failed: no access",
56260484Sobrien			    t->comm);
56360484Sobrien			continue;
56460484Sobrien		}
56560484Sobrien
56660484Sobrien		if ((sndbuf = buf_alloc(1)) == NULL) {
56789857Sobrien			syslog(LOG_ERR, "trap send buffer: %m");
56860484Sobrien			return;
56977298Sobrien		}
57077298Sobrien
57177298Sobrien		snmp_output(&pdu, sndbuf, &sndlen, "TRAP");
57260484Sobrien
57360484Sobrien		if ((len = send(t->socket, sndbuf, sndlen, 0)) == -1)
57460484Sobrien			syslog(LOG_ERR, "send: %m");
57560484Sobrien		else if ((size_t)len != sndlen)
57660484Sobrien			syslog(LOG_ERR, "send: short write %zu/%zu",
57760484Sobrien			    sndlen, (size_t)len);
57860484Sobrien
579130561Sobrien		free(sndbuf);
58089857Sobrien	}
58189857Sobrien
58260484Sobrien	SLIST_FOREACH(n, &target_notifylist, tn) {
58360484Sobrien		if (n->status != RowStatus_active || n->taglist[0] == '\0')
58460484Sobrien			continue;
58577298Sobrien
58660484Sobrien		SLIST_FOREACH(ta, &target_addresslist, ta)
58760484Sobrien			if ((tag = strstr(ta->taglist, n->taglist)) != NULL  &&
58860484Sobrien			    (tag[strlen(n->taglist)] == ' ' ||
58989857Sobrien			     tag[strlen(n->taglist)] == '\0' ||
59089857Sobrien			     tag[strlen(n->taglist)] == '\t' ||
59189857Sobrien			     tag[strlen(n->taglist)] == '\r' ||
59289857Sobrien			     tag[strlen(n->taglist)] == '\n') &&
59389857Sobrien			     ta->status == RowStatus_active)
594130561Sobrien				break;
595104834Sobrien		if (ta == NULL)
596104834Sobrien			continue;
597104834Sobrien
598104834Sobrien		SLIST_FOREACH(tp, &target_paramlist, tp)
599104834Sobrien			if (strcmp(tp->name, ta->paramname) == 0 &&
600104834Sobrien			    tp->status == 1)
601104834Sobrien				break;
602104834Sobrien		if (tp == NULL)
603104834Sobrien			continue;
604104834Sobrien
605104834Sobrien		switch (tp->mpmodel) {
606104834Sobrien		case SNMP_MPM_SNMP_V1:
607104834Sobrien			snmp_create_v1_trap(&pdu, tp->secname, trap_oid);
608104834Sobrien			break;
609130561Sobrien
61060484Sobrien		case SNMP_MPM_SNMP_V2c:
61160484Sobrien			snmp_create_v2_trap(&pdu, tp->secname, trap_oid);
61289857Sobrien			break;
61389857Sobrien
61460484Sobrien		case SNMP_MPM_SNMP_V3:
61589857Sobrien			snmp_create_v3_trap(&pdu, tp, trap_oid);
61660484Sobrien			break;
61760484Sobrien
61889857Sobrien		default:
619104834Sobrien			continue;
620104834Sobrien		}
621104834Sobrien
622104834Sobrien		va_start(ap, trap_oid);
623104834Sobrien		while ((v = va_arg(ap, const struct snmp_value *)) != NULL)
624104834Sobrien			pdu.bindings[pdu.nbindings++] = *v;
625104834Sobrien		va_end(ap);
62689857Sobrien
62789857Sobrien		if (snmp_pdu_auth_access(&pdu, &ip) != SNMP_CODE_OK) {
62889857Sobrien			syslog(LOG_DEBUG, "send trap to %s failed: no access",
62989857Sobrien			    t->comm);
63060484Sobrien			continue;
63160484Sobrien		}
63260484Sobrien
633104834Sobrien		if ((sndbuf = buf_alloc(1)) == NULL) {
634104834Sobrien			syslog(LOG_ERR, "trap send buffer: %m");
635104834Sobrien			return;
636104834Sobrien		}
63789857Sobrien
63889857Sobrien		snmp_output(&pdu, sndbuf, &sndlen, "TRAP");
63989857Sobrien
64089857Sobrien		if ((len = send(ta->socket, sndbuf, sndlen, 0)) == -1)
64189857Sobrien			syslog(LOG_ERR, "send: %m");
64289857Sobrien		else if ((size_t)len != sndlen)
64389857Sobrien			syslog(LOG_ERR, "send: short write %zu/%zu",
64489857Sobrien			    sndlen, (size_t)len);
64589857Sobrien
64689857Sobrien		free(sndbuf);
64789857Sobrien	}
64889857Sobrien}
64989857Sobrien
65089857Sobrien/*
65189857Sobrien * RFC 3413 SNMP Management Target MIB
65260484Sobrien */
65389857Sobrienstruct snmpd_target_stats *
65489857Sobrienbsnmpd_get_target_stats(void)
65560484Sobrien{
656104834Sobrien	return (&snmpd_target_stats);
657104834Sobrien}
658104834Sobrien
659104834Sobrienstruct target_address *
660104834Sobrientarget_first_address(void)
661104834Sobrien{
662104834Sobrien	return (SLIST_FIRST(&target_addresslist));
663104834Sobrien}
664104834Sobrien
665104834Sobrienstruct target_address *
666104834Sobrientarget_next_address(struct target_address *addrs)
667104834Sobrien{
668104834Sobrien	if (addrs == NULL)
669104834Sobrien		return (NULL);
670104834Sobrien
671104834Sobrien	return (SLIST_NEXT(addrs, ta));
672104834Sobrien}
673104834Sobrien
674104834Sobrienstruct target_address *
675218822Sdimtarget_new_address(char *aname)
676104834Sobrien{
67789857Sobrien	int cmp;
678104834Sobrien	struct target_address *addrs, *temp, *prev;
679104834Sobrien
68089857Sobrien	SLIST_FOREACH(addrs, &target_addresslist, ta)
68160484Sobrien		if (strcmp(aname, addrs->name) == 0)
68289857Sobrien			return (NULL);
68389857Sobrien
68489857Sobrien	if ((addrs = (struct target_address *)malloc(sizeof(*addrs))) == NULL)
68560484Sobrien		return (NULL);
68689857Sobrien
68789857Sobrien	memset(addrs, 0, sizeof(*addrs));
68860484Sobrien	strlcpy(addrs->name, aname, sizeof(addrs->name));
68989857Sobrien	addrs->timeout = 150;
69089857Sobrien	addrs->retry = 3; /* XXX */
69160484Sobrien
69289857Sobrien	if ((prev = SLIST_FIRST(&target_addresslist)) == NULL ||
69389857Sobrien	    strcmp(aname, prev->name) < 0) {
69489857Sobrien		SLIST_INSERT_HEAD(&target_addresslist, addrs, ta);
69589857Sobrien		return (addrs);
69689857Sobrien	}
69789857Sobrien
69889857Sobrien	SLIST_FOREACH(temp, &target_addresslist, ta) {
69989857Sobrien		if ((cmp = strcmp(aname, temp->name)) <= 0)
70089857Sobrien			break;
70189857Sobrien		prev = temp;
70260484Sobrien	}
70389857Sobrien
70489857Sobrien	if (temp == NULL || cmp < 0)
70589857Sobrien		SLIST_INSERT_AFTER(prev, addrs, ta);
70689857Sobrien	else if (cmp > 0)
70760484Sobrien		SLIST_INSERT_AFTER(temp, addrs, ta);
70860484Sobrien	else {
70960484Sobrien		syslog(LOG_ERR, "Target address %s exists", addrs->name);
710218822Sdim		free(addrs);
711218822Sdim		return (NULL);
712218822Sdim	}
713218822Sdim
714218822Sdim	return (addrs);
715218822Sdim}
716218822Sdim
717218822Sdimint
718218822Sdimtarget_activate_address(struct target_address *addrs)
719218822Sdim{
720218822Sdim	struct sockaddr_in sa;
72160484Sobrien
72260484Sobrien	if ((addrs->socket = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
72360484Sobrien		syslog(LOG_ERR, "socket(UDP): %m");
724218822Sdim		return (SNMP_ERR_RES_UNAVAIL);
72560484Sobrien	}
72660484Sobrien
72760484Sobrien	(void)shutdown(addrs->socket, SHUT_RD);
72860484Sobrien	memset(&sa, 0, sizeof(sa));
72960484Sobrien	sa.sin_len = sizeof(sa);
73060484Sobrien	sa.sin_family = AF_INET;
73160484Sobrien
73260484Sobrien	sa.sin_addr.s_addr = htonl((addrs->address[0] << 24) |
733130561Sobrien	    (addrs->address[1] << 16) | (addrs->address[2] << 8) |
73460484Sobrien	    (addrs->address[3] << 0));
73560484Sobrien	sa.sin_port = htons(addrs->address[4]) << 8 |
73660484Sobrien	     htons(addrs->address[5]) << 0;
73760484Sobrien
73860484Sobrien	if (connect(addrs->socket, (struct sockaddr *)&sa, sa.sin_len) == -1) {
73960484Sobrien		syslog(LOG_ERR, "connect(%s,%u): %m",
74060484Sobrien		    inet_ntoa(sa.sin_addr), ntohs(sa.sin_port));
74160484Sobrien		(void)close(addrs->socket);
74260484Sobrien		return (SNMP_ERR_GENERR);
74360484Sobrien	}
74460484Sobrien
74560484Sobrien	addrs->status = RowStatus_active;
74660484Sobrien
74760484Sobrien	return (SNMP_ERR_NOERROR);
74860484Sobrien}
74960484Sobrien
75060484Sobrienint
75160484Sobrientarget_delete_address(struct target_address *addrs)
75260484Sobrien{
75360484Sobrien	SLIST_REMOVE(&target_addresslist, addrs, target_address, ta);
75460484Sobrien	if (addrs->status == RowStatus_active)
75560484Sobrien		close(addrs->socket);
75660484Sobrien	free(addrs);
75760484Sobrien
75860484Sobrien	return (0);
75960484Sobrien}
76060484Sobrien
76160484Sobrienstruct target_param *
76260484Sobrientarget_first_param(void)
76360484Sobrien{
76460484Sobrien	return (SLIST_FIRST(&target_paramlist));
76560484Sobrien}
76660484Sobrien
76760484Sobrienstruct target_param *
76860484Sobrientarget_next_param(struct target_param *param)
76960484Sobrien{
77091041Sobrien	if (param == NULL)
77160484Sobrien		return (NULL);
77260484Sobrien
77360484Sobrien	return (SLIST_NEXT(param, tp));
774104834Sobrien}
775104834Sobrien
776218822Sdimstruct target_param *
77760484Sobrientarget_new_param(char *pname)
77860484Sobrien{
77960484Sobrien	int cmp;
78089857Sobrien	struct target_param *param, *temp, *prev;
78191041Sobrien
782104834Sobrien	SLIST_FOREACH(param, &target_paramlist, tp)
783104834Sobrien		if (strcmp(pname, param->name) == 0)
784104834Sobrien			return (NULL);
785104834Sobrien
786104834Sobrien	if ((param = (struct target_param *)malloc(sizeof(*param))) == NULL)
787104834Sobrien		return (NULL);
788104834Sobrien
789104834Sobrien	memset(param, 0, sizeof(*param));
790104834Sobrien	strlcpy(param->name, pname, sizeof(param->name));
791104834Sobrien
79260484Sobrien	if ((prev = SLIST_FIRST(&target_paramlist)) == NULL ||
79360484Sobrien	    strcmp(pname, prev->name) < 0) {
79460484Sobrien		SLIST_INSERT_HEAD(&target_paramlist, param, tp);
79560484Sobrien		return (param);
79660484Sobrien	}
79777298Sobrien
79860484Sobrien	SLIST_FOREACH(temp, &target_paramlist, tp) {
79960484Sobrien		if ((cmp = strcmp(pname, temp->name)) <= 0)
80060484Sobrien			break;
80160484Sobrien		prev = temp;
80260484Sobrien	}
80360484Sobrien
80433965Sjdp	if (temp == NULL || cmp < 0)
80533965Sjdp		SLIST_INSERT_AFTER(prev, param, tp);
80633965Sjdp	else if (cmp > 0)
807218822Sdim		SLIST_INSERT_AFTER(temp, param, tp);
808218822Sdim	else {
809218822Sdim		syslog(LOG_ERR, "Target parameter %s exists", param->name);
81033965Sjdp		free(param);
81133965Sjdp		return (NULL);
81233965Sjdp	}
81333965Sjdp
81460484Sobrien	return (param);
81560484Sobrien}
81660484Sobrien
817130561Sobrienint
81860484Sobrientarget_delete_param(struct target_param *param)
81960484Sobrien{
82060484Sobrien	SLIST_REMOVE(&target_paramlist, param, target_param, tp);
82160484Sobrien	free(param);
82260484Sobrien
823130561Sobrien	return (0);
82433965Sjdp}
82533965Sjdp
82633965Sjdpstruct target_notify *
827130561Sobrientarget_first_notify(void)
82833965Sjdp{
82933965Sjdp	return (SLIST_FIRST(&target_notifylist));
83033965Sjdp}
83189857Sobrien
83289857Sobrienstruct target_notify *
83389857Sobrientarget_next_notify(struct target_notify *notify)
83489857Sobrien{
83589857Sobrien	if (notify == NULL)
83633965Sjdp		return (NULL);
83733965Sjdp
83833965Sjdp	return (SLIST_NEXT(notify, tn));
83933965Sjdp}
84033965Sjdp
84133965Sjdpstruct target_notify *
84233965Sjdptarget_new_notify(char *nname)
84333965Sjdp{
84460484Sobrien	int cmp;
84560484Sobrien	struct target_notify *notify, *temp, *prev;
84660484Sobrien
84760484Sobrien	SLIST_FOREACH(notify, &target_notifylist, tn)
84860484Sobrien		if (strcmp(nname, notify->name) == 0)
84960484Sobrien			return (NULL);
850130561Sobrien
851130561Sobrien	if ((notify = (struct target_notify *)malloc(sizeof(*notify))) == NULL)
85260484Sobrien		return (NULL);
85360484Sobrien
85460484Sobrien	memset(notify, 0, sizeof(*notify));
85560484Sobrien	strlcpy(notify->name, nname, sizeof(notify->name));
85660484Sobrien
85760484Sobrien	if ((prev = SLIST_FIRST(&target_notifylist)) == NULL ||
85860484Sobrien	    strcmp(nname, prev->name) < 0) {
85989857Sobrien		SLIST_INSERT_HEAD(&target_notifylist, notify, tn);
860104834Sobrien		return (notify);
86189857Sobrien	}
86289857Sobrien
86360484Sobrien	SLIST_FOREACH(temp, &target_notifylist, tn) {
864130561Sobrien		if ((cmp = strcmp(nname, temp->name)) <= 0)
86589857Sobrien			break;
86660484Sobrien		prev = temp;
86760484Sobrien	}
86860484Sobrien
86960484Sobrien	if (temp == NULL || cmp < 0)
87060484Sobrien		SLIST_INSERT_AFTER(prev, notify, tn);
87160484Sobrien	else if (cmp > 0)
87289857Sobrien		SLIST_INSERT_AFTER(temp, notify, tn);
87389857Sobrien	else {
87489857Sobrien		syslog(LOG_ERR, "Notification target %s exists", notify->name);
87589857Sobrien		free(notify);
87689857Sobrien		return (NULL);
87760484Sobrien	}
87889857Sobrien
87960484Sobrien	return (notify);
88060484Sobrien}
88160484Sobrien
88289857Sobrienint
883104834Sobrientarget_delete_notify(struct target_notify *notify)
88489857Sobrien{
88560484Sobrien	SLIST_REMOVE(&target_notifylist, notify, target_notify, tn);
88689857Sobrien	free(notify);
88789857Sobrien
88889857Sobrien	return (0);
88989857Sobrien}
89089857Sobrien
89189857Sobrienvoid
89289857Sobrientarget_flush_all(void)
89360484Sobrien{
89489857Sobrien	struct target_address *addrs;
89560484Sobrien	struct target_param *param;
89689857Sobrien	struct target_notify *notify;
89789857Sobrien
89889857Sobrien	while ((addrs = SLIST_FIRST(&target_addresslist)) != NULL) {
89989857Sobrien		SLIST_REMOVE_HEAD(&target_addresslist, ta);
90089857Sobrien		if (addrs->status == RowStatus_active)
90160484Sobrien			close(addrs->socket);
90289857Sobrien		free(addrs);
90360484Sobrien	}
904130561Sobrien	SLIST_INIT(&target_addresslist);
90589857Sobrien
90689857Sobrien	while ((param = SLIST_FIRST(&target_paramlist)) != NULL) {
90789857Sobrien		SLIST_REMOVE_HEAD(&target_paramlist, tp);
90889857Sobrien		free(param);
90989857Sobrien	}
91089857Sobrien	SLIST_INIT(&target_paramlist);
91189857Sobrien
91260484Sobrien	while ((notify = SLIST_FIRST(&target_notifylist)) != NULL) {
91360484Sobrien		SLIST_REMOVE_HEAD(&target_notifylist, tn);
91460484Sobrien		free(notify);
91560484Sobrien	}
91660484Sobrien	SLIST_INIT(&target_notifylist);
91760484Sobrien}
91889857Sobrien