xref: /freebsd/contrib/bsnmp/snmpd/trans_inet.c (revision ee2e9f4dbc195bf07bfc8e8ab2faed357d617e81)
104d17814SAndrey V. Elsukov /*
204d17814SAndrey V. Elsukov  * Copyright (c) 2018
304d17814SAndrey V. Elsukov  *	Hartmut Brandt.
404d17814SAndrey V. Elsukov  *	All rights reserved.
504d17814SAndrey V. Elsukov  *
604d17814SAndrey V. Elsukov  * Author: Harti Brandt <harti@freebsd.org>
704d17814SAndrey V. Elsukov  *
804d17814SAndrey V. Elsukov  * Redistribution and use in source and binary forms, with or without
904d17814SAndrey V. Elsukov  * modification, are permitted provided that the following conditions
1004d17814SAndrey V. Elsukov  * are met:
1104d17814SAndrey V. Elsukov  * 1. Redistributions of source code must retain the above copyright
1204d17814SAndrey V. Elsukov  *    notice, this list of conditions and the following disclaimer.
1304d17814SAndrey V. Elsukov  * 2. Redistributions in binary form must reproduce the above copyright
1404d17814SAndrey V. Elsukov  *    notice, this list of conditions and the following disclaimer in the
1504d17814SAndrey V. Elsukov  *    documentation and/or other materials provided with the distribution.
1604d17814SAndrey V. Elsukov  *
1704d17814SAndrey V. Elsukov  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1804d17814SAndrey V. Elsukov  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1904d17814SAndrey V. Elsukov  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2004d17814SAndrey V. Elsukov  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
2104d17814SAndrey V. Elsukov  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2204d17814SAndrey V. Elsukov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2304d17814SAndrey V. Elsukov  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2404d17814SAndrey V. Elsukov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2504d17814SAndrey V. Elsukov  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2604d17814SAndrey V. Elsukov  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2704d17814SAndrey V. Elsukov  * SUCH DAMAGE.
2804d17814SAndrey V. Elsukov  *
2904d17814SAndrey V. Elsukov  * $Begemot: bsnmp/snmpd/trans_udp.c,v 1.5 2005/10/04 08:46:56 brandt_h Exp $
3004d17814SAndrey V. Elsukov  *
3104d17814SAndrey V. Elsukov  * Internet transport
3204d17814SAndrey V. Elsukov  */
3304d17814SAndrey V. Elsukov 
3404d17814SAndrey V. Elsukov #include <sys/param.h>
3504d17814SAndrey V. Elsukov #include <sys/socket.h>
3604d17814SAndrey V. Elsukov #include <sys/types.h>
3704d17814SAndrey V. Elsukov 
3804d17814SAndrey V. Elsukov #include <assert.h>
3904d17814SAndrey V. Elsukov #include <errno.h>
4004d17814SAndrey V. Elsukov #include <netdb.h>
4104d17814SAndrey V. Elsukov #include <stdbool.h>
4204d17814SAndrey V. Elsukov #include <stddef.h>
4304d17814SAndrey V. Elsukov #include <stdlib.h>
4404d17814SAndrey V. Elsukov #include <string.h>
4504d17814SAndrey V. Elsukov #include <syslog.h>
4604d17814SAndrey V. Elsukov #include <unistd.h>
4704d17814SAndrey V. Elsukov 
4804d17814SAndrey V. Elsukov #include <stdio.h>
4904d17814SAndrey V. Elsukov 
5004d17814SAndrey V. Elsukov #include <arpa/inet.h>
5104d17814SAndrey V. Elsukov 
5204d17814SAndrey V. Elsukov #include "asn1.h"
5304d17814SAndrey V. Elsukov #include "snmp.h"
5404d17814SAndrey V. Elsukov #include "snmpmod.h"
5504d17814SAndrey V. Elsukov 
5604d17814SAndrey V. Elsukov #include "snmpd.h"
5704d17814SAndrey V. Elsukov 
5804d17814SAndrey V. Elsukov #define	SNMPTREE_TYPES
5904d17814SAndrey V. Elsukov #define	SNMPENUM_FUNCS
6004d17814SAndrey V. Elsukov #include "tree.h"
6104d17814SAndrey V. Elsukov #include "oid.h"
6204d17814SAndrey V. Elsukov 
6304d17814SAndrey V. Elsukov extern const struct transport_def inet_trans;
6404d17814SAndrey V. Elsukov 
6504d17814SAndrey V. Elsukov struct inet_port;
6604d17814SAndrey V. Elsukov struct inet_port_params;
6704d17814SAndrey V. Elsukov struct port_sock;
6804d17814SAndrey V. Elsukov 
6904d17814SAndrey V. Elsukov typedef int create_func(struct inet_port *, struct inet_port_params *);
7004d17814SAndrey V. Elsukov typedef void input_func(int, void *);
7104d17814SAndrey V. Elsukov typedef int activate_func(struct inet_port *);
7204d17814SAndrey V. Elsukov typedef void deactivate_func(struct inet_port *);
7304d17814SAndrey V. Elsukov typedef void parse_ctrl_func(struct port_sock *, const struct msghdr *);
74*ee2e9f4dSGleb Smirnoff typedef void setsrc_func(struct port_sock *, struct msghdr *, char *);
7504d17814SAndrey V. Elsukov 
7604d17814SAndrey V. Elsukov static create_func ipv4_create;
7704d17814SAndrey V. Elsukov static input_func ipv4_input;
7804d17814SAndrey V. Elsukov static activate_func ipv4_activate;
7904d17814SAndrey V. Elsukov static deactivate_func ipv4_deactivate;
8004d17814SAndrey V. Elsukov static parse_ctrl_func ipv4_parse_ctrl;
8104d17814SAndrey V. Elsukov static setsrc_func ipv4_setsrc;
8204d17814SAndrey V. Elsukov 
8304d17814SAndrey V. Elsukov static create_func ipv6_create;
8404d17814SAndrey V. Elsukov static input_func ipv6_input;
8504d17814SAndrey V. Elsukov static activate_func ipv6_activate;
8604d17814SAndrey V. Elsukov static deactivate_func ipv6_deactivate;
8704d17814SAndrey V. Elsukov static parse_ctrl_func ipv6_parse_ctrl;
8804d17814SAndrey V. Elsukov static setsrc_func ipv6_setsrc;
8904d17814SAndrey V. Elsukov 
9004d17814SAndrey V. Elsukov static create_func ipv6z_create;
9104d17814SAndrey V. Elsukov 
9204d17814SAndrey V. Elsukov static create_func dns_create;
9304d17814SAndrey V. Elsukov static activate_func dns_activate;
9404d17814SAndrey V. Elsukov static deactivate_func dns_deactivate;
9504d17814SAndrey V. Elsukov 
9604d17814SAndrey V. Elsukov struct port_sock {
9704d17814SAndrey V. Elsukov 	/* common input stuff; must be first */
9804d17814SAndrey V. Elsukov 	struct port_input input;
9904d17814SAndrey V. Elsukov 
10004d17814SAndrey V. Elsukov 	/** link field */
10104d17814SAndrey V. Elsukov 	TAILQ_ENTRY(port_sock) link;
10204d17814SAndrey V. Elsukov 
10304d17814SAndrey V. Elsukov 	/** pointer to parent */
10404d17814SAndrey V. Elsukov 	struct inet_port *port;
10504d17814SAndrey V. Elsukov 
10604d17814SAndrey V. Elsukov 	/** bind address */
10704d17814SAndrey V. Elsukov 	struct sockaddr_storage bind_addr;
10804d17814SAndrey V. Elsukov 
10904d17814SAndrey V. Elsukov 	/** reply destination */
11004d17814SAndrey V. Elsukov 	struct sockaddr_storage ret_dest;
11104d17814SAndrey V. Elsukov 
11204d17814SAndrey V. Elsukov 	/** need to set source address in reply; set for INADDR_ANY */
11304d17814SAndrey V. Elsukov 	bool set_ret_source;
11404d17814SAndrey V. Elsukov 
11504d17814SAndrey V. Elsukov 	/** address of the receive interface */
11604d17814SAndrey V. Elsukov 	union {
11704d17814SAndrey V. Elsukov 		/** IPv4 case */
11804d17814SAndrey V. Elsukov 		struct in_addr	a4;
11904d17814SAndrey V. Elsukov 
12004d17814SAndrey V. Elsukov 		/** IPv6 case */
12104d17814SAndrey V. Elsukov 		struct in6_pktinfo a6;
12204d17814SAndrey V. Elsukov 	} ret_source;
12304d17814SAndrey V. Elsukov 
12404d17814SAndrey V. Elsukov 	/** parse control message */
12504d17814SAndrey V. Elsukov 	parse_ctrl_func *parse_ctrl;
12604d17814SAndrey V. Elsukov 
12704d17814SAndrey V. Elsukov 	/** set source address for a send() */
12804d17814SAndrey V. Elsukov 	setsrc_func *setsrc;
12904d17814SAndrey V. Elsukov };
13004d17814SAndrey V. Elsukov static_assert(offsetof(struct port_sock, input) == 0,
13104d17814SAndrey V. Elsukov     "input not first in port_sock");
13204d17814SAndrey V. Elsukov 
13304d17814SAndrey V. Elsukov /**
13404d17814SAndrey V. Elsukov  * Table row for the inet ports.
13504d17814SAndrey V. Elsukov  *
13604d17814SAndrey V. Elsukov  * When actived each row can have one or several open sockets. For ipv6,
13704d17814SAndrey V. Elsukov  * ipv4 and ipv6z addresses it is always one, for dns addresses more than
13804d17814SAndrey V. Elsukov  * one socket can be open.
13904d17814SAndrey V. Elsukov  */
14004d17814SAndrey V. Elsukov struct inet_port {
14104d17814SAndrey V. Elsukov 	/** common i/o port stuff (must be first) */
14204d17814SAndrey V. Elsukov 	struct tport tport;
14304d17814SAndrey V. Elsukov 
14404d17814SAndrey V. Elsukov 	/** transport protocol */
14504d17814SAndrey V. Elsukov 	enum BegemotSnmpdTransportProto proto;
14604d17814SAndrey V. Elsukov 
14704d17814SAndrey V. Elsukov 	/** row status */
14804d17814SAndrey V. Elsukov 	enum RowStatus row_status;
14904d17814SAndrey V. Elsukov 
15004d17814SAndrey V. Elsukov 	/** socket list */
15104d17814SAndrey V. Elsukov 	TAILQ_HEAD(, port_sock) socks;
15204d17814SAndrey V. Elsukov 
15304d17814SAndrey V. Elsukov 	/** value for InetAddressType::dns */
15404d17814SAndrey V. Elsukov 	char *dns_addr;
15504d17814SAndrey V. Elsukov 
15604d17814SAndrey V. Elsukov 	/** port number in dns case; network byte order */
15704d17814SAndrey V. Elsukov 	uint16_t dns_port;
15804d17814SAndrey V. Elsukov 
15904d17814SAndrey V. Elsukov 	/** create a port */
16004d17814SAndrey V. Elsukov 	create_func *create;
16104d17814SAndrey V. Elsukov 
16204d17814SAndrey V. Elsukov 	/** activate a port */
16304d17814SAndrey V. Elsukov 	activate_func *activate;
16404d17814SAndrey V. Elsukov 
16504d17814SAndrey V. Elsukov 	/** deactivate port */
16604d17814SAndrey V. Elsukov 	deactivate_func *deactivate;
16704d17814SAndrey V. Elsukov };
16804d17814SAndrey V. Elsukov static_assert(offsetof(struct inet_port, tport) == 0,
16904d17814SAndrey V. Elsukov     "tport not first in inet_port");
17004d17814SAndrey V. Elsukov 
17104d17814SAndrey V. Elsukov /** to be used in bind_addr field */
17204d17814SAndrey V. Elsukov #define	AF_DNS	AF_VENDOR00
17304d17814SAndrey V. Elsukov 
17404d17814SAndrey V. Elsukov /** registered transport */
17504d17814SAndrey V. Elsukov static struct transport *my_trans;
17604d17814SAndrey V. Elsukov 
17704d17814SAndrey V. Elsukov /** set operation */
17804d17814SAndrey V. Elsukov enum {
17904d17814SAndrey V. Elsukov 	SET_CREATED,
18004d17814SAndrey V. Elsukov 	SET_ACTIVATED,
18104d17814SAndrey V. Elsukov 	SET_DEACTIVATE,
18204d17814SAndrey V. Elsukov 	SET_DESTROY,
18304d17814SAndrey V. Elsukov };
18404d17814SAndrey V. Elsukov 
18504d17814SAndrey V. Elsukov /** length of the control data buffer */
18604d17814SAndrey V. Elsukov static const size_t RECV_CBUF_SIZE =
18704d17814SAndrey V. Elsukov     MAX(CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) +
18804d17814SAndrey V. Elsukov 	CMSG_SPACE(sizeof(struct in_addr)),
18904d17814SAndrey V. Elsukov 	CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) +
19004d17814SAndrey V. Elsukov 	CMSG_SPACE(sizeof(struct in6_pktinfo)));
19104d17814SAndrey V. Elsukov 
19204d17814SAndrey V. Elsukov /** length of the control data buffer */
19304d17814SAndrey V. Elsukov static const size_t XMIT_CBUF_SIZE =
19404d17814SAndrey V. Elsukov     MAX(CMSG_SPACE(sizeof(struct in_addr)),
19504d17814SAndrey V. Elsukov 	CMSG_SPACE(sizeof(struct in6_pktinfo)));
19604d17814SAndrey V. Elsukov 
19704d17814SAndrey V. Elsukov /**
19804d17814SAndrey V. Elsukov  * Start the transport. This registers the transport with the
19904d17814SAndrey V. Elsukov  * transport table.
20004d17814SAndrey V. Elsukov  *
20104d17814SAndrey V. Elsukov  * \return SNMP error code
20204d17814SAndrey V. Elsukov  */
20304d17814SAndrey V. Elsukov static int
inet_start(void)20404d17814SAndrey V. Elsukov inet_start(void)
20504d17814SAndrey V. Elsukov {
20604d17814SAndrey V. Elsukov 	return (trans_register(&inet_trans, &my_trans));
20704d17814SAndrey V. Elsukov }
20804d17814SAndrey V. Elsukov 
20904d17814SAndrey V. Elsukov /**
21004d17814SAndrey V. Elsukov  * Stop the transport. This tries to unregister the transport which
21104d17814SAndrey V. Elsukov  * in turn fails if the list of ports is not empty.
21204d17814SAndrey V. Elsukov  *
21304d17814SAndrey V. Elsukov  * \return SNMP error code
21404d17814SAndrey V. Elsukov  */
21504d17814SAndrey V. Elsukov static int
inet_stop(int force __unused)21604d17814SAndrey V. Elsukov inet_stop(int force __unused)
21704d17814SAndrey V. Elsukov {
21804d17814SAndrey V. Elsukov 	if (my_trans != NULL)
21904d17814SAndrey V. Elsukov 		if (trans_unregister(my_trans) != 0)
22004d17814SAndrey V. Elsukov 			return (SNMP_ERR_GENERR);
22104d17814SAndrey V. Elsukov 	return (SNMP_ERR_NOERROR);
22204d17814SAndrey V. Elsukov }
22304d17814SAndrey V. Elsukov 
22404d17814SAndrey V. Elsukov /**
22504d17814SAndrey V. Elsukov  * Deactivate SNMP port.
22604d17814SAndrey V. Elsukov  *
22704d17814SAndrey V. Elsukov  * \param tp	port to close
22804d17814SAndrey V. Elsukov  */
22904d17814SAndrey V. Elsukov static void
deactivate_port(struct inet_port * p)23004d17814SAndrey V. Elsukov deactivate_port(struct inet_port *p)
23104d17814SAndrey V. Elsukov {
23204d17814SAndrey V. Elsukov 	p->deactivate(p);
23304d17814SAndrey V. Elsukov }
23404d17814SAndrey V. Elsukov 
23504d17814SAndrey V. Elsukov /*
23604d17814SAndrey V. Elsukov  * This function activates a port. For ports opened via the config files
23704d17814SAndrey V. Elsukov  * this is called just before entering the event loop. For ports create
23804d17814SAndrey V. Elsukov  * during runtime this is called when the RowStatus is set to Active or
23904d17814SAndrey V. Elsukov  * as second step for CreateAndGo.
24004d17814SAndrey V. Elsukov  *
24104d17814SAndrey V. Elsukov  * \param tp	transport port
24204d17814SAndrey V. Elsukov  *
24304d17814SAndrey V. Elsukov  * \return SNMP error code
24404d17814SAndrey V. Elsukov  */
24504d17814SAndrey V. Elsukov static int
inet_activate(struct tport * tp)24604d17814SAndrey V. Elsukov inet_activate(struct tport *tp)
24704d17814SAndrey V. Elsukov {
24804d17814SAndrey V. Elsukov 	struct inet_port *port = (struct inet_port *)tp;
24904d17814SAndrey V. Elsukov 
25004d17814SAndrey V. Elsukov 	return (port->activate(port));
25104d17814SAndrey V. Elsukov }
25204d17814SAndrey V. Elsukov 
25304d17814SAndrey V. Elsukov /*
25404d17814SAndrey V. Elsukov  * Close the SNMP port if it is open and destroy it.
25504d17814SAndrey V. Elsukov  *
25604d17814SAndrey V. Elsukov  * \param tp	port to close
25704d17814SAndrey V. Elsukov  */
25804d17814SAndrey V. Elsukov static void
inet_destroy_port(struct tport * tp)25904d17814SAndrey V. Elsukov inet_destroy_port(struct tport *tp)
26004d17814SAndrey V. Elsukov {
26104d17814SAndrey V. Elsukov 	struct inet_port *port = (struct inet_port *)tp;
26204d17814SAndrey V. Elsukov 
26304d17814SAndrey V. Elsukov 	deactivate_port(port);
26404d17814SAndrey V. Elsukov 
26504d17814SAndrey V. Elsukov 	trans_remove_port(tp);
26604d17814SAndrey V. Elsukov 
26704d17814SAndrey V. Elsukov 	free(port->dns_addr);
26804d17814SAndrey V. Elsukov 	free(port);
26904d17814SAndrey V. Elsukov }
27004d17814SAndrey V. Elsukov 
27104d17814SAndrey V. Elsukov /**
27204d17814SAndrey V. Elsukov  * If the input struct has no buffer allocated yet, do it now. If allocation
27304d17814SAndrey V. Elsukov  * fails, read the data into a local buffer and drop it.
27404d17814SAndrey V. Elsukov  *
27504d17814SAndrey V. Elsukov  * \param pi	input struct
27604d17814SAndrey V. Elsukov  *
27704d17814SAndrey V. Elsukov  * \return -1 if allocation fails, 0 otherwise
27804d17814SAndrey V. Elsukov  */
27904d17814SAndrey V. Elsukov static int
inet_alloc_buf(struct port_input * pi)28004d17814SAndrey V. Elsukov inet_alloc_buf(struct port_input *pi)
28104d17814SAndrey V. Elsukov {
28204d17814SAndrey V. Elsukov 	char drop_buf[2000];
28304d17814SAndrey V. Elsukov 
28404d17814SAndrey V. Elsukov 	if (pi->buf == NULL) {
28504d17814SAndrey V. Elsukov 		if ((pi->buf = buf_alloc(0)) == NULL) {
28604d17814SAndrey V. Elsukov 			(void)recvfrom(pi->fd, drop_buf, sizeof(drop_buf),
28704d17814SAndrey V. Elsukov 			    0, NULL, NULL);
28804d17814SAndrey V. Elsukov 			return (-1);
28904d17814SAndrey V. Elsukov 		}
29004d17814SAndrey V. Elsukov 		pi->buflen = buf_size(0);
29104d17814SAndrey V. Elsukov 	}
29204d17814SAndrey V. Elsukov 	return (0);
29304d17814SAndrey V. Elsukov }
29404d17814SAndrey V. Elsukov 
29504d17814SAndrey V. Elsukov /**
29604d17814SAndrey V. Elsukov  * Read message into input buffer. Get also the source address and any
29704d17814SAndrey V. Elsukov  * control data that is available. If the message is truncated, increment
29804d17814SAndrey V. Elsukov  * corresponding statistics.
29904d17814SAndrey V. Elsukov  *
30004d17814SAndrey V. Elsukov  * \param pi	input object
30104d17814SAndrey V. Elsukov  * \param msg	message object to fill
30204d17814SAndrey V. Elsukov  * \param cbuf	control data buffer
30304d17814SAndrey V. Elsukov  *
30404d17814SAndrey V. Elsukov  * \return -1 when something goes wrong, 0 othersise
30504d17814SAndrey V. Elsukov  */
30604d17814SAndrey V. Elsukov static int
inet_read_msg(struct port_input * pi,struct msghdr * msg,char * cbuf)30704d17814SAndrey V. Elsukov inet_read_msg(struct port_input *pi, struct msghdr *msg, char *cbuf)
30804d17814SAndrey V. Elsukov {
30904d17814SAndrey V. Elsukov 	struct iovec iov[1];
31004d17814SAndrey V. Elsukov 
31104d17814SAndrey V. Elsukov 	iov[0].iov_base = pi->buf;
31204d17814SAndrey V. Elsukov 	iov[0].iov_len = pi->buflen;
31304d17814SAndrey V. Elsukov 
31404d17814SAndrey V. Elsukov 	msg->msg_name = pi->peer;
31504d17814SAndrey V. Elsukov 	msg->msg_namelen = pi->peerlen;
31604d17814SAndrey V. Elsukov 	msg->msg_iov = iov;
31704d17814SAndrey V. Elsukov 	msg->msg_iovlen = 1;
31804d17814SAndrey V. Elsukov 	msg->msg_control = cbuf;
31904d17814SAndrey V. Elsukov 	msg->msg_controllen = RECV_CBUF_SIZE;
32004d17814SAndrey V. Elsukov 	msg->msg_flags = 0;
32104d17814SAndrey V. Elsukov 
32204d17814SAndrey V. Elsukov 	memset(cbuf, 0, RECV_CBUF_SIZE);
32304d17814SAndrey V. Elsukov 
32404d17814SAndrey V. Elsukov 	const ssize_t len = recvmsg(pi->fd, msg, 0);
32504d17814SAndrey V. Elsukov 
32604d17814SAndrey V. Elsukov 	if (len == -1 || len == 0)
32704d17814SAndrey V. Elsukov 		/* receive error */
32804d17814SAndrey V. Elsukov 		return (-1);
32904d17814SAndrey V. Elsukov 
33004d17814SAndrey V. Elsukov 	if (msg->msg_flags & MSG_TRUNC) {
33104d17814SAndrey V. Elsukov 		/* truncated - drop */
33204d17814SAndrey V. Elsukov 		snmpd_stats.silentDrops++;
33304d17814SAndrey V. Elsukov 		snmpd_stats.inTooLong++;
33404d17814SAndrey V. Elsukov 		return (-1);
33504d17814SAndrey V. Elsukov 	}
33604d17814SAndrey V. Elsukov 
33704d17814SAndrey V. Elsukov 	pi->length = (size_t)len;
33804d17814SAndrey V. Elsukov 
33904d17814SAndrey V. Elsukov 	return (0);
34004d17814SAndrey V. Elsukov }
34104d17814SAndrey V. Elsukov 
34204d17814SAndrey V. Elsukov /*
34304d17814SAndrey V. Elsukov  * Input available on socket.
34404d17814SAndrey V. Elsukov  *
34504d17814SAndrey V. Elsukov  * \param tp	transport port
34604d17814SAndrey V. Elsukov  * \param pi	input struct
34704d17814SAndrey V. Elsukov  *
34804d17814SAndrey V. Elsukov  * \return number of bytes received
34904d17814SAndrey V. Elsukov  */
35004d17814SAndrey V. Elsukov static ssize_t
inet_recv(struct tport * tp,struct port_input * pi)35104d17814SAndrey V. Elsukov inet_recv(struct tport *tp, struct port_input *pi)
35204d17814SAndrey V. Elsukov {
35304d17814SAndrey V. Elsukov 	struct inet_port *port = __containerof(tp, struct inet_port, tport);
35404d17814SAndrey V. Elsukov 	struct port_sock *sock = __containerof(pi, struct port_sock, input);
35504d17814SAndrey V. Elsukov 
35604d17814SAndrey V. Elsukov 	assert(port->proto == BegemotSnmpdTransportProto_udp);
35704d17814SAndrey V. Elsukov 
35804d17814SAndrey V. Elsukov 	if (inet_alloc_buf(pi) == -1)
35904d17814SAndrey V. Elsukov 		return (-1);
36004d17814SAndrey V. Elsukov 
36104d17814SAndrey V. Elsukov 	char cbuf[RECV_CBUF_SIZE];
36204d17814SAndrey V. Elsukov 	struct msghdr msg;
36304d17814SAndrey V. Elsukov 
36404d17814SAndrey V. Elsukov 	if (inet_read_msg(pi, &msg, cbuf) == -1)
36504d17814SAndrey V. Elsukov 		return (-1);
36604d17814SAndrey V. Elsukov 
36704d17814SAndrey V. Elsukov 	sock->parse_ctrl(sock, &msg);
36804d17814SAndrey V. Elsukov 
36904d17814SAndrey V. Elsukov 	return (0);
37004d17814SAndrey V. Elsukov }
37104d17814SAndrey V. Elsukov 
37204d17814SAndrey V. Elsukov /*
37304d17814SAndrey V. Elsukov  * Send message.
37404d17814SAndrey V. Elsukov  *
37504d17814SAndrey V. Elsukov  * \param tp		port
37604d17814SAndrey V. Elsukov  * \param buf		data to send
37704d17814SAndrey V. Elsukov  * \param len		number of bytes to send
37804d17814SAndrey V. Elsukov  * \param addr		destination address
37904d17814SAndrey V. Elsukov  * \param addlen	destination address length
38004d17814SAndrey V. Elsukov  *
38104d17814SAndrey V. Elsukov  * \return number of bytes sent
38204d17814SAndrey V. Elsukov  */
38304d17814SAndrey V. Elsukov static ssize_t
inet_send2(struct tport * tp,const u_char * buf,size_t len,struct port_input * pi)38404d17814SAndrey V. Elsukov inet_send2(struct tport *tp, const u_char *buf, size_t len,
38504d17814SAndrey V. Elsukov     struct port_input *pi)
38604d17814SAndrey V. Elsukov {
38704d17814SAndrey V. Elsukov 	struct inet_port *p = __containerof(tp, struct inet_port, tport);
38804d17814SAndrey V. Elsukov 	struct port_sock *s = (pi == NULL) ?  TAILQ_FIRST(&p->socks) :
38904d17814SAndrey V. Elsukov 	    __containerof(pi, struct port_sock, input);
39004d17814SAndrey V. Elsukov 
39104d17814SAndrey V. Elsukov 	struct iovec iov;
39204d17814SAndrey V. Elsukov 
39304d17814SAndrey V. Elsukov 	iov.iov_base = __DECONST(void*, buf);
39404d17814SAndrey V. Elsukov 	iov.iov_len = len;
39504d17814SAndrey V. Elsukov 
39604d17814SAndrey V. Elsukov 	struct msghdr msg;
39704d17814SAndrey V. Elsukov 
39804d17814SAndrey V. Elsukov 	msg.msg_flags = 0;
39904d17814SAndrey V. Elsukov 	msg.msg_iov = &iov;
40004d17814SAndrey V. Elsukov 	msg.msg_iovlen = 1;
40104d17814SAndrey V. Elsukov 	msg.msg_name = (void *)pi->peer;
40204d17814SAndrey V. Elsukov 	msg.msg_namelen = pi->peerlen;
40304d17814SAndrey V. Elsukov 
40404d17814SAndrey V. Elsukov 	char cbuf[XMIT_CBUF_SIZE];
40504d17814SAndrey V. Elsukov 	if (s->set_ret_source) {
406*ee2e9f4dSGleb Smirnoff 		s->setsrc(s, &msg, cbuf);
407*ee2e9f4dSGleb Smirnoff 	} else {
408*ee2e9f4dSGleb Smirnoff 		msg.msg_control = NULL;
409*ee2e9f4dSGleb Smirnoff 		msg.msg_controllen = 0;
41004d17814SAndrey V. Elsukov 	}
41104d17814SAndrey V. Elsukov 
41204d17814SAndrey V. Elsukov 	return (sendmsg(s->input.fd, &msg, 0));
41304d17814SAndrey V. Elsukov }
41404d17814SAndrey V. Elsukov 
41504d17814SAndrey V. Elsukov /** exported to daemon */
41604d17814SAndrey V. Elsukov const struct transport_def inet_trans = {
41704d17814SAndrey V. Elsukov 	"inet",
41804d17814SAndrey V. Elsukov 	OIDX_begemotSnmpdTransInet,
41904d17814SAndrey V. Elsukov 	inet_start,
42004d17814SAndrey V. Elsukov 	inet_stop,
42104d17814SAndrey V. Elsukov 	inet_destroy_port,
42204d17814SAndrey V. Elsukov 	inet_activate,
42304d17814SAndrey V. Elsukov 	NULL,
42404d17814SAndrey V. Elsukov 	inet_recv,
42504d17814SAndrey V. Elsukov 	inet_send2,
42604d17814SAndrey V. Elsukov };
42704d17814SAndrey V. Elsukov 
42804d17814SAndrey V. Elsukov struct inet_port_params {
42904d17814SAndrey V. Elsukov 	/** index oid */
43004d17814SAndrey V. Elsukov 	struct asn_oid index;
43104d17814SAndrey V. Elsukov 
43204d17814SAndrey V. Elsukov 	/** internet address type */
43304d17814SAndrey V. Elsukov 	enum InetAddressType type;
43404d17814SAndrey V. Elsukov 
43504d17814SAndrey V. Elsukov 	/** internet address */
43604d17814SAndrey V. Elsukov 	u_char *addr;
43704d17814SAndrey V. Elsukov 
43804d17814SAndrey V. Elsukov 	/** length of address */
43904d17814SAndrey V. Elsukov 	size_t addr_len;
44004d17814SAndrey V. Elsukov 
44104d17814SAndrey V. Elsukov 	/** port number */
44204d17814SAndrey V. Elsukov 	uint32_t port;
44304d17814SAndrey V. Elsukov 
44404d17814SAndrey V. Elsukov 	/** protocol */
44504d17814SAndrey V. Elsukov 	enum BegemotSnmpdTransportProto proto;
44604d17814SAndrey V. Elsukov };
44704d17814SAndrey V. Elsukov 
44804d17814SAndrey V. Elsukov /**
44904d17814SAndrey V. Elsukov  * IPv4 creation stuff. Parse the index, fill socket address and creates
45004d17814SAndrey V. Elsukov  * a port_sock.
45104d17814SAndrey V. Elsukov  *
45204d17814SAndrey V. Elsukov  * \param port		the port to create
45304d17814SAndrey V. Elsukov  * \param params	parameters from the SNMP SET
45404d17814SAndrey V. Elsukov  *
45504d17814SAndrey V. Elsukov  * \return SNMP error
45604d17814SAndrey V. Elsukov  */
45704d17814SAndrey V. Elsukov static int
ipv4_create(struct inet_port * port,struct inet_port_params * params)45804d17814SAndrey V. Elsukov ipv4_create(struct inet_port *port, struct inet_port_params *params)
45904d17814SAndrey V. Elsukov {
46004d17814SAndrey V. Elsukov 
46104d17814SAndrey V. Elsukov 	if (params->addr_len != 4)
46204d17814SAndrey V. Elsukov 		return (SNMP_ERR_INCONS_VALUE);
46304d17814SAndrey V. Elsukov 
46404d17814SAndrey V. Elsukov 	struct port_sock *sock = calloc(1, sizeof(struct port_sock));
46504d17814SAndrey V. Elsukov 	if (sock == NULL)
46604d17814SAndrey V. Elsukov 		return (SNMP_ERR_GENERR);
46704d17814SAndrey V. Elsukov 
46804d17814SAndrey V. Elsukov 	snmpd_input_init(&sock->input);
46904d17814SAndrey V. Elsukov 
47004d17814SAndrey V. Elsukov 	TAILQ_INSERT_HEAD(&port->socks, sock, link);
47104d17814SAndrey V. Elsukov 
47204d17814SAndrey V. Elsukov 	struct sockaddr_in *sin =
47304d17814SAndrey V. Elsukov 	    (struct sockaddr_in *)&sock->bind_addr;
47404d17814SAndrey V. Elsukov 
47504d17814SAndrey V. Elsukov 	sin->sin_len = sizeof(struct sockaddr_in);
47604d17814SAndrey V. Elsukov 	sin->sin_family = AF_INET;
47704d17814SAndrey V. Elsukov 	sin->sin_port = htons(params->port);
47857c6d427SAndrey V. Elsukov 	memcpy(&sin->sin_addr, params->addr, 4); /* network byte order */
47904d17814SAndrey V. Elsukov 
48004d17814SAndrey V. Elsukov 	sock->port = port;
48104d17814SAndrey V. Elsukov 
48204d17814SAndrey V. Elsukov 	return (SNMP_ERR_NOERROR);
48304d17814SAndrey V. Elsukov }
48404d17814SAndrey V. Elsukov 
48504d17814SAndrey V. Elsukov /*
48604d17814SAndrey V. Elsukov  * An IPv4 inet port is ready. Delegate to the generic code to read the data
48704d17814SAndrey V. Elsukov  * and react.
48804d17814SAndrey V. Elsukov  *
48904d17814SAndrey V. Elsukov  * \param fd	file descriptor that is ready
49004d17814SAndrey V. Elsukov  * \param udata	inet_port pointer
49104d17814SAndrey V. Elsukov  */
49204d17814SAndrey V. Elsukov static void
ipv4_input(int fd __unused,void * udata)49304d17814SAndrey V. Elsukov ipv4_input(int fd __unused, void *udata)
49404d17814SAndrey V. Elsukov {
49504d17814SAndrey V. Elsukov 	struct port_sock *sock = udata;
49604d17814SAndrey V. Elsukov 
49704d17814SAndrey V. Elsukov 	sock->input.peerlen = sizeof(struct sockaddr_in);
49804d17814SAndrey V. Elsukov 	snmpd_input(&sock->input, &sock->port->tport);
49904d17814SAndrey V. Elsukov }
50004d17814SAndrey V. Elsukov 
50104d17814SAndrey V. Elsukov /**
50204d17814SAndrey V. Elsukov  * Activate an IPv4 socket.
50304d17814SAndrey V. Elsukov  *
50404d17814SAndrey V. Elsukov  * \param sock	thhe socket to activate
50504d17814SAndrey V. Elsukov  *
50604d17814SAndrey V. Elsukov  * \return error code
50704d17814SAndrey V. Elsukov  */
50804d17814SAndrey V. Elsukov static int
ipv4_activate_sock(struct port_sock * sock)50904d17814SAndrey V. Elsukov ipv4_activate_sock(struct port_sock *sock)
51004d17814SAndrey V. Elsukov {
51104d17814SAndrey V. Elsukov 	if ((sock->input.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
51204d17814SAndrey V. Elsukov 		syslog(LOG_ERR, "creating UDP4 socket: %m");
51304d17814SAndrey V. Elsukov 		return (SNMP_ERR_RES_UNAVAIL);
51404d17814SAndrey V. Elsukov 	}
51504d17814SAndrey V. Elsukov 
51604d17814SAndrey V. Elsukov 	const struct sockaddr_in *sin =
51704d17814SAndrey V. Elsukov 	    (const struct sockaddr_in *)&sock->bind_addr;
51804d17814SAndrey V. Elsukov 
51904d17814SAndrey V. Elsukov 	if (sin->sin_addr.s_addr == INADDR_ANY) {
52004d17814SAndrey V. Elsukov 		/* need to know from which address to return */
52104d17814SAndrey V. Elsukov 		static const int on = 1;
52204d17814SAndrey V. Elsukov 
52304d17814SAndrey V. Elsukov 		if (setsockopt(sock->input.fd, IPPROTO_IP, IP_RECVDSTADDR, &on,
52404d17814SAndrey V. Elsukov 		    sizeof(on)) == -1) {
52504d17814SAndrey V. Elsukov 			syslog(LOG_ERR, "setsockopt(IP_RECVDSTADDR): %m");
52604d17814SAndrey V. Elsukov 			(void)close(sock->input.fd);
52704d17814SAndrey V. Elsukov 			sock->input.fd = -1;
52804d17814SAndrey V. Elsukov 			return (SNMP_ERR_GENERR);
52904d17814SAndrey V. Elsukov 		}
53004d17814SAndrey V. Elsukov 		sock->set_ret_source = true;
53104d17814SAndrey V. Elsukov 	}
53204d17814SAndrey V. Elsukov 
53304d17814SAndrey V. Elsukov 	if (bind(sock->input.fd, (const struct sockaddr *)sin, sizeof(*sin))) {
53404d17814SAndrey V. Elsukov 		if (errno == EADDRNOTAVAIL) {
53504d17814SAndrey V. Elsukov 			(void)close(sock->input.fd);
53604d17814SAndrey V. Elsukov 			sock->input.fd = -1;
53704d17814SAndrey V. Elsukov 			return (SNMP_ERR_INCONS_NAME);
53804d17814SAndrey V. Elsukov 		}
53904d17814SAndrey V. Elsukov 		syslog(LOG_ERR, "bind: %s:%u %m", inet_ntoa(sin->sin_addr),
54004d17814SAndrey V. Elsukov 		    ntohs(sin->sin_port));
54104d17814SAndrey V. Elsukov 		(void)close(sock->input.fd);
54204d17814SAndrey V. Elsukov 		sock->input.fd = -1;
54304d17814SAndrey V. Elsukov 		return (SNMP_ERR_GENERR);
54404d17814SAndrey V. Elsukov 	}
54504d17814SAndrey V. Elsukov 
54604d17814SAndrey V. Elsukov 	if ((sock->input.id = fd_select(sock->input.fd, ipv4_input,
54704d17814SAndrey V. Elsukov 	    sock, NULL)) == NULL) {
54804d17814SAndrey V. Elsukov 		(void)close(sock->input.fd);
54904d17814SAndrey V. Elsukov 		sock->input.fd = -1;
55004d17814SAndrey V. Elsukov 		return (SNMP_ERR_GENERR);
55104d17814SAndrey V. Elsukov 	}
55204d17814SAndrey V. Elsukov 	sock->input.peer = (struct sockaddr *)&sock->ret_dest;
55304d17814SAndrey V. Elsukov 
55404d17814SAndrey V. Elsukov 	sock->parse_ctrl = ipv4_parse_ctrl;
55504d17814SAndrey V. Elsukov 	sock->setsrc = ipv4_setsrc;
55604d17814SAndrey V. Elsukov 
55704d17814SAndrey V. Elsukov 	return (SNMP_ERR_NOERROR);
55804d17814SAndrey V. Elsukov }
55904d17814SAndrey V. Elsukov 
56004d17814SAndrey V. Elsukov /**
56104d17814SAndrey V. Elsukov  * Open an IPv4 socket. Make the socket, bind it and put it on the select
56204d17814SAndrey V. Elsukov  * list. The socket struct has already been created during creation.
56304d17814SAndrey V. Elsukov  *
56404d17814SAndrey V. Elsukov  * \param p	inet port
56504d17814SAndrey V. Elsukov  *
56604d17814SAndrey V. Elsukov  * \return SNMP error code
56704d17814SAndrey V. Elsukov  */
56804d17814SAndrey V. Elsukov static int
ipv4_activate(struct inet_port * p)56904d17814SAndrey V. Elsukov ipv4_activate(struct inet_port *p)
57004d17814SAndrey V. Elsukov {
57104d17814SAndrey V. Elsukov 	struct port_sock *sock = TAILQ_FIRST(&p->socks);
57204d17814SAndrey V. Elsukov 	assert(sock);
57304d17814SAndrey V. Elsukov 	assert(!TAILQ_NEXT(sock, link));
57404d17814SAndrey V. Elsukov 
57504d17814SAndrey V. Elsukov 	const int ret = ipv4_activate_sock(sock);
57604d17814SAndrey V. Elsukov 	if (ret == SNMP_ERR_NOERROR)
57704d17814SAndrey V. Elsukov 		p->row_status = RowStatus_active;
57804d17814SAndrey V. Elsukov 
57904d17814SAndrey V. Elsukov 	return (ret);
58004d17814SAndrey V. Elsukov }
58104d17814SAndrey V. Elsukov 
58204d17814SAndrey V. Elsukov /**
58304d17814SAndrey V. Elsukov  * Close an IPv4 socket. Keep the sock object.
58404d17814SAndrey V. Elsukov  *
58504d17814SAndrey V. Elsukov  * \param p	inet port
58604d17814SAndrey V. Elsukov  */
58704d17814SAndrey V. Elsukov static void
ipv4_deactivate(struct inet_port * p)58804d17814SAndrey V. Elsukov ipv4_deactivate(struct inet_port *p)
58904d17814SAndrey V. Elsukov {
59004d17814SAndrey V. Elsukov 	struct port_sock *sock = TAILQ_FIRST(&p->socks);
59104d17814SAndrey V. Elsukov 	assert(sock);
59204d17814SAndrey V. Elsukov 	assert(!TAILQ_NEXT(sock, link));
59304d17814SAndrey V. Elsukov 
59404d17814SAndrey V. Elsukov 	snmpd_input_close(&sock->input);
59504d17814SAndrey V. Elsukov 
59604d17814SAndrey V. Elsukov 	p->row_status = RowStatus_notInService;
59704d17814SAndrey V. Elsukov }
59804d17814SAndrey V. Elsukov 
59904d17814SAndrey V. Elsukov /**
60004d17814SAndrey V. Elsukov  * Parse the control data received with a UDPv4 packet. This may contain
60104d17814SAndrey V. Elsukov  * credentials (for a local connection) and the address of the interface
60204d17814SAndrey V. Elsukov  * the message was received on. If there are credentials set the priv flag
60304d17814SAndrey V. Elsukov  * if the effective UID is zero.
60404d17814SAndrey V. Elsukov  *
60504d17814SAndrey V. Elsukov  * \param sock	the sock object
60604d17814SAndrey V. Elsukov  * \param msg	the received message
60704d17814SAndrey V. Elsukov  */
60804d17814SAndrey V. Elsukov static void
ipv4_parse_ctrl(struct port_sock * sock,const struct msghdr * msg)60904d17814SAndrey V. Elsukov ipv4_parse_ctrl(struct port_sock *sock, const struct msghdr *msg)
61004d17814SAndrey V. Elsukov {
61104d17814SAndrey V. Elsukov 	struct sockcred *cred = NULL;
61204d17814SAndrey V. Elsukov 
61304d17814SAndrey V. Elsukov 	for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
61404d17814SAndrey V. Elsukov 	    cmsg = CMSG_NXTHDR(msg, cmsg)) {
61504d17814SAndrey V. Elsukov 
61604d17814SAndrey V. Elsukov 		if (cmsg->cmsg_level == IPPROTO_IP &&
61704d17814SAndrey V. Elsukov 		    cmsg->cmsg_type == IP_RECVDSTADDR) {
61804d17814SAndrey V. Elsukov 			memcpy(&sock->ret_source.a4, CMSG_DATA(cmsg),
61904d17814SAndrey V. Elsukov 			    sizeof(struct in_addr));
62004d17814SAndrey V. Elsukov 
62104d17814SAndrey V. Elsukov 		} else if (cmsg->cmsg_level == SOL_SOCKET &&
62204d17814SAndrey V. Elsukov 		    cmsg->cmsg_type == SCM_CREDS) {
62304d17814SAndrey V. Elsukov 			cred = (struct sockcred *)(void *)CMSG_DATA(cmsg);
62404d17814SAndrey V. Elsukov 		}
62504d17814SAndrey V. Elsukov 	}
62604d17814SAndrey V. Elsukov 
62704d17814SAndrey V. Elsukov 	sock->input.priv = 0;
62804d17814SAndrey V. Elsukov 	if (sock->input.cred && cred)
62904d17814SAndrey V. Elsukov 		/* remote end has sent credentials */
63004d17814SAndrey V. Elsukov 		sock->input.priv = (cred->sc_euid == 0);
63104d17814SAndrey V. Elsukov }
63204d17814SAndrey V. Elsukov 
63304d17814SAndrey V. Elsukov /**
63404d17814SAndrey V. Elsukov  * Set the source address option for IPv4 sockets.
63504d17814SAndrey V. Elsukov  *
63604d17814SAndrey V. Elsukov  * \param sock	socket object
63704d17814SAndrey V. Elsukov  * \param msg	message
63804d17814SAndrey V. Elsukov  */
63904d17814SAndrey V. Elsukov static void
ipv4_setsrc(struct port_sock * sock,struct msghdr * msg,char * cbuf)640*ee2e9f4dSGleb Smirnoff ipv4_setsrc(struct port_sock *sock, struct msghdr *msg, char *cbuf)
64104d17814SAndrey V. Elsukov {
642*ee2e9f4dSGleb Smirnoff 	struct cmsghdr *cmsg;
643*ee2e9f4dSGleb Smirnoff 
644*ee2e9f4dSGleb Smirnoff 	msg->msg_control = cbuf;
645*ee2e9f4dSGleb Smirnoff 	msg->msg_controllen = CMSG_SPACE(sizeof(struct in_addr));
64604d17814SAndrey V. Elsukov 
64704d17814SAndrey V. Elsukov 	/* select outgoing interface by setting source address */
648*ee2e9f4dSGleb Smirnoff 	cmsg = CMSG_FIRSTHDR(msg);
64904d17814SAndrey V. Elsukov 	cmsg->cmsg_level = IPPROTO_IP;
65004d17814SAndrey V. Elsukov 	cmsg->cmsg_type = IP_SENDSRCADDR;
65104d17814SAndrey V. Elsukov 	cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
65204d17814SAndrey V. Elsukov 	memcpy(CMSG_DATA(cmsg), &sock->ret_source.a4,
65304d17814SAndrey V. Elsukov 	    sizeof(struct in_addr));
65404d17814SAndrey V. Elsukov }
65504d17814SAndrey V. Elsukov 
65604d17814SAndrey V. Elsukov /**
65704d17814SAndrey V. Elsukov  * Common part of IPv6 creation. This is used by both ipv6_create() and
65804d17814SAndrey V. Elsukov  * ipv6z_create().
65904d17814SAndrey V. Elsukov  *
66004d17814SAndrey V. Elsukov  * \param port		the table row
66104d17814SAndrey V. Elsukov  * \param params	creation parameters
66204d17814SAndrey V. Elsukov  * \param scope_id	scope_id (0 or from index)
66304d17814SAndrey V. Elsukov  *
66404d17814SAndrey V. Elsukov  * \return SNMP_ERR_NOERROR if port has been created, error code otherwise
66504d17814SAndrey V. Elsukov  */
66604d17814SAndrey V. Elsukov static int
ipv6_create_common(struct inet_port * port,struct inet_port_params * params,u_int scope_id)66704d17814SAndrey V. Elsukov ipv6_create_common(struct inet_port *port, struct inet_port_params *params,
66804d17814SAndrey V. Elsukov     u_int scope_id)
66904d17814SAndrey V. Elsukov {
67004d17814SAndrey V. Elsukov 	struct port_sock *sock = calloc(1, sizeof(struct port_sock));
67104d17814SAndrey V. Elsukov 
67204d17814SAndrey V. Elsukov 	if (sock == NULL)
67304d17814SAndrey V. Elsukov 		return (SNMP_ERR_GENERR);
67404d17814SAndrey V. Elsukov 
67504d17814SAndrey V. Elsukov 	struct sockaddr_in6 *sin = (struct sockaddr_in6 *)&sock->bind_addr;
67604d17814SAndrey V. Elsukov 
67704d17814SAndrey V. Elsukov 	sin->sin6_len = sizeof(struct sockaddr_in6);
67804d17814SAndrey V. Elsukov 	sin->sin6_family = AF_INET6;
67904d17814SAndrey V. Elsukov 	sin->sin6_port = htons(params->port);
68004d17814SAndrey V. Elsukov 	sin->sin6_flowinfo = 0;
68104d17814SAndrey V. Elsukov 	sin->sin6_scope_id = scope_id;
68204d17814SAndrey V. Elsukov 
68304d17814SAndrey V. Elsukov 	memcpy(sin->sin6_addr.s6_addr, params->addr, 16);
68404d17814SAndrey V. Elsukov 
68504d17814SAndrey V. Elsukov 	if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) && scope_id == 0) {
68604d17814SAndrey V. Elsukov 		char buf[INET6_ADDRSTRLEN];
68704d17814SAndrey V. Elsukov 		syslog(LOG_INFO, "%s: link local address used without scope "
68804d17814SAndrey V. Elsukov 		    "index: %s", __func__, inet_ntop(AF_INET6,
68904d17814SAndrey V. Elsukov 		    &sin->sin6_addr, buf, sizeof(buf)));
69004d17814SAndrey V. Elsukov 		free(sock);
69104d17814SAndrey V. Elsukov 		return (SNMP_ERR_NO_CREATION);
69204d17814SAndrey V. Elsukov 	}
69304d17814SAndrey V. Elsukov 
69404d17814SAndrey V. Elsukov 	sock->port = port;
69504d17814SAndrey V. Elsukov 
69604d17814SAndrey V. Elsukov 	snmpd_input_init(&sock->input);
69704d17814SAndrey V. Elsukov 	TAILQ_INSERT_HEAD(&port->socks, sock, link);
69804d17814SAndrey V. Elsukov 
69904d17814SAndrey V. Elsukov 	return (SNMP_ERR_NOERROR);
70004d17814SAndrey V. Elsukov }
70104d17814SAndrey V. Elsukov 
70204d17814SAndrey V. Elsukov /**
70304d17814SAndrey V. Elsukov  * IPv6 creation stuff. Parse the index, fill socket address and creates
70404d17814SAndrey V. Elsukov  * a port_sock.
70504d17814SAndrey V. Elsukov  *
70604d17814SAndrey V. Elsukov  * \param port		the port to create
70704d17814SAndrey V. Elsukov  * \param params	parameters from the SNMP SET
70804d17814SAndrey V. Elsukov  *
70904d17814SAndrey V. Elsukov  * \return SNMP error
71004d17814SAndrey V. Elsukov  */
71104d17814SAndrey V. Elsukov static int
ipv6_create(struct inet_port * port,struct inet_port_params * params)71204d17814SAndrey V. Elsukov ipv6_create(struct inet_port *port, struct inet_port_params *params)
71304d17814SAndrey V. Elsukov {
71404d17814SAndrey V. Elsukov 	if (params->addr_len != 16)
71504d17814SAndrey V. Elsukov 		return (SNMP_ERR_INCONS_VALUE);
71604d17814SAndrey V. Elsukov 
71704d17814SAndrey V. Elsukov 	const int ret = ipv6_create_common(port, params, 0);
71804d17814SAndrey V. Elsukov 	if (ret != SNMP_ERR_NOERROR)
71904d17814SAndrey V. Elsukov 		return (ret);
72004d17814SAndrey V. Elsukov 
72104d17814SAndrey V. Elsukov 	return (SNMP_ERR_NOERROR);
72204d17814SAndrey V. Elsukov }
72304d17814SAndrey V. Elsukov 
72404d17814SAndrey V. Elsukov /*
72504d17814SAndrey V. Elsukov  * An IPv6 inet port is ready. Delegate to the generic code to read the data
72604d17814SAndrey V. Elsukov  * and react.
72704d17814SAndrey V. Elsukov  *
72804d17814SAndrey V. Elsukov  * \param fd	file descriptor that is ready
72904d17814SAndrey V. Elsukov  * \param udata	inet_port pointer
73004d17814SAndrey V. Elsukov  */
73104d17814SAndrey V. Elsukov static void
ipv6_input(int fd __unused,void * udata)73204d17814SAndrey V. Elsukov ipv6_input(int fd __unused, void *udata)
73304d17814SAndrey V. Elsukov {
73404d17814SAndrey V. Elsukov 	struct port_sock *sock = udata;
73504d17814SAndrey V. Elsukov 
73604d17814SAndrey V. Elsukov 	sock->input.peerlen = sizeof(struct sockaddr_in6);
73704d17814SAndrey V. Elsukov 	snmpd_input(&sock->input, &sock->port->tport);
73804d17814SAndrey V. Elsukov }
73904d17814SAndrey V. Elsukov 
74004d17814SAndrey V. Elsukov /**
74104d17814SAndrey V. Elsukov  * Activate an IPv6 socket.
74204d17814SAndrey V. Elsukov  *
74304d17814SAndrey V. Elsukov  * \param sock	thhe socket to activate
74404d17814SAndrey V. Elsukov  *
74504d17814SAndrey V. Elsukov  * \return error code
74604d17814SAndrey V. Elsukov  */
74704d17814SAndrey V. Elsukov static int
ipv6_activate_sock(struct port_sock * sock)74804d17814SAndrey V. Elsukov ipv6_activate_sock(struct port_sock *sock)
74904d17814SAndrey V. Elsukov {
75004d17814SAndrey V. Elsukov 	if ((sock->input.fd = socket(PF_INET6, SOCK_DGRAM, 0)) == -1) {
75104d17814SAndrey V. Elsukov 		syslog(LOG_ERR, "creating UDP6 socket: %m");
75204d17814SAndrey V. Elsukov 		return (SNMP_ERR_RES_UNAVAIL);
75304d17814SAndrey V. Elsukov 	}
75404d17814SAndrey V. Elsukov 
75504d17814SAndrey V. Elsukov 	const struct sockaddr_in6 *sin =
75604d17814SAndrey V. Elsukov 	    (const struct sockaddr_in6 *)&sock->bind_addr;
75704d17814SAndrey V. Elsukov 
75804d17814SAndrey V. Elsukov 	if (memcmp(&sin->sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0) {
75904d17814SAndrey V. Elsukov 		/* need to know from which address to return */
76004d17814SAndrey V. Elsukov 		static const int on = 1;
76104d17814SAndrey V. Elsukov 
76204d17814SAndrey V. Elsukov 		if (setsockopt(sock->input.fd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
76304d17814SAndrey V. Elsukov 		    &on, sizeof(on)) == -1) {
76404d17814SAndrey V. Elsukov 			syslog(LOG_ERR, "setsockopt(IP6_PKTINFO): %m");
76504d17814SAndrey V. Elsukov 			(void)close(sock->input.fd);
76604d17814SAndrey V. Elsukov 			sock->input.fd = -1;
76704d17814SAndrey V. Elsukov 			return (SNMP_ERR_GENERR);
76804d17814SAndrey V. Elsukov 		}
76904d17814SAndrey V. Elsukov 		sock->set_ret_source = true;
77004d17814SAndrey V. Elsukov 	}
77104d17814SAndrey V. Elsukov 
77204d17814SAndrey V. Elsukov 	if (bind(sock->input.fd, (const struct sockaddr *)sin, sizeof(*sin))) {
77304d17814SAndrey V. Elsukov 		if (community != COMM_INITIALIZE && errno == EADDRNOTAVAIL) {
77404d17814SAndrey V. Elsukov 			(void)close(sock->input.fd);
77504d17814SAndrey V. Elsukov 			sock->input.fd = -1;
77604d17814SAndrey V. Elsukov 			return (SNMP_ERR_INCONS_NAME);
77704d17814SAndrey V. Elsukov 		}
77804d17814SAndrey V. Elsukov 		char buf[INET6_ADDRSTRLEN];
77904d17814SAndrey V. Elsukov 		syslog(LOG_ERR, "bind: %s:%u:%u %m", inet_ntop(AF_INET6,
78004d17814SAndrey V. Elsukov 		    &sin->sin6_addr, buf, sizeof(buf)), sin->sin6_scope_id,
78104d17814SAndrey V. Elsukov 		    ntohs(sin->sin6_port));
78204d17814SAndrey V. Elsukov 		(void)close(sock->input.fd);
78304d17814SAndrey V. Elsukov 		sock->input.fd = -1;
78404d17814SAndrey V. Elsukov 		return (SNMP_ERR_GENERR);
78504d17814SAndrey V. Elsukov 	}
78604d17814SAndrey V. Elsukov 	if ((sock->input.id = fd_select(sock->input.fd, ipv6_input,
78704d17814SAndrey V. Elsukov 	    sock, NULL)) == NULL) {
78804d17814SAndrey V. Elsukov 		(void)close(sock->input.fd);
78904d17814SAndrey V. Elsukov 		sock->input.fd = -1;
79004d17814SAndrey V. Elsukov 		return (SNMP_ERR_GENERR);
79104d17814SAndrey V. Elsukov 	}
79204d17814SAndrey V. Elsukov 	sock->input.peer = (struct sockaddr *)&sock->ret_dest;
79304d17814SAndrey V. Elsukov 
79404d17814SAndrey V. Elsukov 	sock->parse_ctrl = ipv6_parse_ctrl;
79504d17814SAndrey V. Elsukov 	sock->setsrc = ipv6_setsrc;
79604d17814SAndrey V. Elsukov 
79704d17814SAndrey V. Elsukov 	return (SNMP_ERR_NOERROR);
79804d17814SAndrey V. Elsukov }
79904d17814SAndrey V. Elsukov 
80004d17814SAndrey V. Elsukov /**
80104d17814SAndrey V. Elsukov  * Open an IPv6 socket.
80204d17814SAndrey V. Elsukov  *
80304d17814SAndrey V. Elsukov  * \param port	inet port
80404d17814SAndrey V. Elsukov  *
80504d17814SAndrey V. Elsukov  * \return SNMP error code
80604d17814SAndrey V. Elsukov  */
80704d17814SAndrey V. Elsukov static int
ipv6_activate(struct inet_port * p)80804d17814SAndrey V. Elsukov ipv6_activate(struct inet_port *p)
80904d17814SAndrey V. Elsukov {
81004d17814SAndrey V. Elsukov 	struct port_sock *sock = TAILQ_FIRST(&p->socks);
81104d17814SAndrey V. Elsukov 	assert(sock);
81204d17814SAndrey V. Elsukov 
81304d17814SAndrey V. Elsukov 	const int ret = ipv6_activate_sock(sock);
81404d17814SAndrey V. Elsukov 
81504d17814SAndrey V. Elsukov 	if (ret == SNMP_ERR_NOERROR)
81604d17814SAndrey V. Elsukov 		p->row_status = RowStatus_active;
81704d17814SAndrey V. Elsukov 	return (ret);
81804d17814SAndrey V. Elsukov }
81904d17814SAndrey V. Elsukov 
82004d17814SAndrey V. Elsukov /**
82104d17814SAndrey V. Elsukov  * Close an IPv6 socket. Keep the sock object.
82204d17814SAndrey V. Elsukov  *
82304d17814SAndrey V. Elsukov  * \param p	inet port
82404d17814SAndrey V. Elsukov  */
82504d17814SAndrey V. Elsukov static void
ipv6_deactivate(struct inet_port * p)82604d17814SAndrey V. Elsukov ipv6_deactivate(struct inet_port *p)
82704d17814SAndrey V. Elsukov {
82804d17814SAndrey V. Elsukov 	struct port_sock *sock = TAILQ_FIRST(&p->socks);
82904d17814SAndrey V. Elsukov 	assert(sock);
83004d17814SAndrey V. Elsukov 	assert(!TAILQ_NEXT(sock, link));
83104d17814SAndrey V. Elsukov 
83204d17814SAndrey V. Elsukov 	snmpd_input_close(&sock->input);
83304d17814SAndrey V. Elsukov 
83404d17814SAndrey V. Elsukov 	p->row_status = RowStatus_notInService;
83504d17814SAndrey V. Elsukov }
83604d17814SAndrey V. Elsukov 
83704d17814SAndrey V. Elsukov /**
83804d17814SAndrey V. Elsukov  * IPv6 specific part of message processing. The control data may contain
83904d17814SAndrey V. Elsukov  * credentials and packet info that contains the destination address of
84004d17814SAndrey V. Elsukov  * the packet.
84104d17814SAndrey V. Elsukov  *
84204d17814SAndrey V. Elsukov  * \param sock	the sock object
84304d17814SAndrey V. Elsukov  * \param msg	the received message
84404d17814SAndrey V. Elsukov  */
84504d17814SAndrey V. Elsukov static void
ipv6_parse_ctrl(struct port_sock * sock,const struct msghdr * msg)84604d17814SAndrey V. Elsukov ipv6_parse_ctrl(struct port_sock *sock, const struct msghdr *msg)
84704d17814SAndrey V. Elsukov {
84804d17814SAndrey V. Elsukov 	struct sockcred *cred = NULL;
84904d17814SAndrey V. Elsukov 
85004d17814SAndrey V. Elsukov 	for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
85104d17814SAndrey V. Elsukov 	    cmsg = CMSG_NXTHDR(msg, cmsg)) {
85204d17814SAndrey V. Elsukov 
85304d17814SAndrey V. Elsukov 		if (cmsg->cmsg_level == IPPROTO_IPV6 &&
85404d17814SAndrey V. Elsukov 		    cmsg->cmsg_type == IPV6_PKTINFO) {
85504d17814SAndrey V. Elsukov 			const struct in6_pktinfo *info =
85604d17814SAndrey V. Elsukov 			    (const struct in6_pktinfo *)(const void *)
85704d17814SAndrey V. Elsukov 			    CMSG_DATA(cmsg);
85804d17814SAndrey V. Elsukov 			sock->ret_source.a6.ipi6_addr = info->ipi6_addr;
85904d17814SAndrey V. Elsukov 			sock->ret_source.a6.ipi6_ifindex =
86004d17814SAndrey V. Elsukov 			    !IN6_IS_ADDR_LINKLOCAL(&info->ipi6_addr) ? 0:
86104d17814SAndrey V. Elsukov 			    info->ipi6_ifindex;
8620bf56da3SHartmut Brandt 
86304d17814SAndrey V. Elsukov 		} else if (cmsg->cmsg_level == SOL_SOCKET &&
86404d17814SAndrey V. Elsukov 		    cmsg->cmsg_type == SCM_CREDS) {
86504d17814SAndrey V. Elsukov 			cred = (struct sockcred *)(void *)CMSG_DATA(cmsg);
86604d17814SAndrey V. Elsukov 		}
86704d17814SAndrey V. Elsukov 	}
86804d17814SAndrey V. Elsukov 
86904d17814SAndrey V. Elsukov 	sock->input.priv = 0;
87004d17814SAndrey V. Elsukov 	if (sock->input.cred && cred)
87104d17814SAndrey V. Elsukov 		/* remote end has sent credentials */
87204d17814SAndrey V. Elsukov 		sock->input.priv = (cred->sc_euid == 0);
87304d17814SAndrey V. Elsukov }
87404d17814SAndrey V. Elsukov 
87504d17814SAndrey V. Elsukov /**
87604d17814SAndrey V. Elsukov  * Set the source address option for IPv4 sockets.
87704d17814SAndrey V. Elsukov  *
87804d17814SAndrey V. Elsukov  * \param sock	socket object
87904d17814SAndrey V. Elsukov  * \param msg	message
88004d17814SAndrey V. Elsukov  */
88104d17814SAndrey V. Elsukov static void
ipv6_setsrc(struct port_sock * sock,struct msghdr * msg,char * cbuf)882*ee2e9f4dSGleb Smirnoff ipv6_setsrc(struct port_sock *sock, struct msghdr *msg, char *cbuf)
88304d17814SAndrey V. Elsukov {
884*ee2e9f4dSGleb Smirnoff 	struct cmsghdr *cmsg;
885*ee2e9f4dSGleb Smirnoff 
886*ee2e9f4dSGleb Smirnoff 	msg->msg_control = cbuf;
887*ee2e9f4dSGleb Smirnoff 	msg->msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
88804d17814SAndrey V. Elsukov 
88904d17814SAndrey V. Elsukov 	/* select outgoing interface by setting source address */
890*ee2e9f4dSGleb Smirnoff 	cmsg = CMSG_FIRSTHDR(msg);
89104d17814SAndrey V. Elsukov 	cmsg->cmsg_level = IPPROTO_IPV6;
89204d17814SAndrey V. Elsukov 	cmsg->cmsg_type = IPV6_PKTINFO;
89304d17814SAndrey V. Elsukov 	cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
89404d17814SAndrey V. Elsukov 	memcpy(CMSG_DATA(cmsg), &sock->ret_source.a6,
89504d17814SAndrey V. Elsukov 	    sizeof(struct in6_pktinfo));
89604d17814SAndrey V. Elsukov }
89704d17814SAndrey V. Elsukov 
89804d17814SAndrey V. Elsukov /**
89904d17814SAndrey V. Elsukov  * IPv6z creation stuff. Parse the index, fill socket address and creates
90004d17814SAndrey V. Elsukov  * a port_sock.
90104d17814SAndrey V. Elsukov  *
90204d17814SAndrey V. Elsukov  * \param port		the port to create
90304d17814SAndrey V. Elsukov  * \param params	parameters from the SNMP SET
90404d17814SAndrey V. Elsukov  *
90504d17814SAndrey V. Elsukov  * \return SNMP error
90604d17814SAndrey V. Elsukov  */
90704d17814SAndrey V. Elsukov static int
ipv6z_create(struct inet_port * port,struct inet_port_params * params)90804d17814SAndrey V. Elsukov ipv6z_create(struct inet_port *port, struct inet_port_params *params)
90904d17814SAndrey V. Elsukov {
91004d17814SAndrey V. Elsukov 	if (params->addr_len != 20)
91104d17814SAndrey V. Elsukov 		return (SNMP_ERR_INCONS_VALUE);
91204d17814SAndrey V. Elsukov 
91304d17814SAndrey V. Elsukov 	u_int scope_id = 0;
91404d17814SAndrey V. Elsukov 	for (u_int i = 16; i < 20; i++) {
91504d17814SAndrey V. Elsukov 		scope_id <<= 8;
91604d17814SAndrey V. Elsukov 		scope_id |= params->addr[i];
91704d17814SAndrey V. Elsukov 	}
91804d17814SAndrey V. Elsukov 
91904d17814SAndrey V. Elsukov 	const int ret = ipv6_create_common(port, params, scope_id);
92004d17814SAndrey V. Elsukov 	if (ret != SNMP_ERR_NOERROR)
92104d17814SAndrey V. Elsukov 		return (ret);
92204d17814SAndrey V. Elsukov 
92304d17814SAndrey V. Elsukov 	return (SNMP_ERR_NOERROR);
92404d17814SAndrey V. Elsukov }
92504d17814SAndrey V. Elsukov 
92604d17814SAndrey V. Elsukov /**
92704d17814SAndrey V. Elsukov  * DNS name creation stuff. Parse the index and save the string.
92804d17814SAndrey V. Elsukov  * This does not create a socket struct.
92904d17814SAndrey V. Elsukov  *
93004d17814SAndrey V. Elsukov  * \param port		the port to create
93104d17814SAndrey V. Elsukov  * \param params	parameters from the SNMP SET
93204d17814SAndrey V. Elsukov  *
93304d17814SAndrey V. Elsukov  * \return SNMP error
93404d17814SAndrey V. Elsukov  */
93504d17814SAndrey V. Elsukov static int
dns_create(struct inet_port * port,struct inet_port_params * params)93604d17814SAndrey V. Elsukov dns_create(struct inet_port *port, struct inet_port_params *params)
93704d17814SAndrey V. Elsukov {
93804d17814SAndrey V. Elsukov 	if (params->addr_len > 64)
93904d17814SAndrey V. Elsukov 		return (SNMP_ERR_INCONS_VALUE);
94004d17814SAndrey V. Elsukov 
94104d17814SAndrey V. Elsukov 	if (strnlen(params->addr, params->addr_len) !=
94204d17814SAndrey V. Elsukov 	    params->addr_len)
94304d17814SAndrey V. Elsukov 		return (SNMP_ERR_INCONS_VALUE);
94404d17814SAndrey V. Elsukov 
94504d17814SAndrey V. Elsukov 	if ((port->dns_addr = realloc(params->addr,
94604d17814SAndrey V. Elsukov 	    params->addr_len + 1)) == NULL)
94704d17814SAndrey V. Elsukov 		return (SNMP_ERR_GENERR);
94804d17814SAndrey V. Elsukov 
94904d17814SAndrey V. Elsukov 	port->dns_addr[params->addr_len] = '\0';
95004d17814SAndrey V. Elsukov 	params->addr = NULL;
95104d17814SAndrey V. Elsukov 
95204d17814SAndrey V. Elsukov 	port->dns_port = htons(params->port);
95304d17814SAndrey V. Elsukov 
95404d17814SAndrey V. Elsukov 	return (SNMP_ERR_NOERROR);
95504d17814SAndrey V. Elsukov }
95604d17814SAndrey V. Elsukov 
95704d17814SAndrey V. Elsukov /**
95804d17814SAndrey V. Elsukov  * Open sockets. This loops through all the addresses returned by getaddrinfo
95904d17814SAndrey V. Elsukov  * and opens a socket for each of them.
96004d17814SAndrey V. Elsukov  *
96104d17814SAndrey V. Elsukov  * \param port	inet port
96204d17814SAndrey V. Elsukov  *
96304d17814SAndrey V. Elsukov  * \return SNMP error code
96404d17814SAndrey V. Elsukov  */
96504d17814SAndrey V. Elsukov static int
dns_activate(struct inet_port * port)96604d17814SAndrey V. Elsukov dns_activate(struct inet_port *port)
96704d17814SAndrey V. Elsukov {
96804d17814SAndrey V. Elsukov 	struct addrinfo hints;
96904d17814SAndrey V. Elsukov 	memset(&hints, 0, sizeof(hints));
97004d17814SAndrey V. Elsukov 	hints.ai_family = AF_UNSPEC;
97104d17814SAndrey V. Elsukov 	hints.ai_socktype = SOCK_DGRAM;		// XXX udp-only
97204d17814SAndrey V. Elsukov 	hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE | AI_NUMERICSERV;
97304d17814SAndrey V. Elsukov 
97404d17814SAndrey V. Elsukov 	char portbuf[8];
97504d17814SAndrey V. Elsukov 	sprintf(portbuf, "%hu", ntohs(port->dns_port));
97604d17814SAndrey V. Elsukov 
97704d17814SAndrey V. Elsukov 	struct addrinfo *res0;
97804d17814SAndrey V. Elsukov 	int error = getaddrinfo(port->dns_addr[0] == '\0'
97904d17814SAndrey V. Elsukov 	    ? NULL : port->dns_addr,
98004d17814SAndrey V. Elsukov 	    portbuf, &hints, &res0);
98104d17814SAndrey V. Elsukov 
98204d17814SAndrey V. Elsukov 	if (error) {
98304d17814SAndrey V. Elsukov 		syslog(LOG_ERR, "cannot resolve address '%s': %s",
98404d17814SAndrey V. Elsukov 		    port->dns_addr, gai_strerror(error));
98504d17814SAndrey V. Elsukov 		return (SNMP_ERR_GENERR);
98604d17814SAndrey V. Elsukov 	}
98704d17814SAndrey V. Elsukov 
98804d17814SAndrey V. Elsukov 	for (struct addrinfo *res = res0; res != NULL; res = res->ai_next) {
98904d17814SAndrey V. Elsukov 		if (res->ai_family != AF_INET && res->ai_family != AF_INET6)
99004d17814SAndrey V. Elsukov 			continue;
99104d17814SAndrey V. Elsukov 
99204d17814SAndrey V. Elsukov 		struct port_sock *sock = calloc(1, sizeof(struct port_sock));
99304d17814SAndrey V. Elsukov 		if (sock == NULL)
99404d17814SAndrey V. Elsukov 			return (SNMP_ERR_GENERR);
99504d17814SAndrey V. Elsukov 
99604d17814SAndrey V. Elsukov 		snmpd_input_init(&sock->input);
99704d17814SAndrey V. Elsukov 		sock->port = port;
99804d17814SAndrey V. Elsukov 
99904d17814SAndrey V. Elsukov 		int ret = SNMP_ERR_NOERROR;
100004d17814SAndrey V. Elsukov 
100104d17814SAndrey V. Elsukov 		if (res->ai_family == AF_INET) {
100204d17814SAndrey V. Elsukov 			*(struct sockaddr_in *)&sock->bind_addr =
100304d17814SAndrey V. Elsukov 			    *(struct sockaddr_in *)(void *)res->ai_addr;
100404d17814SAndrey V. Elsukov 			ret = ipv4_activate_sock(sock);
100504d17814SAndrey V. Elsukov 		} else {
100604d17814SAndrey V. Elsukov 			*(struct sockaddr_in6 *)&sock->bind_addr =
100704d17814SAndrey V. Elsukov 			    *(struct sockaddr_in6 *)(void *)res->ai_addr;
100804d17814SAndrey V. Elsukov 			ret = ipv6_activate_sock(sock);
100904d17814SAndrey V. Elsukov 		}
101004d17814SAndrey V. Elsukov 
101104d17814SAndrey V. Elsukov 		if (ret != SNMP_ERR_NOERROR)
101204d17814SAndrey V. Elsukov 			free(sock);
101304d17814SAndrey V. Elsukov 		else
101404d17814SAndrey V. Elsukov 			TAILQ_INSERT_HEAD(&port->socks, sock, link);
101504d17814SAndrey V. Elsukov 	}
101604d17814SAndrey V. Elsukov 
101704d17814SAndrey V. Elsukov 	if (!TAILQ_EMPTY(&port->socks))
101804d17814SAndrey V. Elsukov 		port->row_status = RowStatus_active;
101904d17814SAndrey V. Elsukov 
102004d17814SAndrey V. Elsukov 	freeaddrinfo(res0);
102104d17814SAndrey V. Elsukov 	return (SNMP_ERR_GENERR);
102204d17814SAndrey V. Elsukov }
102304d17814SAndrey V. Elsukov 
102404d17814SAndrey V. Elsukov /**
102504d17814SAndrey V. Elsukov  * Deactive the socket. Close all open sockets and delete all sock objects.
102604d17814SAndrey V. Elsukov  *
102704d17814SAndrey V. Elsukov  * \param port	inet port
102804d17814SAndrey V. Elsukov  */
102904d17814SAndrey V. Elsukov static void
dns_deactivate(struct inet_port * port)103004d17814SAndrey V. Elsukov dns_deactivate(struct inet_port *port)
103104d17814SAndrey V. Elsukov {
103204d17814SAndrey V. Elsukov 	while (!TAILQ_EMPTY(&port->socks)) {
103304d17814SAndrey V. Elsukov 		struct port_sock *sock = TAILQ_FIRST(&port->socks);
103404d17814SAndrey V. Elsukov 		TAILQ_REMOVE(&port->socks, sock, link);
103504d17814SAndrey V. Elsukov 		snmpd_input_close(&sock->input);
103604d17814SAndrey V. Elsukov 		free(sock);
103704d17814SAndrey V. Elsukov 	}
103804d17814SAndrey V. Elsukov 	port->row_status = RowStatus_notInService;
103904d17814SAndrey V. Elsukov }
104004d17814SAndrey V. Elsukov 
104104d17814SAndrey V. Elsukov static int
inet_create(struct inet_port_params * params,struct inet_port ** pp)104204d17814SAndrey V. Elsukov inet_create(struct inet_port_params *params, struct inet_port **pp)
104304d17814SAndrey V. Elsukov {
104404d17814SAndrey V. Elsukov 	int err = SNMP_ERR_NOERROR;
104504d17814SAndrey V. Elsukov 	struct inet_port *port = NULL;
104604d17814SAndrey V. Elsukov 
104704d17814SAndrey V. Elsukov 	if (params->port > 0xffff) {
104804d17814SAndrey V. Elsukov 		err = SNMP_ERR_NO_CREATION;
104904d17814SAndrey V. Elsukov 		goto fail;
105004d17814SAndrey V. Elsukov 	}
105104d17814SAndrey V. Elsukov 
105204d17814SAndrey V. Elsukov 	if ((port = malloc(sizeof(*port))) == NULL) {
105304d17814SAndrey V. Elsukov 		err =  SNMP_ERR_GENERR;
105404d17814SAndrey V. Elsukov 		goto fail;
105504d17814SAndrey V. Elsukov 	}
105604d17814SAndrey V. Elsukov 	memset(port, 0, sizeof(*port));
105704d17814SAndrey V. Elsukov 	TAILQ_INIT(&port->socks);
105804d17814SAndrey V. Elsukov 
105904d17814SAndrey V. Elsukov 	port->proto = params->proto;
106004d17814SAndrey V. Elsukov 	port->tport.index = params->index;
106104d17814SAndrey V. Elsukov 
106204d17814SAndrey V. Elsukov 	switch (params->type) {
106304d17814SAndrey V. Elsukov 
106404d17814SAndrey V. Elsukov 	  case InetAddressType_ipv4:
106504d17814SAndrey V. Elsukov 		port->create = ipv4_create;
106604d17814SAndrey V. Elsukov 		port->activate = ipv4_activate;
106704d17814SAndrey V. Elsukov 		port->deactivate = ipv4_deactivate;
106804d17814SAndrey V. Elsukov 		break;
106904d17814SAndrey V. Elsukov 
107004d17814SAndrey V. Elsukov 	  case InetAddressType_ipv6:
107104d17814SAndrey V. Elsukov 		port->create = ipv6_create;
107204d17814SAndrey V. Elsukov 		port->activate = ipv6_activate;
107304d17814SAndrey V. Elsukov 		port->deactivate = ipv6_deactivate;
107404d17814SAndrey V. Elsukov 		break;
107504d17814SAndrey V. Elsukov 
107604d17814SAndrey V. Elsukov 	  case InetAddressType_ipv6z:
107704d17814SAndrey V. Elsukov 		port->create = ipv6z_create;
107804d17814SAndrey V. Elsukov 		port->activate = ipv6_activate;
107904d17814SAndrey V. Elsukov 		port->deactivate = ipv6_deactivate;
108004d17814SAndrey V. Elsukov 		break;
108104d17814SAndrey V. Elsukov 
108204d17814SAndrey V. Elsukov 	  case InetAddressType_dns:
108304d17814SAndrey V. Elsukov 		port->create = dns_create;
108404d17814SAndrey V. Elsukov 		port->activate = dns_activate;
108504d17814SAndrey V. Elsukov 		port->deactivate = dns_deactivate;
108604d17814SAndrey V. Elsukov 		break;
108704d17814SAndrey V. Elsukov 
108804d17814SAndrey V. Elsukov 	  default:
108904d17814SAndrey V. Elsukov 		err = SNMP_ERR_NO_CREATION;
109004d17814SAndrey V. Elsukov 		goto fail;
109104d17814SAndrey V. Elsukov 	}
109204d17814SAndrey V. Elsukov 
109304d17814SAndrey V. Elsukov 	if ((err = port->create(port, params)) != SNMP_ERR_NOERROR)
109404d17814SAndrey V. Elsukov 		goto fail;
109504d17814SAndrey V. Elsukov 
109604d17814SAndrey V. Elsukov 	*pp = port;
109704d17814SAndrey V. Elsukov 	trans_insert_port(my_trans, &port->tport);
109804d17814SAndrey V. Elsukov 	return (err);
109904d17814SAndrey V. Elsukov 
110004d17814SAndrey V. Elsukov fail:
110104d17814SAndrey V. Elsukov 	free(port->dns_addr);
110204d17814SAndrey V. Elsukov 	free(port);
110304d17814SAndrey V. Elsukov 	return (err);
110404d17814SAndrey V. Elsukov }
110504d17814SAndrey V. Elsukov 
110604d17814SAndrey V. Elsukov static int
create_and_go(struct snmp_context * ctx,struct inet_port_params * params)110704d17814SAndrey V. Elsukov create_and_go(struct snmp_context *ctx, struct inet_port_params *params)
110804d17814SAndrey V. Elsukov {
110904d17814SAndrey V. Elsukov 	int err;
111004d17814SAndrey V. Elsukov 	struct inet_port *port;
111104d17814SAndrey V. Elsukov 
111204d17814SAndrey V. Elsukov 	if ((err = inet_create(params, &port)) != SNMP_ERR_NOERROR)
111304d17814SAndrey V. Elsukov 		return (err);
111404d17814SAndrey V. Elsukov 
111504d17814SAndrey V. Elsukov 	port->row_status = RowStatus_createAndGo;
111604d17814SAndrey V. Elsukov 	ctx->scratch->ptr1 = port;
111704d17814SAndrey V. Elsukov 
111804d17814SAndrey V. Elsukov 	if (community == COMM_INITIALIZE)
111904d17814SAndrey V. Elsukov 		return (err);
112004d17814SAndrey V. Elsukov 
112104d17814SAndrey V. Elsukov 	return (inet_activate(&port->tport));
112204d17814SAndrey V. Elsukov }
112304d17814SAndrey V. Elsukov 
112404d17814SAndrey V. Elsukov static int
create_and_wait(struct snmp_context * ctx,struct inet_port_params * params)112504d17814SAndrey V. Elsukov create_and_wait(struct snmp_context *ctx, struct inet_port_params *params)
112604d17814SAndrey V. Elsukov {
112704d17814SAndrey V. Elsukov 	int err;
112804d17814SAndrey V. Elsukov 	struct inet_port *port;
112904d17814SAndrey V. Elsukov 
113004d17814SAndrey V. Elsukov 	if ((err = inet_create(params, &port)) != SNMP_ERR_NOERROR)
113104d17814SAndrey V. Elsukov 		return (err);
113204d17814SAndrey V. Elsukov 
113304d17814SAndrey V. Elsukov 	port->row_status = RowStatus_createAndWait;
113404d17814SAndrey V. Elsukov 	ctx->scratch->ptr1 = port;
113504d17814SAndrey V. Elsukov 
113604d17814SAndrey V. Elsukov 	return (err);
113704d17814SAndrey V. Elsukov }
113804d17814SAndrey V. Elsukov 
113904d17814SAndrey V. Elsukov /**
114004d17814SAndrey V. Elsukov  * This is called to set a RowStatus value in the port table during
114104d17814SAndrey V. Elsukov  * SET processing.
114204d17814SAndrey V. Elsukov  *
114304d17814SAndrey V. Elsukov  * When this is called during initialization time and the RowStatus is set
114404d17814SAndrey V. Elsukov  * to CreateAndGo, the port is actually not activated. This is done when
114504d17814SAndrey V. Elsukov  * the main code calls the init() for all ports later.
114604d17814SAndrey V. Elsukov  */
114704d17814SAndrey V. Elsukov static int
inet_port_set(struct snmp_context * ctx,struct inet_port * port,struct inet_port_params * params,enum RowStatus nstatus)114804d17814SAndrey V. Elsukov inet_port_set(struct snmp_context *ctx, struct inet_port *port,
114904d17814SAndrey V. Elsukov     struct inet_port_params *params, enum RowStatus nstatus)
115004d17814SAndrey V. Elsukov {
115104d17814SAndrey V. Elsukov 	switch (nstatus) {
115204d17814SAndrey V. Elsukov 
115304d17814SAndrey V. Elsukov 	  case RowStatus_createAndGo:
115404d17814SAndrey V. Elsukov 		if (port != NULL)
115504d17814SAndrey V. Elsukov 			return (SNMP_ERR_INCONS_VALUE);
115604d17814SAndrey V. Elsukov 		ctx->scratch->int1 = SET_CREATED;
115704d17814SAndrey V. Elsukov 		return (create_and_go(ctx, params));
115804d17814SAndrey V. Elsukov 
115904d17814SAndrey V. Elsukov 	  case RowStatus_createAndWait:
116004d17814SAndrey V. Elsukov 		if (port != NULL)
116104d17814SAndrey V. Elsukov 			return (SNMP_ERR_INCONS_VALUE);
116204d17814SAndrey V. Elsukov 		ctx->scratch->int1 = SET_CREATED;
116304d17814SAndrey V. Elsukov 		return (create_and_wait(ctx, params));
116404d17814SAndrey V. Elsukov 
116504d17814SAndrey V. Elsukov 	  case RowStatus_active:
116604d17814SAndrey V. Elsukov 		if (port == NULL)
116704d17814SAndrey V. Elsukov 			return (SNMP_ERR_INCONS_VALUE);
116804d17814SAndrey V. Elsukov 
116904d17814SAndrey V. Elsukov 		switch (port->row_status) {
117004d17814SAndrey V. Elsukov 
117104d17814SAndrey V. Elsukov 		  case RowStatus_notReady:
117204d17814SAndrey V. Elsukov 			/* this can not happend */
117304d17814SAndrey V. Elsukov 			abort();
117404d17814SAndrey V. Elsukov 
117504d17814SAndrey V. Elsukov 		  case RowStatus_notInService:
117604d17814SAndrey V. Elsukov 			ctx->scratch->int1 = SET_ACTIVATED;
117704d17814SAndrey V. Elsukov 			return (inet_activate(&port->tport));
117804d17814SAndrey V. Elsukov 
117904d17814SAndrey V. Elsukov 		  case RowStatus_active:
118004d17814SAndrey V. Elsukov 			return (SNMP_ERR_NOERROR);
118104d17814SAndrey V. Elsukov 
118204d17814SAndrey V. Elsukov 		  case RowStatus_createAndGo:
118304d17814SAndrey V. Elsukov 		  case RowStatus_createAndWait:
118404d17814SAndrey V. Elsukov 		  case RowStatus_destroy:
118504d17814SAndrey V. Elsukov 			abort();
118604d17814SAndrey V. Elsukov 		}
118704d17814SAndrey V. Elsukov 		break;
118804d17814SAndrey V. Elsukov 
118904d17814SAndrey V. Elsukov 	  case RowStatus_notInService:
119004d17814SAndrey V. Elsukov 		if (port == NULL)
119104d17814SAndrey V. Elsukov 			return (SNMP_ERR_INCONS_VALUE);
119204d17814SAndrey V. Elsukov 
119304d17814SAndrey V. Elsukov 		switch (port->row_status) {
119404d17814SAndrey V. Elsukov 
119504d17814SAndrey V. Elsukov 		  case RowStatus_notReady:
119604d17814SAndrey V. Elsukov 			/* this can not happend */
119704d17814SAndrey V. Elsukov 			abort();
119804d17814SAndrey V. Elsukov 
119904d17814SAndrey V. Elsukov 		  case RowStatus_notInService:
120004d17814SAndrey V. Elsukov 			return (SNMP_ERR_NOERROR);
120104d17814SAndrey V. Elsukov 
120204d17814SAndrey V. Elsukov 		  case RowStatus_active:
120304d17814SAndrey V. Elsukov 			/* this is done during commit */
120404d17814SAndrey V. Elsukov 			ctx->scratch->int1 = SET_DEACTIVATE;
120504d17814SAndrey V. Elsukov 			return (SNMP_ERR_NOERROR);
120604d17814SAndrey V. Elsukov 
120704d17814SAndrey V. Elsukov 		  case RowStatus_createAndGo:
120804d17814SAndrey V. Elsukov 		  case RowStatus_createAndWait:
120904d17814SAndrey V. Elsukov 		  case RowStatus_destroy:
121004d17814SAndrey V. Elsukov 			abort();
121104d17814SAndrey V. Elsukov 		}
121204d17814SAndrey V. Elsukov 		break;
121304d17814SAndrey V. Elsukov 
121404d17814SAndrey V. Elsukov 	  case RowStatus_destroy:
121504d17814SAndrey V. Elsukov 		/* this is done during commit */
121604d17814SAndrey V. Elsukov 		ctx->scratch->int1 = SET_DESTROY;
121704d17814SAndrey V. Elsukov 		return (SNMP_ERR_NOERROR);
121804d17814SAndrey V. Elsukov 
121904d17814SAndrey V. Elsukov 	  case RowStatus_notReady:
122004d17814SAndrey V. Elsukov 		return (SNMP_ERR_WRONG_VALUE);
122104d17814SAndrey V. Elsukov 	}
122204d17814SAndrey V. Elsukov 	abort();
122304d17814SAndrey V. Elsukov }
122404d17814SAndrey V. Elsukov 
122504d17814SAndrey V. Elsukov /*
122604d17814SAndrey V. Elsukov  * Port table
122704d17814SAndrey V. Elsukov  */
122804d17814SAndrey V. Elsukov int
op_snmp_trans_inet(struct snmp_context * ctx,struct snmp_value * value,u_int sub,u_int iidx __unused,enum snmp_op op)122904d17814SAndrey V. Elsukov op_snmp_trans_inet(struct snmp_context *ctx, struct snmp_value *value,
123004d17814SAndrey V. Elsukov     u_int sub, u_int iidx __unused, enum snmp_op op)
123104d17814SAndrey V. Elsukov {
123204d17814SAndrey V. Elsukov 	asn_subid_t which = value->var.subs[sub - 1];
123304d17814SAndrey V. Elsukov 	struct inet_port *port;
123404d17814SAndrey V. Elsukov 	struct inet_port_params params;
123504d17814SAndrey V. Elsukov 	int ret;
123604d17814SAndrey V. Elsukov 
123704d17814SAndrey V. Elsukov 	switch (op) {
123804d17814SAndrey V. Elsukov 
123904d17814SAndrey V. Elsukov 	  case SNMP_OP_GETNEXT:
124004d17814SAndrey V. Elsukov 		if ((port = (struct inet_port *)trans_next_port(my_trans,
124104d17814SAndrey V. Elsukov 		    &value->var, sub)) == NULL)
124204d17814SAndrey V. Elsukov 			return (SNMP_ERR_NOSUCHNAME);
124304d17814SAndrey V. Elsukov 		index_append(&value->var, sub, &port->tport.index);
124404d17814SAndrey V. Elsukov 		goto fetch;
124504d17814SAndrey V. Elsukov 
124604d17814SAndrey V. Elsukov 	  case SNMP_OP_GET:
124704d17814SAndrey V. Elsukov 		if ((port = (struct inet_port *)trans_find_port(my_trans,
124804d17814SAndrey V. Elsukov 		    &value->var, sub)) == NULL)
124904d17814SAndrey V. Elsukov 			return (SNMP_ERR_NOSUCHNAME);
125004d17814SAndrey V. Elsukov 		goto fetch;
125104d17814SAndrey V. Elsukov 
125204d17814SAndrey V. Elsukov 	  case SNMP_OP_SET:
125304d17814SAndrey V. Elsukov 		port = (struct inet_port *)trans_find_port(my_trans,
125404d17814SAndrey V. Elsukov 		    &value->var, sub);
125504d17814SAndrey V. Elsukov 
125604d17814SAndrey V. Elsukov 		if (which != LEAF_begemotSnmpdTransInetStatus)
125704d17814SAndrey V. Elsukov 			abort();
125804d17814SAndrey V. Elsukov 		if (!isok_RowStatus(value->v.integer))
125904d17814SAndrey V. Elsukov 			return (SNMP_ERR_WRONG_VALUE);
126004d17814SAndrey V. Elsukov 
126104d17814SAndrey V. Elsukov 		if (index_decode(&value->var, sub, iidx, &params.type,
126204d17814SAndrey V. Elsukov 		    &params.addr, &params.addr_len, &params.port,
126304d17814SAndrey V. Elsukov 		    &params.proto))
126404d17814SAndrey V. Elsukov 			return (SNMP_ERR_NO_CREATION);
126504d17814SAndrey V. Elsukov 
126604d17814SAndrey V. Elsukov 		asn_slice_oid(&params.index, &value->var, sub, value->var.len);
126704d17814SAndrey V. Elsukov 
126804d17814SAndrey V. Elsukov 		ret = inet_port_set(ctx, port, &params,
126904d17814SAndrey V. Elsukov 		    (enum RowStatus)value->v.integer);
127004d17814SAndrey V. Elsukov 
127104d17814SAndrey V. Elsukov 		free(params.addr);
127204d17814SAndrey V. Elsukov 		return (ret);
127304d17814SAndrey V. Elsukov 
127404d17814SAndrey V. Elsukov 	  case SNMP_OP_ROLLBACK:
127504d17814SAndrey V. Elsukov 		if ((port = (struct inet_port *)trans_find_port(my_trans,
127604d17814SAndrey V. Elsukov 		    &value->var, sub)) == NULL)
127704d17814SAndrey V. Elsukov 			/* cannot happen */
127804d17814SAndrey V. Elsukov 			abort();
127904d17814SAndrey V. Elsukov 
128004d17814SAndrey V. Elsukov 		switch (ctx->scratch->int1) {
128104d17814SAndrey V. Elsukov 
128204d17814SAndrey V. Elsukov 		  case SET_CREATED:
128304d17814SAndrey V. Elsukov 			/* newly created */
128404d17814SAndrey V. Elsukov 			assert(port != NULL);
128504d17814SAndrey V. Elsukov 			inet_destroy_port(&port->tport);
128604d17814SAndrey V. Elsukov 			return (SNMP_ERR_NOERROR);
128704d17814SAndrey V. Elsukov 
128804d17814SAndrey V. Elsukov 		  case SET_DESTROY:
128904d17814SAndrey V. Elsukov 			/* do it now */
129004d17814SAndrey V. Elsukov 			assert(port != NULL);
129104d17814SAndrey V. Elsukov 			return (SNMP_ERR_NOERROR);
129204d17814SAndrey V. Elsukov 
129304d17814SAndrey V. Elsukov 		  case SET_ACTIVATED:
129404d17814SAndrey V. Elsukov 			deactivate_port(port);
129504d17814SAndrey V. Elsukov 			return (SNMP_ERR_NOERROR);
129604d17814SAndrey V. Elsukov 
129704d17814SAndrey V. Elsukov 		  case SET_DEACTIVATE:
129804d17814SAndrey V. Elsukov 			return (SNMP_ERR_NOERROR);
129904d17814SAndrey V. Elsukov 		}
130004d17814SAndrey V. Elsukov 		abort();
130104d17814SAndrey V. Elsukov 
130204d17814SAndrey V. Elsukov 	  case SNMP_OP_COMMIT:
130304d17814SAndrey V. Elsukov 		if ((port = (struct inet_port *)trans_find_port(my_trans,
130404d17814SAndrey V. Elsukov 		    &value->var, sub)) == NULL)
130504d17814SAndrey V. Elsukov 			/* cannot happen */
130604d17814SAndrey V. Elsukov 			abort();
130704d17814SAndrey V. Elsukov 
130804d17814SAndrey V. Elsukov 		switch (ctx->scratch->int1) {
130904d17814SAndrey V. Elsukov 
131004d17814SAndrey V. Elsukov 		  case SET_CREATED:
131104d17814SAndrey V. Elsukov 			/* newly created */
131204d17814SAndrey V. Elsukov 			assert(port != NULL);
131304d17814SAndrey V. Elsukov 			return (SNMP_ERR_NOERROR);
131404d17814SAndrey V. Elsukov 
131504d17814SAndrey V. Elsukov 		  case SET_DESTROY:
131604d17814SAndrey V. Elsukov 			/* do it now */
131704d17814SAndrey V. Elsukov 			assert(port != NULL);
131804d17814SAndrey V. Elsukov 			inet_destroy_port(&port->tport);
131904d17814SAndrey V. Elsukov 			return (SNMP_ERR_NOERROR);
132004d17814SAndrey V. Elsukov 
132104d17814SAndrey V. Elsukov 		  case SET_ACTIVATED:
132204d17814SAndrey V. Elsukov 			return (SNMP_ERR_NOERROR);
132304d17814SAndrey V. Elsukov 
132404d17814SAndrey V. Elsukov 		  case SET_DEACTIVATE:
132504d17814SAndrey V. Elsukov 			deactivate_port(port);
132604d17814SAndrey V. Elsukov 			return (SNMP_ERR_NOERROR);
132704d17814SAndrey V. Elsukov 		}
132804d17814SAndrey V. Elsukov 		abort();
132904d17814SAndrey V. Elsukov 	}
133004d17814SAndrey V. Elsukov 	abort();
133104d17814SAndrey V. Elsukov 
133204d17814SAndrey V. Elsukov   fetch:
133304d17814SAndrey V. Elsukov 	switch (which) {
133404d17814SAndrey V. Elsukov 
133504d17814SAndrey V. Elsukov 	  case LEAF_begemotSnmpdTransInetStatus:
133604d17814SAndrey V. Elsukov 		value->v.integer = port->row_status;
133704d17814SAndrey V. Elsukov 		break;
133804d17814SAndrey V. Elsukov 
133904d17814SAndrey V. Elsukov 	  default:
134004d17814SAndrey V. Elsukov 		abort();
134104d17814SAndrey V. Elsukov 	}
134204d17814SAndrey V. Elsukov 
134304d17814SAndrey V. Elsukov 	return (SNMP_ERR_NOERROR);
134404d17814SAndrey V. Elsukov }
1345