xref: /freebsd/contrib/bsnmp/snmpd/trans_udp.c (revision e17f5b1d)
1 /*
2  * Copyright (c) 2003
3  *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4  *	All rights reserved.
5  *
6  * Author: Harti Brandt <harti@freebsd.org>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $Begemot: bsnmp/snmpd/trans_udp.c,v 1.5 2005/10/04 08:46:56 brandt_h Exp $
30  *
31  * UDP transport
32  */
33 #include <sys/types.h>
34 #include <sys/queue.h>
35 #include <sys/ucred.h>
36 
37 #include <stdbool.h>
38 #include <stdlib.h>
39 #include <syslog.h>
40 #include <string.h>
41 #include <errno.h>
42 #include <unistd.h>
43 
44 #include <netinet/in.h>
45 #include <arpa/inet.h>
46 
47 #include "snmpmod.h"
48 #include "snmpd.h"
49 #include "trans_udp.h"
50 #include "tree.h"
51 #include "oid.h"
52 
53 static int udp_start(void);
54 static int udp_stop(int);
55 static void udp_close_port(struct tport *);
56 static int udp_init_port(struct tport *);
57 static ssize_t udp_send(struct tport *, const u_char *, size_t,
58     const struct sockaddr *, size_t);
59 static ssize_t udp_recv(struct tport *, struct port_input *);
60 
61 /* exported */
62 const struct transport_def udp_trans = {
63 	"udp",
64 	OIDX_begemotSnmpdTransUdp,
65 	udp_start,
66 	udp_stop,
67 	udp_close_port,
68 	udp_init_port,
69 	udp_send,
70 	udp_recv,
71 	NULL
72 };
73 static struct transport *my_trans;
74 
75 static int
76 udp_start(void)
77 {
78 	return (trans_register(&udp_trans, &my_trans));
79 }
80 
81 static int
82 udp_stop(int force __unused)
83 {
84 	if (my_trans != NULL)
85 		if (trans_unregister(my_trans) != 0)
86 			return (SNMP_ERR_GENERR);
87 	return (SNMP_ERR_NOERROR);
88 }
89 
90 /*
91  * A UDP port is ready
92  */
93 static void
94 udp_input(int fd __unused, void *udata)
95 {
96 	struct udp_port *p = udata;
97 
98 	p->input.peerlen = sizeof(p->ret);
99 	snmpd_input(&p->input, &p->tport);
100 }
101 
102 /*
103  * Create a UDP socket and bind it to the given port
104  */
105 static int
106 udp_init_port(struct tport *tp)
107 {
108 	struct udp_port *p = (struct udp_port *)tp;
109 	struct sockaddr_in addr;
110 	u_int32_t ip;
111 	const int on = 1;
112 
113 	if ((p->input.fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
114 		syslog(LOG_ERR, "creating UDP socket: %m");
115 		return (SNMP_ERR_RES_UNAVAIL);
116 	}
117 	ip = (p->addr[0] << 24) | (p->addr[1] << 16) | (p->addr[2] << 8) |
118 	    p->addr[3];
119 	memset(&addr, 0, sizeof(addr));
120 	addr.sin_addr.s_addr = htonl(ip);
121 	addr.sin_port = htons(p->port);
122 	addr.sin_family = AF_INET;
123 	addr.sin_len = sizeof(addr);
124 	if (addr.sin_addr.s_addr == INADDR_ANY) {
125 		if (setsockopt(p->input.fd, IPPROTO_IP, IP_RECVDSTADDR, &on,
126 		    sizeof(on)) == -1) {
127 			syslog(LOG_ERR, "setsockopt(IP_RECVDSTADDR): %m");
128 			close(p->input.fd);
129 			p->input.fd = -1;
130 			return (SNMP_ERR_GENERR);
131 		}
132 		p->recvdstaddr = true;
133 	}
134 	if (bind(p->input.fd, (struct sockaddr *)&addr, sizeof(addr))) {
135 		if (errno == EADDRNOTAVAIL) {
136 			close(p->input.fd);
137 			p->input.fd = -1;
138 			return (SNMP_ERR_INCONS_NAME);
139 		}
140 		syslog(LOG_ERR, "bind: %s:%u %m", inet_ntoa(addr.sin_addr),
141 		    p->port);
142 		close(p->input.fd);
143 		p->input.fd = -1;
144 		return (SNMP_ERR_GENERR);
145 	}
146 	if ((p->input.id = fd_select(p->input.fd, udp_input,
147 	    p, NULL)) == NULL) {
148 		close(p->input.fd);
149 		p->input.fd = -1;
150 		return (SNMP_ERR_GENERR);
151 	}
152 	return (SNMP_ERR_NOERROR);
153 }
154 
155 /*
156  * Create a new SNMP Port object and start it, if we are not
157  * in initialization mode. The arguments are in host byte order.
158  */
159 static int
160 udp_open_port(u_int8_t *addr, u_int32_t udp_port, struct udp_port **pp)
161 {
162 	struct udp_port *port;
163 	int err;
164 
165 	if (udp_port > 0xffff)
166 		return (SNMP_ERR_NO_CREATION);
167 	if ((port = malloc(sizeof(*port))) == NULL)
168 		return (SNMP_ERR_GENERR);
169 	memset(port, 0, sizeof(*port));
170 
171 	/* initialize common part */
172 	port->tport.index.len = 5;
173 	port->tport.index.subs[0] = addr[0];
174 	port->tport.index.subs[1] = addr[1];
175 	port->tport.index.subs[2] = addr[2];
176 	port->tport.index.subs[3] = addr[3];
177 	port->tport.index.subs[4] = udp_port;
178 
179 	port->addr[0] = addr[0];
180 	port->addr[1] = addr[1];
181 	port->addr[2] = addr[2];
182 	port->addr[3] = addr[3];
183 	port->port = udp_port;
184 
185 	port->input.fd = -1;
186 	port->input.id = NULL;
187 	port->input.stream = 0;
188 	port->input.cred = 0;
189 	port->input.peer = (struct sockaddr *)&port->ret;
190 	port->input.peerlen = sizeof(port->ret);
191 
192 	trans_insert_port(my_trans, &port->tport);
193 
194 	if (community != COMM_INITIALIZE &&
195 	    (err = udp_init_port(&port->tport)) != SNMP_ERR_NOERROR) {
196 		udp_close_port(&port->tport);
197 		return (err);
198 	}
199 	*pp = port;
200 	return (SNMP_ERR_NOERROR);
201 }
202 
203 /*
204  * Close an SNMP port
205  */
206 static void
207 udp_close_port(struct tport *tp)
208 {
209 	struct udp_port *port = (struct udp_port *)tp;
210 
211 	snmpd_input_close(&port->input);
212 	trans_remove_port(tp);
213 	free(port);
214 }
215 
216 /*
217  * Send something
218  */
219 static ssize_t
220 udp_send(struct tport *tp, const u_char *buf, size_t len,
221     const struct sockaddr *addr, size_t addrlen)
222 {
223 	struct udp_port *p = (struct udp_port *)tp;
224 	struct cmsghdr *cmsg;
225 	struct msghdr msg;
226 	char cbuf[CMSG_SPACE(sizeof(struct in_addr))];
227 	struct iovec iov;
228 
229 	iov.iov_base = __DECONST(void*, buf);
230 	iov.iov_len = len;
231 
232 	msg.msg_flags = 0;
233 	msg.msg_iov = &iov;
234 	msg.msg_iovlen = 1;
235 	msg.msg_name = __DECONST(void *, addr);
236 	msg.msg_namelen = addrlen;
237 
238 	if (p->recvdstaddr) {
239 		msg.msg_control = cbuf;
240 		msg.msg_controllen = sizeof(cbuf);
241 
242 		cmsg = CMSG_FIRSTHDR(&msg);
243 		cmsg->cmsg_level = IPPROTO_IP;
244 		cmsg->cmsg_type = IP_SENDSRCADDR;
245 		cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
246 		memcpy(CMSG_DATA(cmsg), &p->dstaddr, sizeof(struct in_addr));
247 	} else {
248 		msg.msg_control = NULL;
249 		msg.msg_controllen = 0;
250 	}
251 
252 	return (sendmsg(p->input.fd, &msg, 0));
253 }
254 
255 static void
256 check_priv_dgram(struct port_input *pi, struct sockcred *cred)
257 {
258 
259 	/* process explicitly sends credentials */
260 	if (cred)
261 		pi->priv = (cred->sc_euid == 0);
262 	else
263 		pi->priv = 0;
264 }
265 
266 /*
267  * Input from a datagram socket.
268  * Each receive should return one datagram.
269  */
270 static ssize_t
271 udp_recv(struct tport *tp, struct port_input *pi)
272 {
273 	u_char embuf[1000];
274 	char cbuf[CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) +
275 	    CMSG_SPACE(sizeof(struct in_addr))];
276 	struct udp_port *p = (struct udp_port *)tp;
277 	struct msghdr msg;
278 	struct iovec iov[1];
279 	ssize_t len;
280 	struct cmsghdr *cmsg;
281 	struct sockcred *cred = NULL;
282 
283 	if (pi->buf == NULL) {
284 		/* no buffer yet - allocate one */
285 		if ((pi->buf = buf_alloc(0)) == NULL) {
286 			/* ups - could not get buffer. Read away input
287 			 * and drop it */
288 			(void)recvfrom(pi->fd, embuf, sizeof(embuf),
289 			    0, NULL, NULL);
290 			/* return error */
291 			return (-1);
292 		}
293 		pi->buflen = buf_size(0);
294 	}
295 
296 	/* try to get a message */
297 	msg.msg_name = pi->peer;
298 	msg.msg_namelen = pi->peerlen;
299 	msg.msg_iov = iov;
300 	msg.msg_iovlen = 1;
301 	memset(cbuf, 0, sizeof(cbuf));
302 	msg.msg_control = cbuf;
303 	msg.msg_controllen = sizeof(cbuf);
304 	msg.msg_flags = 0;
305 
306 	iov[0].iov_base = pi->buf;
307 	iov[0].iov_len = pi->buflen;
308 
309 	len = recvmsg(pi->fd, &msg, 0);
310 
311 	if (len == -1 || len == 0)
312 		/* receive error */
313 		return (-1);
314 
315 	if (msg.msg_flags & MSG_TRUNC) {
316 		/* truncated - drop */
317 		snmpd_stats.silentDrops++;
318 		snmpd_stats.inTooLong++;
319 		return (-1);
320 	}
321 
322 	pi->length = (size_t)len;
323 
324 	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
325 	    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
326 		if (cmsg->cmsg_level == IPPROTO_IP &&
327 		    cmsg->cmsg_type == IP_RECVDSTADDR)
328 			memcpy(&p->dstaddr, CMSG_DATA(cmsg),
329 			    sizeof(struct in_addr));
330 		if (cmsg->cmsg_level == SOL_SOCKET &&
331 		    cmsg->cmsg_type == SCM_CREDS)
332 			cred = (struct sockcred *)(void *)CMSG_DATA(cmsg);
333 	}
334 
335 	if (pi->cred)
336 		check_priv_dgram(pi, cred);
337 
338 	return (0);
339 }
340 
341 /*
342  * Port table
343  */
344 int
345 op_snmp_port(struct snmp_context *ctx, struct snmp_value *value,
346     u_int sub, u_int iidx, enum snmp_op op)
347 {
348 	asn_subid_t which = value->var.subs[sub-1];
349 	struct udp_port *p;
350 	u_int8_t addr[4];
351 	u_int32_t port;
352 
353 	switch (op) {
354 
355 	  case SNMP_OP_GETNEXT:
356 		if ((p = (struct udp_port *)trans_next_port(my_trans,
357 		    &value->var, sub)) == NULL)
358 			return (SNMP_ERR_NOSUCHNAME);
359 		index_append(&value->var, sub, &p->tport.index);
360 		break;
361 
362 	  case SNMP_OP_GET:
363 		if ((p = (struct udp_port *)trans_find_port(my_trans,
364 		    &value->var, sub)) == NULL)
365 			return (SNMP_ERR_NOSUCHNAME);
366 		break;
367 
368 	  case SNMP_OP_SET:
369 		p = (struct udp_port *)trans_find_port(my_trans,
370 		    &value->var, sub);
371 		ctx->scratch->int1 = (p != NULL);
372 
373 		if (which != LEAF_begemotSnmpdPortStatus)
374 			abort();
375 		if (!TRUTH_OK(value->v.integer))
376 			return (SNMP_ERR_WRONG_VALUE);
377 
378 		ctx->scratch->int2 = TRUTH_GET(value->v.integer);
379 
380 		if (ctx->scratch->int2) {
381 			/* open an SNMP port */
382 			if (p != NULL)
383 				/* already open - do nothing */
384 				return (SNMP_ERR_NOERROR);
385 
386 			if (index_decode(&value->var, sub, iidx, addr, &port))
387 				return (SNMP_ERR_NO_CREATION);
388 			return (udp_open_port(addr, port, &p));
389 
390 		} else {
391 			/* close SNMP port - do in commit */
392 		}
393 		return (SNMP_ERR_NOERROR);
394 
395 	  case SNMP_OP_ROLLBACK:
396 		p = (struct udp_port *)trans_find_port(my_trans,
397 		    &value->var, sub);
398 		if (ctx->scratch->int1 == 0) {
399 			/* did not exist */
400 			if (ctx->scratch->int2 == 1) {
401 				/* created */
402 				if (p != NULL)
403 					udp_close_port(&p->tport);
404 			}
405 		}
406 		return (SNMP_ERR_NOERROR);
407 
408 	  case SNMP_OP_COMMIT:
409 		p = (struct udp_port *)trans_find_port(my_trans,
410 		    &value->var, sub);
411 		if (ctx->scratch->int1 == 1) {
412 			/* did exist */
413 			if (ctx->scratch->int2 == 0) {
414 				/* delete */
415 				if (p != NULL)
416 					udp_close_port(&p->tport);
417 			}
418 		}
419 		return (SNMP_ERR_NOERROR);
420 
421 	  default:
422 		abort();
423 	}
424 
425 	/*
426 	 * Come here to fetch the value
427 	 */
428 	switch (which) {
429 
430 	  case LEAF_begemotSnmpdPortStatus:
431 		value->v.integer = 1;
432 		break;
433 
434 	  default:
435 		abort();
436 	}
437 
438 	return (SNMP_ERR_NOERROR);
439 }
440