trap.c revision 311596
12061Sjkh/*
236622Scharnier * Copyright (c) 2001-2003
32061Sjkh *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
433611Sjb *	All rights reserved.
532427Sjb *
632427Sjb * Author: Harti Brandt <harti@freebsd.org>
736111Sjb *
833611Sjb * Copyright (c) 2010 The FreeBSD Foundation
932427Sjb * All rights reserved.
1032427Sjb *
112061Sjkh * Portions of this software were developed by Shteryana Sotirova Shopova
1215603Smarkm * under sponsorship from the FreeBSD Foundation.
1330169Sjkh *
1420710Sasami * Redistribution and use in source and binary forms, with or without
1520710Sasami * modification, are permitted provided that the following conditions
163197Scsgr * are met:
172061Sjkh * 1. Redistributions of source code must retain the above copyright
1812483Speter *    notice, this list of conditions and the following disclaimer.
1934509Sbde * 2. Redistributions in binary form must reproduce the above copyright
202160Scsgr *    notice, this list of conditions and the following disclaimer in the
212834Swollman *    documentation and/or other materials provided with the distribution.
222061Sjkh *
232061Sjkh * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
242160Scsgr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2517308Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2619320Sadam * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
2727788Sasami * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2830169Sjkh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2925980Sasami * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
301594Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3117308Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3217308Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3327910Sasami * SUCH DAMAGE.
3427910Sasami *
3527910Sasami * $Begemot: bsnmp/snmpd/trap.c,v 1.9 2005/10/04 11:21:39 brandt_h Exp $
3617308Speter *
3717308Speter * TrapSinkTable
3817308Speter */
3919175Sbde#include <sys/types.h>
4019175Sbde#include <sys/queue.h>
4119175Sbde#include <sys/sysctl.h>
4219175Sbde#include <sys/un.h>
4317308Speter#include <stdint.h>
4427910Sasami#include <stdio.h>
4534509Sbde#include <stdlib.h>
4627910Sasami#include <stdarg.h>
4717308Speter#include <stdarg.h>
482061Sjkh#include <string.h>
492061Sjkh#include <ctype.h>
501594Srgrimes#include <syslog.h>
5130169Sjkh#include <unistd.h>
5230169Sjkh#include <netinet/in.h>
5330169Sjkh#include <arpa/inet.h>
5430169Sjkh
5530169Sjkh#include "snmpmod.h"
5630169Sjkh#include "snmpd.h"
5730169Sjkh#include "tree.h"
5830169Sjkh#include "oid.h"
597407Srgrimes
607108Sphkstruct trapsink_list trapsink_list = TAILQ_HEAD_INITIALIZER(trapsink_list);
617108Sphk
627108Sphk/* List of target addresses */
637407Srgrimesstatic struct target_addresslist target_addresslist =
647407Srgrimes    SLIST_HEAD_INITIALIZER(target_addresslist);
657407Srgrimes
667108Sphk/* List of target parameters */
672061Sjkhstatic struct target_paramlist target_paramlist =
682061Sjkh    SLIST_HEAD_INITIALIZER(target_paramlist);
692061Sjkh
7017308Speter/* List of notification targets */
712061Sjkhstatic struct target_notifylist target_notifylist =
722061Sjkh    SLIST_HEAD_INITIALIZER(target_notifylist);
732061Sjkh
742061Sjkhstatic const struct asn_oid oid_begemotTrapSinkTable =
752061Sjkh    OIDX_begemotTrapSinkTable;
7635427Sbdestatic const struct asn_oid oid_sysUpTime = OIDX_sysUpTime;
7735427Sbdestatic const struct asn_oid oid_snmpTrapOID = OIDX_snmpTrapOID;
7830169Sjkh
792626Scsgrstruct trapsink_dep {
802061Sjkh	struct snmp_dependency dep;
812061Sjkh	u_int	set;
822061Sjkh	u_int	status;
832061Sjkh	u_char	comm[SNMP_COMMUNITY_MAXLEN + 1];
842061Sjkh	u_int	version;
852061Sjkh	u_int	rb;
8619320Sadam	u_int	rb_status;
872061Sjkh	u_int	rb_version;
882061Sjkh	u_char	rb_comm[SNMP_COMMUNITY_MAXLEN + 1];
892061Sjkh};
902061Sjkhenum {
912061Sjkh	TDEP_STATUS	= 0x0001,
922061Sjkh	TDEP_COMM	= 0x0002,
932061Sjkh	TDEP_VERSION	= 0x0004,
942061Sjkh
952061Sjkh	TDEP_CREATE	= 0x0001,
962061Sjkh	TDEP_MODIFY	= 0x0002,
972061Sjkh	TDEP_DESTROY	= 0x0004,
982834Swollman};
992834Swollman
1002834Swollmanstatic int
1012834Swollmantrapsink_create(struct trapsink_dep *tdep)
1022834Swollman{
1032834Swollman	struct trapsink *t;
1041594Srgrimes	struct sockaddr_in sa;
1054486Sphk
1064486Sphk	if ((t = malloc(sizeof(*t))) == NULL)
1074486Sphk		return (SNMP_ERR_RES_UNAVAIL);
1084486Sphk
1094486Sphk	t->index = tdep->dep.idx;
1102061Sjkh	t->status = TRAPSINK_NOT_READY;
1112061Sjkh	t->comm[0] = '\0';
11225979Sjkh	t->version = TRAPSINK_V2;
11325979Sjkh
11425979Sjkh	if ((t->socket = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
11525979Sjkh		syslog(LOG_ERR, "socket(UDP): %m");
1162061Sjkh		free(t);
11725979Sjkh		return (SNMP_ERR_RES_UNAVAIL);
1182061Sjkh	}
1192061Sjkh	(void)shutdown(t->socket, SHUT_RD);
12017308Speter	memset(&sa, 0, sizeof(sa));
1212061Sjkh	sa.sin_len = sizeof(sa);
1222061Sjkh	sa.sin_family = AF_INET;
1232061Sjkh	sa.sin_addr.s_addr = htonl((t->index.subs[0] << 24) |
1242061Sjkh	    (t->index.subs[1] << 16) | (t->index.subs[2] << 8) |
1252061Sjkh	    (t->index.subs[3] << 0));
12612483Speter	sa.sin_port = htons(t->index.subs[4]);
12712483Speter
12812483Speter	if (connect(t->socket, (struct sockaddr *)&sa, sa.sin_len) == -1) {
12912483Speter		syslog(LOG_ERR, "connect(%s,%u): %m",
1302061Sjkh		    inet_ntoa(sa.sin_addr), ntohs(sa.sin_port));
13135479Sbde		(void)close(t->socket);
1328854Srgrimes		free(t);
1332061Sjkh		return (SNMP_ERR_GENERR);
1342061Sjkh	}
13512483Speter
1362061Sjkh	if (tdep->set & TDEP_VERSION)
13735479Sbde		t->version = tdep->version;
13835479Sbde	if (tdep->set & TDEP_COMM)
13935479Sbde		strcpy(t->comm, tdep->comm);
14035479Sbde
14135479Sbde	if (t->comm[0] != '\0')
14235479Sbde		t->status = TRAPSINK_NOT_IN_SERVICE;
14335479Sbde
14435479Sbde	/* look whether we should activate */
14535479Sbde	if (tdep->status == 4) {
14635462Sjkh		if (t->status == TRAPSINK_NOT_READY) {
14735462Sjkh			if (t->socket != -1)
14818714Sache				(void)close(t->socket);
14917308Speter			free(t);
15034541Sbde			return (SNMP_ERR_INCONS_VALUE);
15134575Sbde		}
15234575Sbde		t->status = TRAPSINK_ACTIVE;
15334575Sbde	}
15434592Sbde
15517308Speter	INSERT_OBJECT_OID(t, &trapsink_list);
15634575Sbde
15735427Sbde	tdep->rb |= TDEP_CREATE;
15834575Sbde
15935427Sbde	return (SNMP_ERR_NOERROR);
16034575Sbde}
16115603Smarkm
16217308Speterstatic void
16317308Spetertrapsink_free(struct trapsink *t)
16417308Speter{
16517308Speter	TAILQ_REMOVE(&trapsink_list, t, link);
16617308Speter	if (t->socket != -1)
16717308Speter		(void)close(t->socket);
16817308Speter	free(t);
16917308Speter}
17017308Speter
17118362Sjkhstatic int
17219966Sachetrapsink_modify(struct trapsink *t, struct trapsink_dep *tdep)
17318362Sjkh{
17417308Speter	tdep->rb_status = t->status;
17527910Sasami	tdep->rb_version = t->version;
17617308Speter	strcpy(tdep->rb_comm, t->comm);
17717308Speter
17817308Speter	if (tdep->set & TDEP_STATUS) {
17936074Sbde		/* if we are active and should move to not_in_service do
18027910Sasami		 * this first */
18136074Sbde		if (tdep->status == 2 && tdep->rb_status == TRAPSINK_ACTIVE) {
18236074Sbde			t->status = TRAPSINK_NOT_IN_SERVICE;
18327910Sasami			tdep->rb |= TDEP_MODIFY;
18417308Speter		}
1852061Sjkh	}
18627910Sasami
1872061Sjkh	if (tdep->set & TDEP_VERSION)
18836074Sbde		t->version = tdep->version;
18927910Sasami	if (tdep->set & TDEP_COMM)
1902061Sjkh		strcpy(t->comm, tdep->comm);
19117308Speter
19227910Sasami	if (tdep->set & TDEP_STATUS) {
19317308Speter		/* if we were inactive and should go active - do this now */
19427910Sasami		if (tdep->status == 1 && tdep->rb_status != TRAPSINK_ACTIVE) {
19527910Sasami			if (t->comm[0] == '\0') {
19627910Sasami				t->status = tdep->rb_status;
19717308Speter				t->version = tdep->rb_version;
19827910Sasami				strcpy(t->comm, tdep->rb_comm);
19917308Speter				return (SNMP_ERR_INCONS_VALUE);
20027910Sasami			}
20127910Sasami			t->status = TRAPSINK_ACTIVE;
20227910Sasami			tdep->rb |= TDEP_MODIFY;
20327910Sasami		}
20436622Scharnier	}
20536622Scharnier	return (SNMP_ERR_NOERROR);
20627910Sasami}
20727910Sasami
20827910Sasamistatic int
20927910Sasamitrapsink_unmodify(struct trapsink *t, struct trapsink_dep *tdep)
21027910Sasami{
21127910Sasami	if (tdep->set & TDEP_STATUS)
21234509Sbde		t->status = tdep->rb_status;
21327910Sasami	if (tdep->set & TDEP_VERSION)
21427910Sasami		t->version = tdep->rb_version;
21527910Sasami	if (tdep->set & TDEP_COMM)
21636423Speter		strcpy(t->comm, tdep->rb_comm);
21736423Speter
21827910Sasami	return (SNMP_ERR_NOERROR);
21936423Speter}
22035479Sbde
22127910Sasamistatic int
22227910Sasamitrapsink_destroy(struct snmp_context *ctx __unused, struct trapsink *t,
22334688Sbde    struct trapsink_dep *tdep)
22434688Sbde{
22527910Sasami	t->status = TRAPSINK_DESTROY;
22635427Sbde	tdep->rb_status = t->status;
22727910Sasami	tdep->rb |= TDEP_DESTROY;
22835427Sbde	return (SNMP_ERR_NOERROR);
22927910Sasami}
23035427Sbde
23127910Sasamistatic int
23227910Sasamitrapsink_undestroy(struct trapsink *t, struct trapsink_dep *tdep)
23327910Sasami{
23427910Sasami	t->status = tdep->rb_status;
23527910Sasami	return (SNMP_ERR_NOERROR);
23627910Sasami}
23727910Sasami
23827910Sasamistatic int
23927910Sasamitrapsink_dep(struct snmp_context *ctx, struct snmp_dependency *dep,
24017308Speter    enum snmp_depop op)
24117308Speter{
24227910Sasami	struct trapsink_dep *tdep = (struct trapsink_dep *)dep;
24317308Speter	struct trapsink *t;
24427910Sasami
24527910Sasami	t = FIND_OBJECT_OID(&trapsink_list, &dep->idx, 0);
24627910Sasami
24727910Sasami	switch (op) {
24833133Sadam
24917466Speter	  case SNMP_DEPOP_COMMIT:
25017308Speter		if (tdep->set & TDEP_STATUS) {
25127910Sasami			switch (tdep->status) {
25217308Speter
25334688Sbde			  case 1:
25434688Sbde			  case 2:
25536074Sbde				if (t == NULL)
25636074Sbde					return (SNMP_ERR_INCONS_VALUE);
25736074Sbde				return (trapsink_modify(t, tdep));
25836074Sbde
25934688Sbde			  case 4:
26034688Sbde			  case 5:
26133133Sadam				if (t != NULL)
26217308Speter					return (SNMP_ERR_INCONS_VALUE);
26317308Speter				return (trapsink_create(tdep));
26427910Sasami
26517308Speter			  case 6:
26636074Sbde				if (t == NULL)
26727910Sasami					return (SNMP_ERR_NOERROR);
26817308Speter				return (trapsink_destroy(ctx, t, tdep));
26917308Speter			}
27027910Sasami		} else if (tdep->set != 0)
27117308Speter			return (trapsink_modify(t, tdep));
27236074Sbde
27327910Sasami		return (SNMP_ERR_NOERROR);
27427910Sasami
27517308Speter	  case SNMP_DEPOP_ROLLBACK:
27617308Speter		if (tdep->rb & TDEP_CREATE) {
27727910Sasami			trapsink_free(t);
27817308Speter			return (SNMP_ERR_NOERROR);
27936074Sbde		}
28027910Sasami		if (tdep->rb & TDEP_MODIFY)
28133133Sadam			return (trapsink_unmodify(t, tdep));
28217308Speter		if(tdep->rb & TDEP_DESTROY)
28317308Speter			return (trapsink_undestroy(t, tdep));
28427910Sasami		return (SNMP_ERR_NOERROR);
28517308Speter
28636074Sbde	  case SNMP_DEPOP_FINISH:
28717308Speter		if ((tdep->rb & TDEP_DESTROY) && t != NULL &&
28817308Speter		    ctx->code == SNMP_RET_OK)
28927910Sasami			trapsink_free(t);
29017308Speter		return (SNMP_ERR_NOERROR);
29136074Sbde	}
29233133Sadam	abort();
29317308Speter}
29417308Speter
29534509Sbdeint
29617308Speterop_trapsink(struct snmp_context *ctx, struct snmp_value *value,
29736429Speter    u_int sub, u_int iidx, enum snmp_op op)
29835851Sjb{
29935851Sjb	struct trapsink *t;
30035851Sjb	u_char ipa[4];
30135851Sjb	int32_t port;
30236074Sbde	struct asn_oid idx;
30333133Sadam	struct trapsink_dep *tdep;
30417962Speter	u_char *p;
30517962Speter
30635851Sjb	t = NULL;		/* gcc */
30717962Speter
30836074Sbde	switch (op) {
30933133Sadam
31033133Sadam	  case SNMP_OP_GETNEXT:
31117962Speter		if ((t = NEXT_OBJECT_OID(&trapsink_list, &value->var, sub)) == NULL)
31217962Speter			return (SNMP_ERR_NOSUCHNAME);
31327910Sasami		index_append(&value->var, sub, &t->index);
31417962Speter		break;
31536074Sbde
31633133Sadam	  case SNMP_OP_GET:
31735479Sbde		if ((t = FIND_OBJECT_OID(&trapsink_list, &value->var, sub)) == NULL)
31817308Speter			return (SNMP_ERR_NOSUCHNAME);
31917308Speter		break;
32027910Sasami
32117308Speter	  case SNMP_OP_SET:
32236074Sbde		if (index_decode(&value->var, sub, iidx, ipa, &port) ||
32335479Sbde		    port == 0 || port > 65535)
32417308Speter			return (SNMP_ERR_NO_CREATION);
32517308Speter		t = FIND_OBJECT_OID(&trapsink_list, &value->var, sub);
32635427Sbde
32735427Sbde		asn_slice_oid(&idx, &value->var, sub, value->var.len);
32836074Sbde
32935427Sbde		tdep = (struct trapsink_dep *)snmp_dep_lookup(ctx,
33035427Sbde		    &oid_begemotTrapSinkTable, &idx,
33127910Sasami		    sizeof(*tdep), trapsink_dep);
33217962Speter		if (tdep == NULL)
33336074Sbde			return (SNMP_ERR_RES_UNAVAIL);
3342061Sjkh
33517308Speter		switch (value->var.subs[sub - 1]) {
33627910Sasami
33727910Sasami		  case LEAF_begemotTrapSinkStatus:
33827910Sasami			if (tdep->set & TDEP_STATUS)
33927910Sasami				return (SNMP_ERR_INCONS_VALUE);
34027910Sasami			switch (value->v.integer) {
34136074Sbde
34227910Sasami			  case 1:
34327910Sasami			  case 2:
34417308Speter				if (t == NULL)
34517308Speter					return (SNMP_ERR_INCONS_VALUE);
34617308Speter				break;
34717308Speter
34817308Speter			  case 4:
34917308Speter			  case 5:
35017308Speter				if (t != NULL)
35112483Speter					return (SNMP_ERR_INCONS_VALUE);
35217308Speter				break;
35312483Speter
35436074Sbde			  case 6:
35512483Speter				break;
3562061Sjkh
35717962Speter			  default:
35817962Speter				return (SNMP_ERR_WRONG_VALUE);
35936074Sbde			}
36017962Speter			tdep->status = value->v.integer;
36117962Speter			tdep->set |= TDEP_STATUS;
36233595Snate			return (SNMP_ERR_NOERROR);
36333595Snate
36436074Sbde		  case LEAF_begemotTrapSinkComm:
36533595Snate			if (tdep->set & TDEP_COMM)
36633595Snate				return (SNMP_ERR_INCONS_VALUE);
36717962Speter			if (value->v.octetstring.len == 0 ||
36817962Speter			    value->v.octetstring.len > SNMP_COMMUNITY_MAXLEN)
36936074Sbde				return (SNMP_ERR_WRONG_VALUE);
3702061Sjkh			for (p = value->v.octetstring.octets;
37117308Speter			     p < value->v.octetstring.octets + value->v.octetstring.len;
37217308Speter			     p++) {
37317308Speter				if (!isascii(*p) || !isprint(*p))
37417308Speter					return (SNMP_ERR_WRONG_VALUE);
37517308Speter			}
37617308Speter			tdep->set |= TDEP_COMM;
3772302Spaul			strncpy(tdep->comm, value->v.octetstring.octets,
3782302Spaul			    value->v.octetstring.len);
3792302Spaul			tdep->comm[value->v.octetstring.len] = '\0';
38035462Sjkh			return (SNMP_ERR_NOERROR);
3812302Spaul
38218714Sache		  case LEAF_begemotTrapSinkVersion:
38310760Sache			if (tdep->set & TDEP_VERSION)
38418714Sache				return (SNMP_ERR_INCONS_VALUE);
3852302Spaul			if (value->v.integer != TRAPSINK_V1 &&
38610760Sache			    value->v.integer != TRAPSINK_V2)
38718714Sache				return (SNMP_ERR_WRONG_VALUE);
38810760Sache			tdep->version = value->v.integer;
38910760Sache			tdep->set |= TDEP_VERSION;
3902302Spaul			return (SNMP_ERR_NOERROR);
3912302Spaul		}
3922302Spaul		if (t == NULL)
3932302Spaul			return (SNMP_ERR_INCONS_NAME);
39436074Sbde		else
3952302Spaul			return (SNMP_ERR_NOT_WRITEABLE);
3962302Spaul
39717308Speter
39817308Speter	  case SNMP_OP_ROLLBACK:
39917308Speter	  case SNMP_OP_COMMIT:
40017308Speter		return (SNMP_ERR_NOERROR);
40117308Speter	}
40217308Speter
4032061Sjkh	switch (value->var.subs[sub - 1]) {
40417308Speter
4052061Sjkh	  case LEAF_begemotTrapSinkStatus:
40636074Sbde		value->v.integer = t->status;
40736074Sbde		break;
40836074Sbde
40936074Sbde	  case LEAF_begemotTrapSinkComm:
41036074Sbde		return (string_get(value, t->comm, -1));
41136074Sbde
41236074Sbde	  case LEAF_begemotTrapSinkVersion:
41336074Sbde		value->v.integer = t->version;
41430169Sjkh		break;
41536074Sbde
41617308Speter	}
41717308Speter	return (SNMP_ERR_NOERROR);
41836074Sbde}
41917308Speter
4202061Sjkhstatic void
42117308Spetersnmp_create_v1_trap(struct snmp_pdu *pdu, char *com,
42217308Speter    const struct asn_oid *trap_oid)
42317308Speter{
42417308Speter	memset(pdu, 0, sizeof(*pdu));
42517308Speter	strlcpy(pdu->community, com, sizeof(pdu->community));
42617308Speter
4273626Swollman	pdu->version = SNMP_V1;
4283626Swollman	pdu->type = SNMP_PDU_TRAP;
4293626Swollman	pdu->enterprise = systemg.object_id;
4303626Swollman	memcpy(pdu->agent_addr, snmpd.trap1addr, 4);
43136074Sbde	pdu->generic_trap = trap_oid->subs[trap_oid->len - 1] - 1;
43236074Sbde	pdu->specific_trap = 0;
43336074Sbde	pdu->time_stamp = get_ticks() - start_tick;
43436074Sbde	pdu->nbindings = 0;
43536074Sbde}
43636074Sbde
43736074Sbdestatic void
43836074Sbdesnmp_create_v2_trap(struct snmp_pdu *pdu, char *com,
43930169Sjkh    const struct asn_oid *trap_oid)
44036074Sbde{
4413626Swollman	memset(pdu, 0, sizeof(*pdu));
4423626Swollman	strlcpy(pdu->community, com, sizeof(pdu->community));
44336074Sbde
4443626Swollman	pdu->version = SNMP_V2c;
4453626Swollman	pdu->type = SNMP_PDU_TRAP2;
44617308Speter	pdu->request_id = reqid_next(trap_reqid);
44717308Speter	pdu->error_index = 0;
44817308Speter	pdu->error_status = SNMP_ERR_NOERROR;
44917308Speter
45017308Speter	pdu->bindings[0].var = oid_sysUpTime;
45117308Speter	pdu->bindings[0].var.subs[pdu->bindings[0].var.len++] = 0;
45217308Speter	pdu->bindings[0].syntax = SNMP_SYNTAX_TIMETICKS;
45317308Speter	pdu->bindings[0].v.uint32 = get_ticks() - start_tick;
45417308Speter
45517308Speter	pdu->bindings[1].var = oid_snmpTrapOID;
4563626Swollman	pdu->bindings[1].var.subs[pdu->bindings[1].var.len++] = 0;
45717308Speter	pdu->bindings[1].syntax = SNMP_SYNTAX_OID;
45817308Speter	pdu->bindings[1].v.oid = *trap_oid;
45917308Speter
46017308Speter	pdu->nbindings = 2;
46136074Sbde}
46217308Speter
46317308Speterstatic void
46417308Spetersnmp_create_v3_trap(struct snmp_pdu *pdu, struct target_param *target,
46517308Speter    const struct asn_oid *trap_oid)
46617308Speter{
46717308Speter	struct usm_user *usmuser;
46817308Speter
46927910Sasami	memset(pdu, 0, sizeof(*pdu));
47027910Sasami
47127910Sasami	pdu->version = SNMP_V3;
47236074Sbde	pdu->type = SNMP_PDU_TRAP2;
47336442Speter	pdu->request_id = reqid_next(trap_reqid);
47436442Speter	pdu->error_index = 0;
47536442Speter	pdu->error_status = SNMP_ERR_NOERROR;
47636442Speter
47736442Speter	pdu->bindings[0].var = oid_sysUpTime;
47836429Speter	pdu->bindings[0].var.subs[pdu->bindings[0].var.len++] = 0;
47936429Speter	pdu->bindings[0].syntax = SNMP_SYNTAX_TIMETICKS;
48036429Speter	pdu->bindings[0].v.uint32 = get_ticks() - start_tick;
48136429Speter
48236429Speter	pdu->bindings[1].var = oid_snmpTrapOID;
48336429Speter	pdu->bindings[1].var.subs[pdu->bindings[1].var.len++] = 0;
48427910Sasami	pdu->bindings[1].syntax = SNMP_SYNTAX_OID;
48536074Sbde	pdu->bindings[1].v.oid = *trap_oid;
48636074Sbde
48730113Sjkh	pdu->nbindings = 2;
48836074Sbde
48936074Sbde	update_snmpd_engine_time();
49030113Sjkh
49136074Sbde	memcpy(pdu->engine.engine_id, snmpd_engine.engine_id,
49236074Sbde	    snmpd_engine.engine_len);
49336074Sbde	pdu->engine.engine_len = snmpd_engine.engine_len;
49430113Sjkh	pdu->engine.engine_boots = snmpd_engine.engine_boots;
49536429Speter	pdu->engine.engine_time = snmpd_engine.engine_time;
49636429Speter	pdu->engine.max_msg_size = snmpd_engine.max_msg_size;
49736429Speter	strlcpy(pdu->user.sec_name, target->secname,
49829938Smckay	    sizeof(pdu->user.sec_name));
49936074Sbde	pdu->security_model = target->sec_model;
50029938Smckay
50117308Speter	pdu->context_engine_len = snmpd_engine.engine_len;
50217308Speter	memcpy(pdu->context_engine, snmpd_engine.engine_id,
50317308Speter	    snmpd_engine.engine_len);
50417308Speter
50517308Speter	if (target->sec_model == SNMP_SECMODEL_USM &&
50627910Sasami	    target->sec_level != SNMP_noAuthNoPriv) {
50727910Sasami	    	usmuser = usm_find_user(pdu->engine.engine_id,
50827910Sasami	    	   pdu->engine.engine_len, pdu->user.sec_name);
50917308Speter		if (usmuser != NULL) {
51017308Speter			pdu->user.auth_proto = usmuser->suser.auth_proto;
51134520Sbde			pdu->user.priv_proto = usmuser->suser.priv_proto;
51236074Sbde			memcpy(pdu->user.auth_key, usmuser->suser.auth_key,
51336074Sbde			    sizeof(pdu->user.auth_key));
51436074Sbde			memcpy(pdu->user.priv_key, usmuser->suser.priv_key,
51530113Sjkh			    sizeof(pdu->user.priv_key));
51634520Sbde		}
51717308Speter		snmp_pdu_init_secparams(pdu);
51817308Speter	}
51917308Speter}
52017308Speter
52114119Spetervoid
5222061Sjkhsnmp_send_trap(const struct asn_oid *trap_oid, ...)
5237130Srgrimes{
5247130Srgrimes	struct snmp_pdu pdu;
5257130Srgrimes	struct trapsink *t;
5262061Sjkh	const struct snmp_value *v;
52736074Sbde	struct target_notify *n;
52836074Sbde	struct target_address *ta;
52936074Sbde	struct target_param *tp;
53036074Sbde
53136074Sbde	va_list ap;
53236074Sbde	u_char *sndbuf;
53336074Sbde	char *tag;
53436074Sbde	size_t sndlen;
53536074Sbde	ssize_t len;
53636074Sbde	int32_t ip;
53730169Sjkh
53836074Sbde	TAILQ_FOREACH(t, &trapsink_list, link) {
5393197Scsgr		if (t->status != TRAPSINK_ACTIVE)
54030169Sjkh			continue;
54136074Sbde
54236074Sbde		if (t->version == TRAPSINK_V1)
54336074Sbde			snmp_create_v1_trap(&pdu, t->comm, trap_oid);
54436074Sbde		else
54536074Sbde			snmp_create_v2_trap(&pdu, t->comm, trap_oid);
54636074Sbde
54730169Sjkh		va_start(ap, trap_oid);
54836074Sbde		while ((v = va_arg(ap, const struct snmp_value *)) != NULL)
54930169Sjkh			pdu.bindings[pdu.nbindings++] = *v;
55032427Sjb		va_end(ap);
55136074Sbde
55232427Sjb		if (snmp_pdu_auth_access(&pdu, &ip) != SNMP_CODE_OK) {
55336074Sbde			syslog(LOG_DEBUG, "send trap to %s failed: no access",
55436074Sbde			    t->comm);
55536074Sbde			continue;
55636074Sbde		}
55736074Sbde
55836074Sbde		if ((sndbuf = buf_alloc(1)) == NULL) {
55936074Sbde			syslog(LOG_ERR, "trap send buffer: %m");
56036074Sbde			return;
56136074Sbde		}
56236074Sbde
5637281Srgrimes		snmp_output(&pdu, sndbuf, &sndlen, "TRAP");
56436074Sbde
5653242Spaul		if ((len = send(t->socket, sndbuf, sndlen, 0)) == -1)
56636074Sbde			syslog(LOG_ERR, "send: %m");
56736074Sbde		else if ((size_t)len != sndlen)
56836074Sbde			syslog(LOG_ERR, "send: short write %zu/%zu",
56936074Sbde			    sndlen, (size_t)len);
57030169Sjkh
57130169Sjkh		free(sndbuf);
57236074Sbde	}
57330169Sjkh
57436074Sbde	SLIST_FOREACH(n, &target_notifylist, tn) {
57536074Sbde		if (n->status != RowStatus_active || n->taglist[0] == '\0')
57636074Sbde			continue;
57736074Sbde
57836074Sbde		SLIST_FOREACH(ta, &target_addresslist, ta)
57936074Sbde			if ((tag = strstr(ta->taglist, n->taglist)) != NULL  &&
58036074Sbde			    (tag[strlen(n->taglist)] == ' ' ||
58136074Sbde			     tag[strlen(n->taglist)] == '\0' ||
58236074Sbde			     tag[strlen(n->taglist)] == '\t' ||
5832061Sjkh			     tag[strlen(n->taglist)] == '\r' ||
58417308Speter			     tag[strlen(n->taglist)] == '\n') &&
58517308Speter			     ta->status == RowStatus_active)
58617308Speter				break;
58727910Sasami		if (ta == NULL)
58827910Sasami			continue;
58936573Speter
5905366Snate		SLIST_FOREACH(tp, &target_paramlist, tp)
59127910Sasami			if (strcmp(tp->name, ta->paramname) == 0 &&
59227910Sasami			    tp->status == 1)
59327910Sasami				break;
59427910Sasami		if (tp == NULL)
59527910Sasami			continue;
59627910Sasami
59727910Sasami		switch (tp->mpmodel) {
59827910Sasami		case SNMP_MPM_SNMP_V1:
59927910Sasami			snmp_create_v1_trap(&pdu, tp->secname, trap_oid);
60027910Sasami			break;
60127910Sasami
60227910Sasami		case SNMP_MPM_SNMP_V2c:
60336374Ssos			snmp_create_v2_trap(&pdu, tp->secname, trap_oid);
60436374Ssos			break;
60536374Ssos
60636419Speter		case SNMP_MPM_SNMP_V3:
60736585Speter			snmp_create_v3_trap(&pdu, tp, trap_oid);
60836419Speter			break;
60936074Sbde
61036074Sbde		default:
61136074Sbde			continue;
61234575Sbde		}
61327910Sasami
6145366Snate		va_start(ap, trap_oid);
61517308Speter		while ((v = va_arg(ap, const struct snmp_value *)) != NULL)
61635427Sbde			pdu.bindings[pdu.nbindings++] = *v;
61717308Speter		va_end(ap);
61835427Sbde
61935427Sbde		if (snmp_pdu_auth_access(&pdu, &ip) != SNMP_CODE_OK) {
62035427Sbde			syslog(LOG_DEBUG, "send trap to %s failed: no access",
62135427Sbde			    t->comm);
62235427Sbde			continue;
62335427Sbde		}
62435427Sbde
62535427Sbde		if ((sndbuf = buf_alloc(1)) == NULL) {
62635427Sbde			syslog(LOG_ERR, "trap send buffer: %m");
62735427Sbde			return;
62835427Sbde		}
62935427Sbde
63035427Sbde		snmp_output(&pdu, sndbuf, &sndlen, "TRAP");
63135427Sbde
63235427Sbde		if ((len = send(ta->socket, sndbuf, sndlen, 0)) == -1)
63334541Sbde			syslog(LOG_ERR, "send: %m");
63435427Sbde		else if ((size_t)len != sndlen)
63524754Sjdp			syslog(LOG_ERR, "send: short write %zu/%zu",
63634541Sbde			    sndlen, (size_t)len);
63736444Speter
63836444Speter		free(sndbuf);
63936470Sjhay	}
64036444Speter}
64136444Speter
64235427Sbde/*
64335427Sbde * RFC 3413 SNMP Management Target MIB
64435427Sbde */
64534541Sbdestruct snmpd_target_stats *
64635427Sbdebsnmpd_get_target_stats(void)
64734541Sbde{
64835427Sbde	return (&snmpd_target_stats);
6498295Srgrimes}
65034541Sbde
65134541Sbdestruct target_address *
65235427Sbdetarget_first_address(void)
65335427Sbde{
65434541Sbde	return (SLIST_FIRST(&target_addresslist));
65535427Sbde}
65635427Sbde
65735427Sbdestruct target_address *
65835427Sbdetarget_next_address(struct target_address *addrs)
65935427Sbde{
66034541Sbde	if (addrs == NULL)
66135427Sbde		return (NULL);
66235427Sbde
66335427Sbde	return (SLIST_NEXT(addrs, ta));
66435427Sbde}
66534541Sbde
66635427Sbdestruct target_address *
66735427Sbdetarget_new_address(char *aname)
66836397Ssos{
66936444Speter	int cmp;
67035427Sbde	struct target_address *addrs, *temp, *prev;
67134541Sbde
67236074Sbde	SLIST_FOREACH(addrs, &target_addresslist, ta)
67336074Sbde		if (strcmp(aname, addrs->name) == 0)
67436074Sbde			return (NULL);
67530113Sjkh
6768489Srgrimes	if ((addrs = (struct target_address *)malloc(sizeof(*addrs))) == NULL)
67734541Sbde		return (NULL);
67835427Sbde
67935427Sbde	memset(addrs, 0, sizeof(*addrs));
68035427Sbde	strlcpy(addrs->name, aname, sizeof(addrs->name));
68135427Sbde	addrs->timeout = 150;
68235427Sbde	addrs->retry = 3; /* XXX */
68335427Sbde
68435427Sbde	if ((prev = SLIST_FIRST(&target_addresslist)) == NULL ||
68535427Sbde	    strcmp(aname, prev->name) < 0) {
68635427Sbde		SLIST_INSERT_HEAD(&target_addresslist, addrs, ta);
68735427Sbde		return (addrs);
68835427Sbde	}
68935427Sbde
69035427Sbde	SLIST_FOREACH(temp, &target_addresslist, ta) {
69136074Sbde		if ((cmp = strcmp(aname, temp->name)) <= 0)
69235427Sbde			break;
69335427Sbde		prev = temp;
69434541Sbde	}
69536074Sbde
6962160Scsgr	if (temp == NULL || cmp < 0)
69734541Sbde		SLIST_INSERT_AFTER(prev, addrs, ta);
69834541Sbde	else if (cmp > 0)
69936074Sbde		SLIST_INSERT_AFTER(temp, addrs, ta);
7002626Scsgr	else {
7012061Sjkh		syslog(LOG_ERR, "Target address %s exists", addrs->name);
70236589Sjhay		free(addrs);
70336589Sjhay		return (NULL);
70436589Sjhay	}
70536589Sjhay
70636589Sjhay	return (addrs);
70736589Sjhay}
70836589Sjhay
70917308Speterint
71017308Spetertarget_activate_address(struct target_address *addrs)
71117308Speter{
71227910Sasami	struct sockaddr_in sa;
71327910Sasami
71427910Sasami	if ((addrs->socket = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
71527910Sasami		syslog(LOG_ERR, "socket(UDP): %m");
71627910Sasami		return (SNMP_ERR_RES_UNAVAIL);
71727910Sasami	}
71817308Speter
71911806Sphk	(void)shutdown(addrs->socket, SHUT_RD);
72019175Sbde	memset(&sa, 0, sizeof(sa));
72127910Sasami	sa.sin_len = sizeof(sa);
72227910Sasami	sa.sin_family = AF_INET;
72327910Sasami
72427910Sasami	sa.sin_addr.s_addr = htonl((addrs->address[0] << 24) |
72527910Sasami	    (addrs->address[1] << 16) | (addrs->address[2] << 8) |
72627910Sasami	    (addrs->address[3] << 0));
72727910Sasami	sa.sin_port = htons(addrs->address[4]) << 8 |
72827910Sasami	     htons(addrs->address[5]) << 0;
72927910Sasami
73027910Sasami	if (connect(addrs->socket, (struct sockaddr *)&sa, sa.sin_len) == -1) {
73127910Sasami		syslog(LOG_ERR, "connect(%s,%u): %m",
73227910Sasami		    inet_ntoa(sa.sin_addr), ntohs(sa.sin_port));
73327910Sasami		(void)close(addrs->socket);
73427910Sasami		return (SNMP_ERR_GENERR);
73527910Sasami	}
73627910Sasami
73727910Sasami	addrs->status = RowStatus_active;
73827910Sasami
73927910Sasami	return (SNMP_ERR_NOERROR);
74027910Sasami}
74135621Sbde
74236589Sjhayint
74327910Sasamitarget_delete_address(struct target_address *addrs)
74434509Sbde{
74527910Sasami	SLIST_REMOVE(&target_addresslist, addrs, target_address, ta);
74627910Sasami	if (addrs->status == RowStatus_active)
74727910Sasami		close(addrs->socket);
74827910Sasami	free(addrs);
74927910Sasami
75027910Sasami	return (0);
75127910Sasami}
75227910Sasami
75327910Sasamistruct target_param *
75427910Sasamitarget_first_param(void)
75536454Sjkh{
75627910Sasami	return (SLIST_FIRST(&target_paramlist));
75727910Sasami}
75827910Sasami
75927910Sasamistruct target_param *
76027910Sasamitarget_next_param(struct target_param *param)
76127910Sasami{
76227910Sasami	if (param == NULL)
76327910Sasami		return (NULL);
76419175Sbde
76527910Sasami	return (SLIST_NEXT(param, tp));
76627910Sasami}
76727910Sasami
76827910Sasamistruct target_param *
76927910Sasamitarget_new_param(char *pname)
77027910Sasami{
77127910Sasami	int cmp;
77227910Sasami	struct target_param *param, *temp, *prev;
77334688Sbde
77427910Sasami	SLIST_FOREACH(param, &target_paramlist, tp)
77527910Sasami		if (strcmp(pname, param->name) == 0)
77627910Sasami			return (NULL);
77736455Sjkh
77836455Sjkh	if ((param = (struct target_param *)malloc(sizeof(*param))) == NULL)
77936074Sbde		return (NULL);
78036074Sbde
78130113Sjkh	memset(param, 0, sizeof(*param));
78219175Sbde	strlcpy(param->name, pname, sizeof(param->name));
7832061Sjkh
78435479Sbde	if ((prev = SLIST_FIRST(&target_paramlist)) == NULL ||
78530113Sjkh	    strcmp(pname, prev->name) < 0) {
78630113Sjkh		SLIST_INSERT_HEAD(&target_paramlist, param, tp);
78735294Sdt		return (param);
78830113Sjkh	}
78930113Sjkh
79030113Sjkh	SLIST_FOREACH(temp, &target_paramlist, tp) {
79130113Sjkh		if ((cmp = strcmp(pname, temp->name)) <= 0)
79230113Sjkh			break;
79330113Sjkh		prev = temp;
79430113Sjkh	}
79530113Sjkh
79630113Sjkh	if (temp == NULL || cmp < 0)
79730113Sjkh		SLIST_INSERT_AFTER(prev, param, tp);
79830113Sjkh	else if (cmp > 0)
79930113Sjkh		SLIST_INSERT_AFTER(temp, param, tp);
80030113Sjkh	else {
80132427Sjb		syslog(LOG_ERR, "Target parameter %s exists", param->name);
80232427Sjb		free(param);
8031594Srgrimes		return (NULL);
804	}
805
806	return (param);
807}
808
809int
810target_delete_param(struct target_param *param)
811{
812	SLIST_REMOVE(&target_paramlist, param, target_param, tp);
813	free(param);
814
815	return (0);
816}
817
818struct target_notify *
819target_first_notify(void)
820{
821	return (SLIST_FIRST(&target_notifylist));
822}
823
824struct target_notify *
825target_next_notify(struct target_notify *notify)
826{
827	if (notify == NULL)
828		return (NULL);
829
830	return (SLIST_NEXT(notify, tn));
831}
832
833struct target_notify *
834target_new_notify(char *nname)
835{
836	int cmp;
837	struct target_notify *notify, *temp, *prev;
838
839	SLIST_FOREACH(notify, &target_notifylist, tn)
840		if (strcmp(nname, notify->name) == 0)
841			return (NULL);
842
843	if ((notify = (struct target_notify *)malloc(sizeof(*notify))) == NULL)
844		return (NULL);
845
846	memset(notify, 0, sizeof(*notify));
847	strlcpy(notify->name, nname, sizeof(notify->name));
848
849	if ((prev = SLIST_FIRST(&target_notifylist)) == NULL ||
850	    strcmp(nname, prev->name) < 0) {
851		SLIST_INSERT_HEAD(&target_notifylist, notify, tn);
852		return (notify);
853	}
854
855	SLIST_FOREACH(temp, &target_notifylist, tn) {
856		if ((cmp = strcmp(nname, temp->name)) <= 0)
857			break;
858		prev = temp;
859	}
860
861	if (temp == NULL || cmp < 0)
862		SLIST_INSERT_AFTER(prev, notify, tn);
863	else if (cmp > 0)
864		SLIST_INSERT_AFTER(temp, notify, tn);
865	else {
866		syslog(LOG_ERR, "Notification target %s exists", notify->name);
867		free(notify);
868		return (NULL);
869	}
870
871	return (notify);
872}
873
874int
875target_delete_notify(struct target_notify *notify)
876{
877	SLIST_REMOVE(&target_notifylist, notify, target_notify, tn);
878	free(notify);
879
880	return (0);
881}
882
883void
884target_flush_all(void)
885{
886	struct target_address *addrs;
887	struct target_param *param;
888	struct target_notify *notify;
889
890	while ((addrs = SLIST_FIRST(&target_addresslist)) != NULL) {
891		SLIST_REMOVE_HEAD(&target_addresslist, ta);
892		if (addrs->status == RowStatus_active)
893			close(addrs->socket);
894		free(addrs);
895	}
896	SLIST_INIT(&target_addresslist);
897
898	while ((param = SLIST_FIRST(&target_paramlist)) != NULL) {
899		SLIST_REMOVE_HEAD(&target_paramlist, tp);
900		free(param);
901	}
902	SLIST_INIT(&target_paramlist);
903
904	while ((notify = SLIST_FIRST(&target_notifylist)) != NULL) {
905		SLIST_REMOVE_HEAD(&target_notifylist, tn);
906		free(notify);
907	}
908	SLIST_INIT(&target_notifylist);
909}
910