trans_udp.c revision 133211
1184610Salfred/*
2184610Salfred * Copyright (c) 2003
3184610Salfred *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4184610Salfred *	All rights reserved.
5184610Salfred *
6184610Salfred * Author: Harti Brandt <harti@freebsd.org>
7184610Salfred *
8184610Salfred * Redistribution and use in source and binary forms, with or without
9184610Salfred * modification, are permitted provided that the following conditions
10184610Salfred * are met:
11184610Salfred * 1. Redistributions of source code must retain the above copyright
12184610Salfred *    notice, this list of conditions and the following disclaimer.
13184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
14184610Salfred *    notice, this list of conditions and the following disclaimer in the
15184610Salfred *    documentation and/or other materials provided with the distribution.
16184610Salfred *
17184610Salfred * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20184610Salfred * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22184610Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24184610Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25184610Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26188417Sthompsa * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27188417Sthompsa * SUCH DAMAGE.
28188417Sthompsa *
29188417Sthompsa * $Begemot: bsnmp/snmpd/trans_udp.c,v 1.4 2004/08/06 08:47:16 brandt Exp $
30188417Sthompsa *
31188417Sthompsa * UDP transport
32188417Sthompsa */
33188417Sthompsa#include <sys/types.h>
34188417Sthompsa
35188417Sthompsa#include <stdlib.h>
36188417Sthompsa#include <syslog.h>
37188417Sthompsa#include <string.h>
38188417Sthompsa#include <errno.h>
39188417Sthompsa#include <unistd.h>
40188417Sthompsa
41188417Sthompsa#include <netinet/in.h>
42188417Sthompsa#include <arpa/inet.h>
43188417Sthompsa
44188417Sthompsa#include "snmpmod.h"
45188417Sthompsa#include "snmpd.h"
46188417Sthompsa#include "trans_udp.h"
47188417Sthompsa#include "tree.h"
48188417Sthompsa#include "oid.h"
49188417Sthompsa
50188417Sthompsastatic int udp_start(void);
51184610Salfredstatic int udp_stop(int);
52184610Salfredstatic void udp_close_port(struct tport *);
53184610Salfredstatic int udp_init_port(struct tport *);
54184610Salfredstatic ssize_t udp_send(struct tport *, const u_char *, size_t,
55188417Sthompsa    const struct sockaddr *, size_t);
56188417Sthompsa
57188417Sthompsa/* exported */
58188417Sthompsaconst struct transport_def udp_trans = {
59188417Sthompsa	"udp",
60188417Sthompsa	OIDX_begemotSnmpdTransUdp,
61188417Sthompsa	udp_start,
62188417Sthompsa	udp_stop,
63188417Sthompsa	udp_close_port,
64188417Sthompsa	udp_init_port,
65188417Sthompsa	udp_send
66188417Sthompsa};
67188417Sthompsastatic struct transport *my_trans;
68188417Sthompsa
69188417Sthompsastatic int
70188417Sthompsaudp_start(void)
71188417Sthompsa{
72188417Sthompsa	return (trans_register(&udp_trans, &my_trans));
73188417Sthompsa}
74188417Sthompsa
75188417Sthompsastatic int
76188417Sthompsaudp_stop(int force __unused)
77188417Sthompsa{
78188417Sthompsa	if (my_trans != NULL)
79188417Sthompsa		if (trans_unregister(my_trans) != 0)
80188417Sthompsa			return (SNMP_ERR_GENERR);
81188417Sthompsa	return (SNMP_ERR_NOERROR);
82188417Sthompsa}
83188417Sthompsa
84188417Sthompsa/*
85188417Sthompsa * A UDP port is ready
86188417Sthompsa */
87188417Sthompsastatic void
88188417Sthompsaudp_input(int fd __unused, void *udata)
89188417Sthompsa{
90188417Sthompsa	struct udp_port *p = udata;
91188417Sthompsa
92188417Sthompsa	p->input.peerlen = sizeof(p->ret);
93188417Sthompsa	snmpd_input(&p->input, &p->tport);
94188417Sthompsa}
95188417Sthompsa
96188417Sthompsa/*
97188417Sthompsa * Create a UDP socket and bind it to the given port
98188417Sthompsa */
99188417Sthompsastatic int
100188417Sthompsaudp_init_port(struct tport *tp)
101188417Sthompsa{
102188417Sthompsa	struct udp_port *p = (struct udp_port *)tp;
103188417Sthompsa	struct sockaddr_in addr;
104188417Sthompsa	u_int32_t ip;
105188417Sthompsa
106188417Sthompsa	if ((p->input.fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
107188417Sthompsa		syslog(LOG_ERR, "creating UDP socket: %m");
108188417Sthompsa		return (SNMP_ERR_RES_UNAVAIL);
109188417Sthompsa	}
110188417Sthompsa	ip = (p->addr[0] << 24) | (p->addr[1] << 16) | (p->addr[2] << 8) |
111188417Sthompsa	    p->addr[3];
112188417Sthompsa	memset(&addr, 0, sizeof(addr));
113184610Salfred	addr.sin_addr.s_addr = htonl(ip);
114184610Salfred	addr.sin_port = htons(p->port);
115249584Sgabor	addr.sin_family = AF_INET;
116184610Salfred	addr.sin_len = sizeof(addr);
117188417Sthompsa	if (bind(p->input.fd, (struct sockaddr *)&addr, sizeof(addr))) {
118188417Sthompsa		if (errno == EADDRNOTAVAIL) {
119184610Salfred			close(p->input.fd);
120184610Salfred			p->input.fd = -1;
121184610Salfred			return (SNMP_ERR_INCONS_NAME);
122184610Salfred		}
123188417Sthompsa		syslog(LOG_ERR, "bind: %s:%u %m", inet_ntoa(addr.sin_addr),
124188417Sthompsa		    p->port);
125188417Sthompsa		close(p->input.fd);
126188417Sthompsa		p->input.fd = -1;
127188417Sthompsa		return (SNMP_ERR_GENERR);
128188417Sthompsa	}
129188417Sthompsa	if ((p->input.id = fd_select(p->input.fd, udp_input,
130188417Sthompsa	    p, NULL)) == NULL) {
131188417Sthompsa		close(p->input.fd);
132188417Sthompsa		p->input.fd = -1;
133188417Sthompsa		return (SNMP_ERR_GENERR);
134188417Sthompsa	}
135188417Sthompsa	return (SNMP_ERR_NOERROR);
136188417Sthompsa}
137188417Sthompsa
138188417Sthompsa/*
139184610Salfred * Create a new SNMP Port object and start it, if we are not
140184610Salfred * in initialisation mode. The arguments are in host byte order.
141184610Salfred */
142184610Salfredstatic int
143188417Sthompsaudp_open_port(u_int8_t *addr, u_int32_t udp_port, struct udp_port **pp)
144188417Sthompsa{
145188417Sthompsa	struct udp_port *port;
146188417Sthompsa	int err;
147188417Sthompsa
148188417Sthompsa	if (udp_port > 0xffff)
149184610Salfred		return (SNMP_ERR_NO_CREATION);
150184610Salfred	if ((port = malloc(sizeof(*port))) == NULL)
151188417Sthompsa		return (SNMP_ERR_GENERR);
152188417Sthompsa	memset(port, 0, sizeof(*port));
153184610Salfred
154184610Salfred	/* initialize common part */
155184610Salfred	port->tport.index.len = 5;
156184610Salfred	port->tport.index.subs[0] = addr[0];
157188417Sthompsa	port->tport.index.subs[1] = addr[1];
158188417Sthompsa	port->tport.index.subs[2] = addr[2];
159188417Sthompsa	port->tport.index.subs[3] = addr[3];
160188417Sthompsa	port->tport.index.subs[4] = udp_port;
161188417Sthompsa
162188417Sthompsa	port->addr[0] = addr[0];
163188417Sthompsa	port->addr[1] = addr[1];
164188417Sthompsa	port->addr[2] = addr[2];
165188417Sthompsa	port->addr[3] = addr[3];
166188417Sthompsa	port->port = udp_port;
167188417Sthompsa
168188417Sthompsa	port->input.fd = -1;
169188417Sthompsa	port->input.id = NULL;
170188417Sthompsa	port->input.stream = 0;
171184610Salfred	port->input.cred = 0;
172184610Salfred	port->input.peer = (struct sockaddr *)&port->ret;
173184610Salfred	port->input.peerlen = sizeof(port->ret);
174184610Salfred
175188417Sthompsa	trans_insert_port(my_trans, &port->tport);
176188417Sthompsa
177188417Sthompsa	if (community != COMM_INITIALIZE &&
178188417Sthompsa	    (err = udp_init_port(&port->tport)) != SNMP_ERR_NOERROR) {
179188417Sthompsa		udp_close_port(&port->tport);
180188417Sthompsa		return (err);
181188417Sthompsa	}
182188417Sthompsa	*pp = port;
183188417Sthompsa	return (SNMP_ERR_NOERROR);
184188417Sthompsa}
185188417Sthompsa
186188417Sthompsa/*
187188417Sthompsa * Close an SNMP port
188188417Sthompsa */
189188417Sthompsastatic void
190188417Sthompsaudp_close_port(struct tport *tp)
191188417Sthompsa{
192188417Sthompsa	struct udp_port *port = (struct udp_port *)tp;
193188417Sthompsa
194188417Sthompsa	snmpd_input_close(&port->input);
195188417Sthompsa	trans_remove_port(tp);
196188417Sthompsa	free(port);
197188417Sthompsa}
198188417Sthompsa
199188417Sthompsa/*
200188417Sthompsa * Send something
201188417Sthompsa */
202188417Sthompsastatic ssize_t
203188417Sthompsaudp_send(struct tport *tp, const u_char *buf, size_t len,
204188417Sthompsa    const struct sockaddr *addr, size_t addrlen)
205188417Sthompsa{
206188417Sthompsa	struct udp_port *p = (struct udp_port *)tp;
207188417Sthompsa
208188417Sthompsa	return (sendto(p->input.fd, buf, len, 0, addr, addrlen));
209188417Sthompsa}
210188417Sthompsa
211188417Sthompsa/*
212188417Sthompsa * Port table
213188417Sthompsa */
214188417Sthompsaint
215188417Sthompsaop_snmp_port(struct snmp_context *ctx, struct snmp_value *value,
216188417Sthompsa    u_int sub, u_int iidx, enum snmp_op op)
217188417Sthompsa{
218188417Sthompsa	asn_subid_t which = value->var.subs[sub-1];
219188417Sthompsa	struct udp_port *p;
220188417Sthompsa	u_int8_t addr[4];
221188417Sthompsa	u_int32_t port;
222188417Sthompsa
223188417Sthompsa	switch (op) {
224188417Sthompsa
225188417Sthompsa	  case SNMP_OP_GETNEXT:
226188417Sthompsa		if ((p = (struct udp_port *)trans_next_port(my_trans,
227188417Sthompsa		    &value->var, sub)) == NULL)
228188417Sthompsa			return (SNMP_ERR_NOSUCHNAME);
229188417Sthompsa		index_append(&value->var, sub, &p->tport.index);
230188417Sthompsa		break;
231188417Sthompsa
232188417Sthompsa	  case SNMP_OP_GET:
233188417Sthompsa		if ((p = (struct udp_port *)trans_find_port(my_trans,
234188417Sthompsa		    &value->var, sub)) == NULL)
235188417Sthompsa			return (SNMP_ERR_NOSUCHNAME);
236188417Sthompsa		break;
237188417Sthompsa
238188417Sthompsa	  case SNMP_OP_SET:
239188417Sthompsa		p = (struct udp_port *)trans_find_port(my_trans,
240188417Sthompsa		    &value->var, sub);
241188417Sthompsa		ctx->scratch->int1 = (p != NULL);
242188417Sthompsa
243188417Sthompsa		if (which != LEAF_begemotSnmpdPortStatus)
244188417Sthompsa			abort();
245188417Sthompsa		if (!TRUTH_OK(value->v.integer))
246188417Sthompsa			return (SNMP_ERR_WRONG_VALUE);
247188417Sthompsa
248188417Sthompsa		ctx->scratch->int2 = TRUTH_GET(value->v.integer);
249188417Sthompsa
250188417Sthompsa		if (ctx->scratch->int2) {
251188417Sthompsa			/* open an SNMP port */
252188417Sthompsa			if (p != NULL)
253188417Sthompsa				/* already open - do nothing */
254188417Sthompsa				return (SNMP_ERR_NOERROR);
255188417Sthompsa
256188417Sthompsa			if (index_decode(&value->var, sub, iidx, addr, &port))
257188417Sthompsa				return (SNMP_ERR_NO_CREATION);
258188417Sthompsa			return (udp_open_port(addr, port, &p));
259188417Sthompsa
260188417Sthompsa		} else {
261188417Sthompsa			/* close SNMP port - do in commit */
262188417Sthompsa		}
263188417Sthompsa		return (SNMP_ERR_NOERROR);
264188417Sthompsa
265188417Sthompsa	  case SNMP_OP_ROLLBACK:
266188417Sthompsa		p = (struct udp_port *)trans_find_port(my_trans,
267188417Sthompsa		    &value->var, sub);
268188417Sthompsa		if (ctx->scratch->int1 == 0) {
269188417Sthompsa			/* did not exist */
270188417Sthompsa			if (ctx->scratch->int2 == 1) {
271188417Sthompsa				/* created */
272188417Sthompsa				if (p != NULL)
273188417Sthompsa					udp_close_port(&p->tport);
274188417Sthompsa			}
275188417Sthompsa		}
276188417Sthompsa		return (SNMP_ERR_NOERROR);
277188417Sthompsa
278188417Sthompsa	  case SNMP_OP_COMMIT:
279188417Sthompsa		p = (struct udp_port *)trans_find_port(my_trans,
280188417Sthompsa		    &value->var, sub);
281188417Sthompsa		if (ctx->scratch->int1 == 1) {
282188417Sthompsa			/* did exist */
283188417Sthompsa			if (ctx->scratch->int2 == 0) {
284188417Sthompsa				/* delete */
285188417Sthompsa				if (p != NULL)
286188417Sthompsa					udp_close_port(&p->tport);
287188417Sthompsa			}
288188417Sthompsa		}
289188417Sthompsa		return (SNMP_ERR_NOERROR);
290188417Sthompsa
291188417Sthompsa	  default:
292188417Sthompsa		abort();
293188417Sthompsa	}
294188417Sthompsa
295188417Sthompsa	/*
296188417Sthompsa	 * Come here to fetch the value
297188417Sthompsa	 */
298188417Sthompsa	switch (which) {
299188417Sthompsa
300188417Sthompsa	  case LEAF_begemotSnmpdPortStatus:
301188417Sthompsa		value->v.integer = 1;
302188417Sthompsa		break;
303188417Sthompsa
304188417Sthompsa	  default:
305188417Sthompsa		abort();
306188417Sthompsa	}
307188417Sthompsa
308188417Sthompsa	return (SNMP_ERR_NOERROR);
309188417Sthompsa}
310188417Sthompsa