xref: /minix3/minix/net/lwip/rtsock.c (revision ef8d499e2d2af900e9b2ab297171d7b088652482)
1*ef8d499eSDavid van Moolenbroek /* LWIP service - rtsock.c - routing sockets and route sysctl support */
2*ef8d499eSDavid van Moolenbroek /*
3*ef8d499eSDavid van Moolenbroek  * In a nutshell, the intended abstraction is that only this module deals with
4*ef8d499eSDavid van Moolenbroek  * route messages, message headers, and RTA arrays, whereas other modules
5*ef8d499eSDavid van Moolenbroek  * (ifaddr, route) are responsible for parsing and providing sockaddr_* type
6*ef8d499eSDavid van Moolenbroek  * addresses, with the exception of compression and expansion which is
7*ef8d499eSDavid van Moolenbroek  * particular to routing sockets.  Concretely, there should be no reference to
8*ef8d499eSDavid van Moolenbroek  * (e.g.) rt_msghdr outside this module, and no mention of ip_addr_t inside it.
9*ef8d499eSDavid van Moolenbroek  */
10*ef8d499eSDavid van Moolenbroek 
11*ef8d499eSDavid van Moolenbroek #include "lwip.h"
12*ef8d499eSDavid van Moolenbroek #include "ifaddr.h"
13*ef8d499eSDavid van Moolenbroek #include "rtsock.h"
14*ef8d499eSDavid van Moolenbroek #include "route.h"
15*ef8d499eSDavid van Moolenbroek #include "lldata.h"
16*ef8d499eSDavid van Moolenbroek 
17*ef8d499eSDavid van Moolenbroek /* The number of routing sockets. */
18*ef8d499eSDavid van Moolenbroek #define NR_RTSOCK		8
19*ef8d499eSDavid van Moolenbroek 
20*ef8d499eSDavid van Moolenbroek /*
21*ef8d499eSDavid van Moolenbroek  * The send buffer maximum determines the maximum size of requests.  The
22*ef8d499eSDavid van Moolenbroek  * maximum possible request size is the size of the routing message header plus
23*ef8d499eSDavid van Moolenbroek  * RTAX_MAX times the maximum socket address size, including alignment.  That
24*ef8d499eSDavid van Moolenbroek  * currently works out to a number in the low 400s, so 512 should be fine for
25*ef8d499eSDavid van Moolenbroek  * now.  At this time we do not support changing the send buffer size, because
26*ef8d499eSDavid van Moolenbroek  * there really is no point in doing so.  Hence also no RT_SNDBUF_{MIN,DEF}.
27*ef8d499eSDavid van Moolenbroek  */
28*ef8d499eSDavid van Moolenbroek #define RT_SNDBUF_MAX		512	/* maximum RT send buffer size */
29*ef8d499eSDavid van Moolenbroek 
30*ef8d499eSDavid van Moolenbroek #define RT_RCVBUF_MIN		0	/* minimum RT receive buffer size */
31*ef8d499eSDavid van Moolenbroek #define RT_RCVBUF_DEF		16384	/* default RT receive buffer size */
32*ef8d499eSDavid van Moolenbroek #define RT_RCVBUF_MAX		65536	/* maximum RT receive buffer size */
33*ef8d499eSDavid van Moolenbroek 
34*ef8d499eSDavid van Moolenbroek /* Address length of routing socket address structures; two bytes only. */
35*ef8d499eSDavid van Moolenbroek #define RTSOCK_ADDR_LEN		offsetof(struct sockaddr, sa_data)
36*ef8d499eSDavid van Moolenbroek 
37*ef8d499eSDavid van Moolenbroek struct rtsock_rta {
38*ef8d499eSDavid van Moolenbroek 	const void *rta_ptr[RTAX_MAX];
39*ef8d499eSDavid van Moolenbroek 	socklen_t rta_len[RTAX_MAX];
40*ef8d499eSDavid van Moolenbroek };
41*ef8d499eSDavid van Moolenbroek 
42*ef8d499eSDavid van Moolenbroek static const char rtsock_padbuf[RT_ROUNDUP(0)];
43*ef8d499eSDavid van Moolenbroek 
44*ef8d499eSDavid van Moolenbroek static struct rtsock {
45*ef8d499eSDavid van Moolenbroek 	struct sock rt_sock;		/* socket object, MUST be first */
46*ef8d499eSDavid van Moolenbroek 	int rt_family;			/* address family filter if not zero */
47*ef8d499eSDavid van Moolenbroek 	unsigned int rt_flags;		/* routing socket flags (RTF_) */
48*ef8d499eSDavid van Moolenbroek 	struct pbuf *rt_rcvhead;	/* receive buffer, first packet */
49*ef8d499eSDavid van Moolenbroek 	struct pbuf **rt_rcvtailp;	/* receive buffer, last ptr-ptr */
50*ef8d499eSDavid van Moolenbroek 	size_t rt_rcvlen;		/* receive buffer, length in bytes */
51*ef8d499eSDavid van Moolenbroek 	size_t rt_rcvbuf;		/* receive buffer, maximum size */
52*ef8d499eSDavid van Moolenbroek 	TAILQ_ENTRY(rtsock) rt_next;	/* next in active or free list */
53*ef8d499eSDavid van Moolenbroek } rt_array[NR_RTSOCK];
54*ef8d499eSDavid van Moolenbroek 
55*ef8d499eSDavid van Moolenbroek #define RTF_NOLOOPBACK		0x1	/* suppress reply messages */
56*ef8d499eSDavid van Moolenbroek 
57*ef8d499eSDavid van Moolenbroek static TAILQ_HEAD(, rtsock) rt_freelist;	/* free routing sockets */
58*ef8d499eSDavid van Moolenbroek static TAILQ_HEAD(, rtsock) rt_activelist;	/* active routing sockets */
59*ef8d499eSDavid van Moolenbroek 
60*ef8d499eSDavid van Moolenbroek struct rtsock_request {
61*ef8d499eSDavid van Moolenbroek 	struct rtsock *rtr_src;		/* source socket of the request */
62*ef8d499eSDavid van Moolenbroek 	pid_t rtr_pid;			/* process ID of requesting process */
63*ef8d499eSDavid van Moolenbroek 	int rtr_seq;			/* sequence number from the request */
64*ef8d499eSDavid van Moolenbroek 	int rtr_getif;			/* RTM_GET only: get interface info */
65*ef8d499eSDavid van Moolenbroek };
66*ef8d499eSDavid van Moolenbroek 
67*ef8d499eSDavid van Moolenbroek static const struct sockevent_ops rtsock_ops;
68*ef8d499eSDavid van Moolenbroek 
69*ef8d499eSDavid van Moolenbroek static ssize_t rtsock_info(struct rmib_call *, struct rmib_node *,
70*ef8d499eSDavid van Moolenbroek 	struct rmib_oldp *, struct rmib_newp *);
71*ef8d499eSDavid van Moolenbroek 
72*ef8d499eSDavid van Moolenbroek /* The CTL_NET PF_ROUTE subtree. */
73*ef8d499eSDavid van Moolenbroek static struct rmib_node net_route_table[] = {
74*ef8d499eSDavid van Moolenbroek 	[0]	= RMIB_FUNC(RMIB_RO | CTLTYPE_NODE, 0, rtsock_info,
75*ef8d499eSDavid van Moolenbroek 		    "rtable", "Routing table information"),
76*ef8d499eSDavid van Moolenbroek };
77*ef8d499eSDavid van Moolenbroek 
78*ef8d499eSDavid van Moolenbroek /* The CTL_NET PF_ROUTE node. */
79*ef8d499eSDavid van Moolenbroek static struct rmib_node net_route_node =
80*ef8d499eSDavid van Moolenbroek     RMIB_NODE(RMIB_RO, net_route_table, "route", "PF_ROUTE information");
81*ef8d499eSDavid van Moolenbroek 
82*ef8d499eSDavid van Moolenbroek /*
83*ef8d499eSDavid van Moolenbroek  * Initialize the routing sockets module.
84*ef8d499eSDavid van Moolenbroek  */
85*ef8d499eSDavid van Moolenbroek void
rtsock_init(void)86*ef8d499eSDavid van Moolenbroek rtsock_init(void)
87*ef8d499eSDavid van Moolenbroek {
88*ef8d499eSDavid van Moolenbroek 	const int mib[] = { CTL_NET, PF_ROUTE };
89*ef8d499eSDavid van Moolenbroek 	unsigned int slot;
90*ef8d499eSDavid van Moolenbroek 	int r;
91*ef8d499eSDavid van Moolenbroek 
92*ef8d499eSDavid van Moolenbroek 	/* Initialize the list of free routing sockets. */
93*ef8d499eSDavid van Moolenbroek 	TAILQ_INIT(&rt_freelist);
94*ef8d499eSDavid van Moolenbroek 
95*ef8d499eSDavid van Moolenbroek 	for (slot = 0; slot < __arraycount(rt_array); slot++)
96*ef8d499eSDavid van Moolenbroek 		TAILQ_INSERT_TAIL(&rt_freelist, &rt_array[slot], rt_next);
97*ef8d499eSDavid van Moolenbroek 
98*ef8d499eSDavid van Moolenbroek 	/* Initialize the list of acive routing sockets. */
99*ef8d499eSDavid van Moolenbroek 	TAILQ_INIT(&rt_activelist);
100*ef8d499eSDavid van Moolenbroek 
101*ef8d499eSDavid van Moolenbroek 	/* Register the "net.route" subtree with the MIB service. */
102*ef8d499eSDavid van Moolenbroek 	if ((r = rmib_register(mib, __arraycount(mib), &net_route_node)) != OK)
103*ef8d499eSDavid van Moolenbroek 		panic("unable to register net.route RMIB tree: %d", r);
104*ef8d499eSDavid van Moolenbroek }
105*ef8d499eSDavid van Moolenbroek 
106*ef8d499eSDavid van Moolenbroek /*
107*ef8d499eSDavid van Moolenbroek  * Allocate a pbuf suitable for storing a routing message of 'size' bytes.
108*ef8d499eSDavid van Moolenbroek  * Return the allocated pbuf on success, or NULL on memory allocation failure.
109*ef8d499eSDavid van Moolenbroek  */
110*ef8d499eSDavid van Moolenbroek static struct pbuf *
rtsock_alloc(size_t size)111*ef8d499eSDavid van Moolenbroek rtsock_alloc(size_t size)
112*ef8d499eSDavid van Moolenbroek {
113*ef8d499eSDavid van Moolenbroek 	struct pbuf *pbuf;
114*ef8d499eSDavid van Moolenbroek 
115*ef8d499eSDavid van Moolenbroek 	/*
116*ef8d499eSDavid van Moolenbroek 	 * The data will currently always fit in a single pool buffer.  Just in
117*ef8d499eSDavid van Moolenbroek 	 * case this changes in the future, warn and fail cleanly.  The rest of
118*ef8d499eSDavid van Moolenbroek 	 * the code is not able to deal with buffer chains as it is, although
119*ef8d499eSDavid van Moolenbroek 	 * that can be changed if necessary.
120*ef8d499eSDavid van Moolenbroek 	 */
121*ef8d499eSDavid van Moolenbroek 	if (size > MEMPOOL_BUFSIZE) {
122*ef8d499eSDavid van Moolenbroek 		printf("LWIP: routing socket packet too large (%zu)\n", size);
123*ef8d499eSDavid van Moolenbroek 
124*ef8d499eSDavid van Moolenbroek 		return NULL;
125*ef8d499eSDavid van Moolenbroek 	}
126*ef8d499eSDavid van Moolenbroek 
127*ef8d499eSDavid van Moolenbroek 	pbuf = pbuf_alloc(PBUF_RAW, size, PBUF_RAM);
128*ef8d499eSDavid van Moolenbroek 
129*ef8d499eSDavid van Moolenbroek 	assert(pbuf == NULL || pbuf->tot_len == pbuf->len);
130*ef8d499eSDavid van Moolenbroek 
131*ef8d499eSDavid van Moolenbroek 	return pbuf;
132*ef8d499eSDavid van Moolenbroek }
133*ef8d499eSDavid van Moolenbroek 
134*ef8d499eSDavid van Moolenbroek /*
135*ef8d499eSDavid van Moolenbroek  * Initialize a routing addresses map.
136*ef8d499eSDavid van Moolenbroek  */
137*ef8d499eSDavid van Moolenbroek static void
rtsock_rta_init(struct rtsock_rta * rta)138*ef8d499eSDavid van Moolenbroek rtsock_rta_init(struct rtsock_rta * rta)
139*ef8d499eSDavid van Moolenbroek {
140*ef8d499eSDavid van Moolenbroek 
141*ef8d499eSDavid van Moolenbroek 	memset(rta, 0, sizeof(*rta));
142*ef8d499eSDavid van Moolenbroek }
143*ef8d499eSDavid van Moolenbroek 
144*ef8d499eSDavid van Moolenbroek /*
145*ef8d499eSDavid van Moolenbroek  * Set an entry in a routing addresses map.  When computing sizes, 'ptr' may be
146*ef8d499eSDavid van Moolenbroek  * NULL.
147*ef8d499eSDavid van Moolenbroek  */
148*ef8d499eSDavid van Moolenbroek static void
rtsock_rta_set(struct rtsock_rta * rta,unsigned int rtax,const void * ptr,socklen_t len)149*ef8d499eSDavid van Moolenbroek rtsock_rta_set(struct rtsock_rta * rta, unsigned int rtax, const void * ptr,
150*ef8d499eSDavid van Moolenbroek 	socklen_t len)
151*ef8d499eSDavid van Moolenbroek {
152*ef8d499eSDavid van Moolenbroek 
153*ef8d499eSDavid van Moolenbroek 	assert(rtax < RTAX_MAX);
154*ef8d499eSDavid van Moolenbroek 
155*ef8d499eSDavid van Moolenbroek 	rta->rta_ptr[rtax] = ptr;
156*ef8d499eSDavid van Moolenbroek 	rta->rta_len[rtax] = len;
157*ef8d499eSDavid van Moolenbroek }
158*ef8d499eSDavid van Moolenbroek 
159*ef8d499eSDavid van Moolenbroek /*
160*ef8d499eSDavid van Moolenbroek  * Copy out a message with a header and any entries in a routing addresses map,
161*ef8d499eSDavid van Moolenbroek  * either into a pbuf allocated for this purpose, or to a RMIB (sysctl) caller,
162*ef8d499eSDavid van Moolenbroek  * at the given offset.  If no destination is given ('pbuf ' and 'oldp' are
163*ef8d499eSDavid van Moolenbroek  * both NULL), compute just the size of the resulting data.  Otherwise, set the
164*ef8d499eSDavid van Moolenbroek  * length and address mask fields in the header as a side effect.  Return the
165*ef8d499eSDavid van Moolenbroek  * number of bytes copied on success, and if 'pbuf' is not NULL, it is filled
166*ef8d499eSDavid van Moolenbroek  * with a pointer to the newly allocated pbuf.  Return a negative error code on
167*ef8d499eSDavid van Moolenbroek  * failure.  Note that when computing the size only, any actual data pointers
168*ef8d499eSDavid van Moolenbroek  * ('hdr', 'msglen', 'addrs', and the pointers in 'rta') may be NULL or even
169*ef8d499eSDavid van Moolenbroek  * invalid, even though the corresponding sizes should still be supplied.
170*ef8d499eSDavid van Moolenbroek  */
171*ef8d499eSDavid van Moolenbroek static ssize_t
rtsock_rta_finalize(void * hdr,size_t hdrlen,u_short * msglen,int * addrs,const struct rtsock_rta * rta,struct pbuf ** pbuf,struct rmib_oldp * oldp,ssize_t off)172*ef8d499eSDavid van Moolenbroek rtsock_rta_finalize(void * hdr, size_t hdrlen, u_short * msglen, int * addrs,
173*ef8d499eSDavid van Moolenbroek 	const struct rtsock_rta * rta, struct pbuf ** pbuf,
174*ef8d499eSDavid van Moolenbroek 	struct rmib_oldp * oldp, ssize_t off)
175*ef8d499eSDavid van Moolenbroek {
176*ef8d499eSDavid van Moolenbroek 	iovec_t iov[1 + RTAX_MAX * 2];
177*ef8d499eSDavid van Moolenbroek 	size_t len, padlen, totallen;
178*ef8d499eSDavid van Moolenbroek 	unsigned int i, iovcnt;
179*ef8d499eSDavid van Moolenbroek 	int mask;
180*ef8d499eSDavid van Moolenbroek 
181*ef8d499eSDavid van Moolenbroek 	assert(pbuf == NULL || oldp == NULL);
182*ef8d499eSDavid van Moolenbroek 	assert(pbuf == NULL || off == 0);
183*ef8d499eSDavid van Moolenbroek 	assert(RT_ROUNDUP(hdrlen) == hdrlen);
184*ef8d499eSDavid van Moolenbroek 
185*ef8d499eSDavid van Moolenbroek 	iov[0].iov_addr = (vir_bytes)hdr;
186*ef8d499eSDavid van Moolenbroek 	iov[0].iov_size = hdrlen;
187*ef8d499eSDavid van Moolenbroek 	iovcnt = 1;
188*ef8d499eSDavid van Moolenbroek 
189*ef8d499eSDavid van Moolenbroek 	totallen = hdrlen;
190*ef8d499eSDavid van Moolenbroek 	mask = 0;
191*ef8d499eSDavid van Moolenbroek 
192*ef8d499eSDavid van Moolenbroek 	/*
193*ef8d499eSDavid van Moolenbroek 	 * The addresses in the given RTA map, as present, should be stored in
194*ef8d499eSDavid van Moolenbroek 	 * the numbering order of the map.
195*ef8d499eSDavid van Moolenbroek 	 */
196*ef8d499eSDavid van Moolenbroek 	for (i = 0; i < RTAX_MAX; i++) {
197*ef8d499eSDavid van Moolenbroek 		if (rta->rta_ptr[i] == NULL)
198*ef8d499eSDavid van Moolenbroek 			continue;
199*ef8d499eSDavid van Moolenbroek 
200*ef8d499eSDavid van Moolenbroek 		if ((len = rta->rta_len[i]) > 0) {
201*ef8d499eSDavid van Moolenbroek 			assert(iovcnt < __arraycount(iov));
202*ef8d499eSDavid van Moolenbroek 			iov[iovcnt].iov_addr = (vir_bytes)rta->rta_ptr[i];
203*ef8d499eSDavid van Moolenbroek 			iov[iovcnt++].iov_size = len;
204*ef8d499eSDavid van Moolenbroek 		}
205*ef8d499eSDavid van Moolenbroek 
206*ef8d499eSDavid van Moolenbroek 		/* Note that RT_ROUNDUP(0) is not 0.. */
207*ef8d499eSDavid van Moolenbroek 		if ((padlen = RT_ROUNDUP(len) - len) > 0) {
208*ef8d499eSDavid van Moolenbroek 			assert(iovcnt < __arraycount(iov));
209*ef8d499eSDavid van Moolenbroek 			iov[iovcnt].iov_addr = (vir_bytes)rtsock_padbuf;
210*ef8d499eSDavid van Moolenbroek 			iov[iovcnt++].iov_size = padlen;
211*ef8d499eSDavid van Moolenbroek 		}
212*ef8d499eSDavid van Moolenbroek 
213*ef8d499eSDavid van Moolenbroek 		totallen += len + padlen;
214*ef8d499eSDavid van Moolenbroek 		mask |= (1 << i);	/* convert RTAX_ to RTA_ */
215*ef8d499eSDavid van Moolenbroek 	}
216*ef8d499eSDavid van Moolenbroek 
217*ef8d499eSDavid van Moolenbroek 	/* If only the length was requested, return it now. */
218*ef8d499eSDavid van Moolenbroek 	if (pbuf == NULL && oldp == NULL)
219*ef8d499eSDavid van Moolenbroek 		return totallen;
220*ef8d499eSDavid van Moolenbroek 
221*ef8d499eSDavid van Moolenbroek 	/*
222*ef8d499eSDavid van Moolenbroek 	 * Casting 'hdr' would violate C99 strict aliasing rules, but the
223*ef8d499eSDavid van Moolenbroek 	 * address mask is not always at the same location anyway.
224*ef8d499eSDavid van Moolenbroek 	 */
225*ef8d499eSDavid van Moolenbroek 	*msglen = totallen;
226*ef8d499eSDavid van Moolenbroek 	*addrs = mask;
227*ef8d499eSDavid van Moolenbroek 
228*ef8d499eSDavid van Moolenbroek 	if (pbuf != NULL) {
229*ef8d499eSDavid van Moolenbroek 		if ((*pbuf = rtsock_alloc(totallen)) == NULL)
230*ef8d499eSDavid van Moolenbroek 			return ENOMEM;
231*ef8d499eSDavid van Moolenbroek 
232*ef8d499eSDavid van Moolenbroek 		return util_coalesce((char *)(*pbuf)->payload, totallen, iov,
233*ef8d499eSDavid van Moolenbroek 		    iovcnt);
234*ef8d499eSDavid van Moolenbroek 	} else
235*ef8d499eSDavid van Moolenbroek 		return rmib_vcopyout(oldp, off, iov, iovcnt);
236*ef8d499eSDavid van Moolenbroek }
237*ef8d499eSDavid van Moolenbroek 
238*ef8d499eSDavid van Moolenbroek /*
239*ef8d499eSDavid van Moolenbroek  * Reduce the size of a network mask to the bytes actually used.  It is highly
240*ef8d499eSDavid van Moolenbroek  * doubtful that this extra complexity pays off in any form, but it is what the
241*ef8d499eSDavid van Moolenbroek  * BSDs historically do.  We currently implement compression for IPv4 only.
242*ef8d499eSDavid van Moolenbroek  */
243*ef8d499eSDavid van Moolenbroek static void
rtsock_compress_netmask(struct sockaddr * sa)244*ef8d499eSDavid van Moolenbroek rtsock_compress_netmask(struct sockaddr * sa)
245*ef8d499eSDavid van Moolenbroek {
246*ef8d499eSDavid van Moolenbroek 	struct sockaddr_in sin;
247*ef8d499eSDavid van Moolenbroek 	uint32_t addr;
248*ef8d499eSDavid van Moolenbroek 
249*ef8d499eSDavid van Moolenbroek 	if (sa->sa_family != AF_INET)
250*ef8d499eSDavid van Moolenbroek 		return; /* nothing to do */
251*ef8d499eSDavid van Moolenbroek 
252*ef8d499eSDavid van Moolenbroek 	memcpy(&sin, sa, sizeof(sin));	/* no type punning.. (sigh) */
253*ef8d499eSDavid van Moolenbroek 
254*ef8d499eSDavid van Moolenbroek 	addr = htonl(sin.sin_addr.s_addr);
255*ef8d499eSDavid van Moolenbroek 
256*ef8d499eSDavid van Moolenbroek 	if (addr & 0x000000ff)
257*ef8d499eSDavid van Moolenbroek 		sa->sa_len = 8;
258*ef8d499eSDavid van Moolenbroek 	else if (addr & 0x0000ffff)
259*ef8d499eSDavid van Moolenbroek 		sa->sa_len = 7;
260*ef8d499eSDavid van Moolenbroek 	else if (addr & 0x00ffffff)
261*ef8d499eSDavid van Moolenbroek 		sa->sa_len = 6;
262*ef8d499eSDavid van Moolenbroek 	else if (addr != 0)
263*ef8d499eSDavid van Moolenbroek 		sa->sa_len = 5;
264*ef8d499eSDavid van Moolenbroek 	else
265*ef8d499eSDavid van Moolenbroek 		sa->sa_len = 0;
266*ef8d499eSDavid van Moolenbroek }
267*ef8d499eSDavid van Moolenbroek 
268*ef8d499eSDavid van Moolenbroek /*
269*ef8d499eSDavid van Moolenbroek  * Expand a possibly compressed IPv4 or IPv6 network mask, given as 'sa', into
270*ef8d499eSDavid van Moolenbroek  * 'mask'.  Return TRUE if expansion succeeded.  In that case, the resulting
271*ef8d499eSDavid van Moolenbroek  * mask must have sa.sa_len and sa.sa_family filled in correctly, and have the
272*ef8d499eSDavid van Moolenbroek  * appropriate size for its address family.  Return FALSE if expansion failed
273*ef8d499eSDavid van Moolenbroek  * and an error should be returned to the caller.
274*ef8d499eSDavid van Moolenbroek  */
275*ef8d499eSDavid van Moolenbroek static int
rtsock_expand_netmask(union sockaddr_any * mask,const struct sockaddr * sa)276*ef8d499eSDavid van Moolenbroek rtsock_expand_netmask(union sockaddr_any * mask, const struct sockaddr * sa)
277*ef8d499eSDavid van Moolenbroek {
278*ef8d499eSDavid van Moolenbroek 
279*ef8d499eSDavid van Moolenbroek 	if (sa->sa_len > sizeof(*mask))
280*ef8d499eSDavid van Moolenbroek 		return FALSE;
281*ef8d499eSDavid van Moolenbroek 
282*ef8d499eSDavid van Moolenbroek 	memset(mask, 0, sizeof(*mask));
283*ef8d499eSDavid van Moolenbroek 	memcpy(mask, sa, sa->sa_len);
284*ef8d499eSDavid van Moolenbroek 
285*ef8d499eSDavid van Moolenbroek 	/*
286*ef8d499eSDavid van Moolenbroek 	 * Amazingly, even the address family may be chopped off, in which case
287*ef8d499eSDavid van Moolenbroek 	 * an IPv4 address is implied.
288*ef8d499eSDavid van Moolenbroek 	 */
289*ef8d499eSDavid van Moolenbroek 	if (sa->sa_len >= offsetof(struct sockaddr, sa_data) &&
290*ef8d499eSDavid van Moolenbroek 	    sa->sa_family == AF_INET6) {
291*ef8d499eSDavid van Moolenbroek 		if (sa->sa_len > sizeof(struct sockaddr_in6))
292*ef8d499eSDavid van Moolenbroek 			return FALSE;
293*ef8d499eSDavid van Moolenbroek 
294*ef8d499eSDavid van Moolenbroek 		mask->sa.sa_len = sizeof(struct sockaddr_in6);
295*ef8d499eSDavid van Moolenbroek 		mask->sa.sa_family = AF_INET6;
296*ef8d499eSDavid van Moolenbroek 	} else {
297*ef8d499eSDavid van Moolenbroek 		if (sa->sa_len > sizeof(struct sockaddr_in))
298*ef8d499eSDavid van Moolenbroek 			return FALSE;
299*ef8d499eSDavid van Moolenbroek 
300*ef8d499eSDavid van Moolenbroek 		mask->sa.sa_len = sizeof(struct sockaddr_in);
301*ef8d499eSDavid van Moolenbroek 		mask->sa.sa_family = AF_INET;
302*ef8d499eSDavid van Moolenbroek 	}
303*ef8d499eSDavid van Moolenbroek 
304*ef8d499eSDavid van Moolenbroek 	return TRUE;
305*ef8d499eSDavid van Moolenbroek }
306*ef8d499eSDavid van Moolenbroek 
307*ef8d499eSDavid van Moolenbroek /*
308*ef8d499eSDavid van Moolenbroek  * Create a routing socket.
309*ef8d499eSDavid van Moolenbroek  */
310*ef8d499eSDavid van Moolenbroek sockid_t
rtsock_socket(int type,int protocol,struct sock ** sockp,const struct sockevent_ops ** ops)311*ef8d499eSDavid van Moolenbroek rtsock_socket(int type, int protocol, struct sock ** sockp,
312*ef8d499eSDavid van Moolenbroek 	const struct sockevent_ops ** ops)
313*ef8d499eSDavid van Moolenbroek {
314*ef8d499eSDavid van Moolenbroek 	struct rtsock *rt;
315*ef8d499eSDavid van Moolenbroek 
316*ef8d499eSDavid van Moolenbroek 	/*
317*ef8d499eSDavid van Moolenbroek 	 * There is no superuser check here: regular users are allowed to issue
318*ef8d499eSDavid van Moolenbroek 	 * (only) RTM_GET requests on routing sockets.
319*ef8d499eSDavid van Moolenbroek 	 */
320*ef8d499eSDavid van Moolenbroek 	if (type != SOCK_RAW)
321*ef8d499eSDavid van Moolenbroek 		return EPROTOTYPE;
322*ef8d499eSDavid van Moolenbroek 
323*ef8d499eSDavid van Moolenbroek 	/* We could accept only the protocols we know, but this is fine too. */
324*ef8d499eSDavid van Moolenbroek 	if (protocol < 0 || protocol >= AF_MAX)
325*ef8d499eSDavid van Moolenbroek 		return EPROTONOSUPPORT;
326*ef8d499eSDavid van Moolenbroek 
327*ef8d499eSDavid van Moolenbroek 	if (TAILQ_EMPTY(&rt_freelist))
328*ef8d499eSDavid van Moolenbroek 		return ENOBUFS;
329*ef8d499eSDavid van Moolenbroek 
330*ef8d499eSDavid van Moolenbroek 	rt = TAILQ_FIRST(&rt_freelist);
331*ef8d499eSDavid van Moolenbroek 	TAILQ_REMOVE(&rt_freelist, rt, rt_next);
332*ef8d499eSDavid van Moolenbroek 
333*ef8d499eSDavid van Moolenbroek 	rt->rt_flags = 0;
334*ef8d499eSDavid van Moolenbroek 	rt->rt_family = protocol;
335*ef8d499eSDavid van Moolenbroek 	rt->rt_rcvhead = NULL;
336*ef8d499eSDavid van Moolenbroek 	rt->rt_rcvtailp = &rt->rt_rcvhead;
337*ef8d499eSDavid van Moolenbroek 	rt->rt_rcvlen = 0;
338*ef8d499eSDavid van Moolenbroek 	rt->rt_rcvbuf = RT_RCVBUF_DEF;
339*ef8d499eSDavid van Moolenbroek 
340*ef8d499eSDavid van Moolenbroek 	TAILQ_INSERT_HEAD(&rt_activelist, rt, rt_next);
341*ef8d499eSDavid van Moolenbroek 
342*ef8d499eSDavid van Moolenbroek 	*sockp = &rt->rt_sock;
343*ef8d499eSDavid van Moolenbroek 	*ops = &rtsock_ops;
344*ef8d499eSDavid van Moolenbroek 	return SOCKID_RT | (sockid_t)(rt - rt_array);
345*ef8d499eSDavid van Moolenbroek }
346*ef8d499eSDavid van Moolenbroek 
347*ef8d499eSDavid van Moolenbroek /*
348*ef8d499eSDavid van Moolenbroek  * Enqueue data on the receive queue of a routing socket.  The caller must have
349*ef8d499eSDavid van Moolenbroek  * checked whether the receive buffer size allows for the receipt of the data.
350*ef8d499eSDavid van Moolenbroek  */
351*ef8d499eSDavid van Moolenbroek static void
rtsock_enqueue(struct rtsock * rt,struct pbuf * pbuf)352*ef8d499eSDavid van Moolenbroek rtsock_enqueue(struct rtsock * rt, struct pbuf * pbuf)
353*ef8d499eSDavid van Moolenbroek {
354*ef8d499eSDavid van Moolenbroek 
355*ef8d499eSDavid van Moolenbroek 	*rt->rt_rcvtailp = pbuf;
356*ef8d499eSDavid van Moolenbroek 	rt->rt_rcvtailp = pchain_end(pbuf);
357*ef8d499eSDavid van Moolenbroek 	rt->rt_rcvlen += pchain_size(pbuf);
358*ef8d499eSDavid van Moolenbroek 
359*ef8d499eSDavid van Moolenbroek 	sockevent_raise(&rt->rt_sock, SEV_RECV);
360*ef8d499eSDavid van Moolenbroek }
361*ef8d499eSDavid van Moolenbroek 
362*ef8d499eSDavid van Moolenbroek /*
363*ef8d499eSDavid van Moolenbroek  * Determine whether a routing message for address family 'family', originated
364*ef8d499eSDavid van Moolenbroek  * from routing socket 'rtsrc' if not NULL, should be sent to routing socket
365*ef8d499eSDavid van Moolenbroek  * 'rt'.  Return TRUE if the message should be sent to this socket, or FALSE
366*ef8d499eSDavid van Moolenbroek  * if it should not.
367*ef8d499eSDavid van Moolenbroek  */
368*ef8d499eSDavid van Moolenbroek static int
rtsock_can_send(struct rtsock * rt,struct rtsock * rtsrc,int family)369*ef8d499eSDavid van Moolenbroek rtsock_can_send(struct rtsock *rt, struct rtsock *rtsrc, int family)
370*ef8d499eSDavid van Moolenbroek {
371*ef8d499eSDavid van Moolenbroek 
372*ef8d499eSDavid van Moolenbroek 	/* Do not send anything on sockets shut down for reading. */
373*ef8d499eSDavid van Moolenbroek 	if (sockevent_is_shutdown(&rt->rt_sock, SFL_SHUT_RD))
374*ef8d499eSDavid van Moolenbroek 		return FALSE;
375*ef8d499eSDavid van Moolenbroek 
376*ef8d499eSDavid van Moolenbroek 	/*
377*ef8d499eSDavid van Moolenbroek 	 * Do not send a reply message to the source of the request if the
378*ef8d499eSDavid van Moolenbroek 	 * source is not interested in replies to its own requests.
379*ef8d499eSDavid van Moolenbroek 	 */
380*ef8d499eSDavid van Moolenbroek 	if (rt == rtsrc && (rt->rt_flags & RTF_NOLOOPBACK))
381*ef8d499eSDavid van Moolenbroek 		return FALSE;
382*ef8d499eSDavid van Moolenbroek 
383*ef8d499eSDavid van Moolenbroek 	/*
384*ef8d499eSDavid van Moolenbroek 	 * For address family specific messages, make sure the routing socket
385*ef8d499eSDavid van Moolenbroek 	 * is interested in that family.  Make an exception if the socket was
386*ef8d499eSDavid van Moolenbroek 	 * the source of the request, though: we currently do not prevent user
387*ef8d499eSDavid van Moolenbroek 	 * processes from issuing commands for the "wrong" family.
388*ef8d499eSDavid van Moolenbroek 	 */
389*ef8d499eSDavid van Moolenbroek 	if (rt->rt_family != AF_UNSPEC && family != AF_UNSPEC &&
390*ef8d499eSDavid van Moolenbroek 	    rt->rt_family != family && rt != rtsrc)
391*ef8d499eSDavid van Moolenbroek 		return FALSE;
392*ef8d499eSDavid van Moolenbroek 
393*ef8d499eSDavid van Moolenbroek 	/*
394*ef8d499eSDavid van Moolenbroek 	 * See whether the receive queue of the socket is already full.  We do
395*ef8d499eSDavid van Moolenbroek 	 * not consider the size of the current request, in order to not drop
396*ef8d499eSDavid van Moolenbroek 	 * larger messages and then enqueue smaller ones.
397*ef8d499eSDavid van Moolenbroek 	 */
398*ef8d499eSDavid van Moolenbroek 	if (rt->rt_rcvlen >= rt->rt_rcvbuf)
399*ef8d499eSDavid van Moolenbroek 		return FALSE;
400*ef8d499eSDavid van Moolenbroek 
401*ef8d499eSDavid van Moolenbroek 	/* All is well: go on and deliver the message. */
402*ef8d499eSDavid van Moolenbroek 	return TRUE;
403*ef8d499eSDavid van Moolenbroek }
404*ef8d499eSDavid van Moolenbroek 
405*ef8d499eSDavid van Moolenbroek /*
406*ef8d499eSDavid van Moolenbroek  * Send the routing message in 'pbuf' to the given routing socket if possible,
407*ef8d499eSDavid van Moolenbroek  * or check whether such a message could be sent to that socket if 'pbuf' is
408*ef8d499eSDavid van Moolenbroek  * NULL.  In the former case, the function takes ownership of 'pbuf'.  The
409*ef8d499eSDavid van Moolenbroek  * given routing socket is assumed to be the source of the routing request that
410*ef8d499eSDavid van Moolenbroek  * generated this message.  In the latter case, the function returns TRUE if
411*ef8d499eSDavid van Moolenbroek  * the socket would take the message or FALSE if not.  If 'family' is not
412*ef8d499eSDavid van Moolenbroek  * AF_UNSPEC, it is to be the address family of the message.
413*ef8d499eSDavid van Moolenbroek  */
414*ef8d499eSDavid van Moolenbroek static int
rtsock_msg_one(struct rtsock * rt,int family,struct pbuf * pbuf)415*ef8d499eSDavid van Moolenbroek rtsock_msg_one(struct rtsock * rt, int family, struct pbuf * pbuf)
416*ef8d499eSDavid van Moolenbroek {
417*ef8d499eSDavid van Moolenbroek 
418*ef8d499eSDavid van Moolenbroek 	if (rtsock_can_send(rt, rt, family)) {
419*ef8d499eSDavid van Moolenbroek 		if (pbuf != NULL)
420*ef8d499eSDavid van Moolenbroek 			rtsock_enqueue(rt, pbuf);
421*ef8d499eSDavid van Moolenbroek 
422*ef8d499eSDavid van Moolenbroek 		return TRUE;
423*ef8d499eSDavid van Moolenbroek 	} else {
424*ef8d499eSDavid van Moolenbroek 		if (pbuf != NULL)
425*ef8d499eSDavid van Moolenbroek 			pbuf_free(pbuf);
426*ef8d499eSDavid van Moolenbroek 
427*ef8d499eSDavid van Moolenbroek 		return FALSE;
428*ef8d499eSDavid van Moolenbroek 	}
429*ef8d499eSDavid van Moolenbroek }
430*ef8d499eSDavid van Moolenbroek 
431*ef8d499eSDavid van Moolenbroek /*
432*ef8d499eSDavid van Moolenbroek  * Send the routing message in 'pbuf' to all matching routing sockets, or check
433*ef8d499eSDavid van Moolenbroek  * whether there are any such matching routing sockets if 'pbuf' is NULL.  In
434*ef8d499eSDavid van Moolenbroek  * the former case, the function takes ownership of 'pbuf'.  In the latter
435*ef8d499eSDavid van Moolenbroek  * case, the function returns TRUE if there are any matching sockets or FALSE
436*ef8d499eSDavid van Moolenbroek  * if there are none.  If 'rtsrc' is not NULL, it is to be the routing socket
437*ef8d499eSDavid van Moolenbroek  * that is the source of the message.  If 'family' is not AF_UNSPEC, it is to
438*ef8d499eSDavid van Moolenbroek  * be the address family of the message.
439*ef8d499eSDavid van Moolenbroek  */
440*ef8d499eSDavid van Moolenbroek static int
rtsock_msg_match(struct rtsock * rtsrc,int family,struct pbuf * pbuf)441*ef8d499eSDavid van Moolenbroek rtsock_msg_match(struct rtsock * rtsrc, int family, struct pbuf * pbuf)
442*ef8d499eSDavid van Moolenbroek {
443*ef8d499eSDavid van Moolenbroek 	struct rtsock *rt, *rtprev;
444*ef8d499eSDavid van Moolenbroek 	struct pbuf *pcopy;
445*ef8d499eSDavid van Moolenbroek 
446*ef8d499eSDavid van Moolenbroek 	rtprev = NULL;
447*ef8d499eSDavid van Moolenbroek 
448*ef8d499eSDavid van Moolenbroek 	TAILQ_FOREACH(rt, &rt_activelist, rt_next) {
449*ef8d499eSDavid van Moolenbroek 		if (!rtsock_can_send(rt, rtsrc, family))
450*ef8d499eSDavid van Moolenbroek 			continue;
451*ef8d499eSDavid van Moolenbroek 
452*ef8d499eSDavid van Moolenbroek 		/*
453*ef8d499eSDavid van Moolenbroek 		 * There is at least one routing socket that is interested in
454*ef8d499eSDavid van Moolenbroek 		 * receiving this message, and able to receive it.
455*ef8d499eSDavid van Moolenbroek 		 */
456*ef8d499eSDavid van Moolenbroek 		if (pbuf == NULL)
457*ef8d499eSDavid van Moolenbroek 			return TRUE;
458*ef8d499eSDavid van Moolenbroek 
459*ef8d499eSDavid van Moolenbroek 		/*
460*ef8d499eSDavid van Moolenbroek 		 * We need to make copies of the generated message for all but
461*ef8d499eSDavid van Moolenbroek 		 * the last matching socket, which gets the original.  If we're
462*ef8d499eSDavid van Moolenbroek 		 * out of memory, free the original and stop: there are more
463*ef8d499eSDavid van Moolenbroek 		 * important things to spend memory on than routing sockets.
464*ef8d499eSDavid van Moolenbroek 		 */
465*ef8d499eSDavid van Moolenbroek 		if (rtprev != NULL) {
466*ef8d499eSDavid van Moolenbroek 			if ((pcopy = rtsock_alloc(pbuf->tot_len)) == NULL) {
467*ef8d499eSDavid van Moolenbroek 				pbuf_free(pbuf);
468*ef8d499eSDavid van Moolenbroek 
469*ef8d499eSDavid van Moolenbroek 				return TRUE;
470*ef8d499eSDavid van Moolenbroek 			}
471*ef8d499eSDavid van Moolenbroek 
472*ef8d499eSDavid van Moolenbroek 			if (pbuf_copy(pcopy, pbuf) != ERR_OK)
473*ef8d499eSDavid van Moolenbroek 				panic("unexpected pbuf copy failure");
474*ef8d499eSDavid van Moolenbroek 
475*ef8d499eSDavid van Moolenbroek 			rtsock_enqueue(rtprev, pcopy);
476*ef8d499eSDavid van Moolenbroek 		}
477*ef8d499eSDavid van Moolenbroek 
478*ef8d499eSDavid van Moolenbroek 		rtprev = rt;
479*ef8d499eSDavid van Moolenbroek 	}
480*ef8d499eSDavid van Moolenbroek 
481*ef8d499eSDavid van Moolenbroek 	if (rtprev != NULL)
482*ef8d499eSDavid van Moolenbroek 		rtsock_enqueue(rtprev, pbuf);
483*ef8d499eSDavid van Moolenbroek 	else if (pbuf != NULL)
484*ef8d499eSDavid van Moolenbroek 		pbuf_free(pbuf);
485*ef8d499eSDavid van Moolenbroek 
486*ef8d499eSDavid van Moolenbroek 	return (rtprev != NULL);
487*ef8d499eSDavid van Moolenbroek }
488*ef8d499eSDavid van Moolenbroek 
489*ef8d499eSDavid van Moolenbroek /*
490*ef8d499eSDavid van Moolenbroek  * Dequeue and free the head of the receive queue of a routing socket.
491*ef8d499eSDavid van Moolenbroek  */
492*ef8d499eSDavid van Moolenbroek static void
rtsock_dequeue(struct rtsock * rt)493*ef8d499eSDavid van Moolenbroek rtsock_dequeue(struct rtsock * rt)
494*ef8d499eSDavid van Moolenbroek {
495*ef8d499eSDavid van Moolenbroek 	struct pbuf *pbuf, **pnext;
496*ef8d499eSDavid van Moolenbroek 	size_t size;
497*ef8d499eSDavid van Moolenbroek 
498*ef8d499eSDavid van Moolenbroek 	pbuf = rt->rt_rcvhead;
499*ef8d499eSDavid van Moolenbroek 	assert(pbuf != NULL);
500*ef8d499eSDavid van Moolenbroek 
501*ef8d499eSDavid van Moolenbroek 	pnext = pchain_end(pbuf);
502*ef8d499eSDavid van Moolenbroek 	size = pchain_size(pbuf);
503*ef8d499eSDavid van Moolenbroek 
504*ef8d499eSDavid van Moolenbroek 	if ((rt->rt_rcvhead = *pnext) == NULL)
505*ef8d499eSDavid van Moolenbroek 		rt->rt_rcvtailp = &rt->rt_rcvhead;
506*ef8d499eSDavid van Moolenbroek 
507*ef8d499eSDavid van Moolenbroek 	assert(rt->rt_rcvlen >= size);
508*ef8d499eSDavid van Moolenbroek 	rt->rt_rcvlen -= size;
509*ef8d499eSDavid van Moolenbroek 
510*ef8d499eSDavid van Moolenbroek 	*pnext = NULL;
511*ef8d499eSDavid van Moolenbroek 	pbuf_free(pbuf);
512*ef8d499eSDavid van Moolenbroek }
513*ef8d499eSDavid van Moolenbroek 
514*ef8d499eSDavid van Moolenbroek /*
515*ef8d499eSDavid van Moolenbroek  * Process a routing message sent on a socket.  Return OK on success, in which
516*ef8d499eSDavid van Moolenbroek  * case the caller assumes that the processing routine has sent a reply to the
517*ef8d499eSDavid van Moolenbroek  * user and possibly other routing sockets.  Return a negative error code on
518*ef8d499eSDavid van Moolenbroek  * failure, in which case the caller will send the reply to the user instead.
519*ef8d499eSDavid van Moolenbroek  */
520*ef8d499eSDavid van Moolenbroek static int
rtsock_process(struct rtsock * rt,struct rt_msghdr * rtm,char * buf,size_t len,int is_root)521*ef8d499eSDavid van Moolenbroek rtsock_process(struct rtsock *rt, struct rt_msghdr * rtm, char * buf,
522*ef8d499eSDavid van Moolenbroek 	size_t len, int is_root)
523*ef8d499eSDavid van Moolenbroek {
524*ef8d499eSDavid van Moolenbroek 	struct rtsock_request rtr;
525*ef8d499eSDavid van Moolenbroek 	struct rtsock_rta rta;
526*ef8d499eSDavid van Moolenbroek 	const struct sockaddr *netmask;
527*ef8d499eSDavid van Moolenbroek 	struct sockaddr sa;
528*ef8d499eSDavid van Moolenbroek 	union sockaddr_any mask;
529*ef8d499eSDavid van Moolenbroek 	size_t off;
530*ef8d499eSDavid van Moolenbroek 	int i;
531*ef8d499eSDavid van Moolenbroek 
532*ef8d499eSDavid van Moolenbroek 	if (rtm->rtm_msglen != len)
533*ef8d499eSDavid van Moolenbroek 		return EINVAL;
534*ef8d499eSDavid van Moolenbroek 
535*ef8d499eSDavid van Moolenbroek 	if (rtm->rtm_version != RTM_VERSION) {
536*ef8d499eSDavid van Moolenbroek 		printf("LWIP: PID %d uses routing sockets version %u\n",
537*ef8d499eSDavid van Moolenbroek 			rtm->rtm_pid, rtm->rtm_version);
538*ef8d499eSDavid van Moolenbroek 
539*ef8d499eSDavid van Moolenbroek 		return EPROTONOSUPPORT;
540*ef8d499eSDavid van Moolenbroek 	}
541*ef8d499eSDavid van Moolenbroek 
542*ef8d499eSDavid van Moolenbroek 	/*
543*ef8d499eSDavid van Moolenbroek 	 * Make sure that we won't misinterpret the rest of the message.  While
544*ef8d499eSDavid van Moolenbroek 	 * looking at the message type, also make sure non-root users can only
545*ef8d499eSDavid van Moolenbroek 	 * ever issue RTM_GET requests.
546*ef8d499eSDavid van Moolenbroek 	 */
547*ef8d499eSDavid van Moolenbroek 	switch (rtm->rtm_type) {
548*ef8d499eSDavid van Moolenbroek 	case RTM_ADD:
549*ef8d499eSDavid van Moolenbroek 	case RTM_DELETE:
550*ef8d499eSDavid van Moolenbroek 	case RTM_CHANGE:
551*ef8d499eSDavid van Moolenbroek 	case RTM_LOCK:
552*ef8d499eSDavid van Moolenbroek 		if (!is_root)
553*ef8d499eSDavid van Moolenbroek 			return EPERM;
554*ef8d499eSDavid van Moolenbroek 
555*ef8d499eSDavid van Moolenbroek 		/* FALLTHROUGH */
556*ef8d499eSDavid van Moolenbroek 	case RTM_GET:
557*ef8d499eSDavid van Moolenbroek 		break;
558*ef8d499eSDavid van Moolenbroek 
559*ef8d499eSDavid van Moolenbroek 	default:
560*ef8d499eSDavid van Moolenbroek 		return EOPNOTSUPP;
561*ef8d499eSDavid van Moolenbroek 	}
562*ef8d499eSDavid van Moolenbroek 
563*ef8d499eSDavid van Moolenbroek 	/*
564*ef8d499eSDavid van Moolenbroek 	 * Extract all given addresses.  We do not actually support all types
565*ef8d499eSDavid van Moolenbroek 	 * of entries, but we cannot skip the ones we do not need either.
566*ef8d499eSDavid van Moolenbroek 	 */
567*ef8d499eSDavid van Moolenbroek 	rtsock_rta_init(&rta);
568*ef8d499eSDavid van Moolenbroek 
569*ef8d499eSDavid van Moolenbroek 	off = sizeof(*rtm);
570*ef8d499eSDavid van Moolenbroek 	assert(off == RT_ROUNDUP(off));
571*ef8d499eSDavid van Moolenbroek 
572*ef8d499eSDavid van Moolenbroek 	for (i = 0; i < RTAX_MAX; i++) {
573*ef8d499eSDavid van Moolenbroek 		if (!(rtm->rtm_addrs & (1 << i)))
574*ef8d499eSDavid van Moolenbroek 			continue;
575*ef8d499eSDavid van Moolenbroek 
576*ef8d499eSDavid van Moolenbroek 		if (off + offsetof(struct sockaddr, sa_data) > len)
577*ef8d499eSDavid van Moolenbroek 			return EINVAL;
578*ef8d499eSDavid van Moolenbroek 
579*ef8d499eSDavid van Moolenbroek 		/*
580*ef8d499eSDavid van Moolenbroek 		 * It is safe to access sa_len and even sa_family in all cases,
581*ef8d499eSDavid van Moolenbroek 		 * in particular even when the structure is of size zero.
582*ef8d499eSDavid van Moolenbroek 		 */
583*ef8d499eSDavid van Moolenbroek 		assert(offsetof(struct sockaddr, sa_data) <= RT_ROUNDUP(0));
584*ef8d499eSDavid van Moolenbroek 
585*ef8d499eSDavid van Moolenbroek 		memcpy(&sa, &buf[off], offsetof(struct sockaddr, sa_data));
586*ef8d499eSDavid van Moolenbroek 
587*ef8d499eSDavid van Moolenbroek 		if (off + sa.sa_len > len)
588*ef8d499eSDavid van Moolenbroek 			return EINVAL;
589*ef8d499eSDavid van Moolenbroek 
590*ef8d499eSDavid van Moolenbroek 		rtsock_rta_set(&rta, i, &buf[off], sa.sa_len);
591*ef8d499eSDavid van Moolenbroek 
592*ef8d499eSDavid van Moolenbroek 		off += RT_ROUNDUP((size_t)sa.sa_len);
593*ef8d499eSDavid van Moolenbroek 	}
594*ef8d499eSDavid van Moolenbroek 
595*ef8d499eSDavid van Moolenbroek 	/*
596*ef8d499eSDavid van Moolenbroek 	 * Expand the given netmask if it is in compressed IPv4 form.  We do
597*ef8d499eSDavid van Moolenbroek 	 * this here because it is particular to routing sockets; we also do
598*ef8d499eSDavid van Moolenbroek 	 * the compression in this module.  Note how the compression may even
599*ef8d499eSDavid van Moolenbroek 	 * strip off the address family; really, who came up with this ****?
600*ef8d499eSDavid van Moolenbroek 	 */
601*ef8d499eSDavid van Moolenbroek 	netmask = (const struct sockaddr *)rta.rta_ptr[RTAX_NETMASK];
602*ef8d499eSDavid van Moolenbroek 
603*ef8d499eSDavid van Moolenbroek 	if (netmask != NULL) {
604*ef8d499eSDavid van Moolenbroek 		if (!rtsock_expand_netmask(&mask, netmask))
605*ef8d499eSDavid van Moolenbroek 			return EINVAL;
606*ef8d499eSDavid van Moolenbroek 
607*ef8d499eSDavid van Moolenbroek 		rtsock_rta_set(&rta, RTAX_NETMASK, &mask, mask.sa.sa_len);
608*ef8d499eSDavid van Moolenbroek 	}
609*ef8d499eSDavid van Moolenbroek 
610*ef8d499eSDavid van Moolenbroek 	/*
611*ef8d499eSDavid van Moolenbroek 	 * Actually process the command.  Pass on enough information so that a
612*ef8d499eSDavid van Moolenbroek 	 * reply can be generated on success.  The abstraction as sketched at
613*ef8d499eSDavid van Moolenbroek 	 * the top of the file imposes that we pass quite a few parameters.
614*ef8d499eSDavid van Moolenbroek 	 */
615*ef8d499eSDavid van Moolenbroek 	rtr.rtr_src = rt;
616*ef8d499eSDavid van Moolenbroek 	rtr.rtr_pid = rtm->rtm_pid;
617*ef8d499eSDavid van Moolenbroek 	rtr.rtr_seq = rtm->rtm_seq;
618*ef8d499eSDavid van Moolenbroek 	rtr.rtr_getif = (rtm->rtm_type == RTM_GET &&
619*ef8d499eSDavid van Moolenbroek 	    (rta.rta_ptr[RTAX_IFP] != NULL || rta.rta_ptr[RTAX_IFA] != NULL));
620*ef8d499eSDavid van Moolenbroek 
621*ef8d499eSDavid van Moolenbroek 	return route_process(rtm->rtm_type,
622*ef8d499eSDavid van Moolenbroek 	    (const struct sockaddr *)rta.rta_ptr[RTAX_DST],
623*ef8d499eSDavid van Moolenbroek 	    (const struct sockaddr *)rta.rta_ptr[RTAX_NETMASK],
624*ef8d499eSDavid van Moolenbroek 	    (const struct sockaddr *)rta.rta_ptr[RTAX_GATEWAY],
625*ef8d499eSDavid van Moolenbroek 	    (const struct sockaddr *)rta.rta_ptr[RTAX_IFP],
626*ef8d499eSDavid van Moolenbroek 	    (const struct sockaddr *)rta.rta_ptr[RTAX_IFA],
627*ef8d499eSDavid van Moolenbroek 	    rtm->rtm_flags, rtm->rtm_inits, &rtm->rtm_rmx, &rtr);
628*ef8d499eSDavid van Moolenbroek }
629*ef8d499eSDavid van Moolenbroek 
630*ef8d499eSDavid van Moolenbroek /*
631*ef8d499eSDavid van Moolenbroek  * Perform preliminary checks on a send request.
632*ef8d499eSDavid van Moolenbroek  */
633*ef8d499eSDavid van Moolenbroek static int
rtsock_pre_send(struct sock * sock __unused,size_t len,socklen_t ctl_len __unused,const struct sockaddr * addr,socklen_t addr_len __unused,endpoint_t user_endpt __unused,int flags)634*ef8d499eSDavid van Moolenbroek rtsock_pre_send(struct sock * sock __unused, size_t len,
635*ef8d499eSDavid van Moolenbroek 	socklen_t ctl_len __unused, const struct sockaddr * addr,
636*ef8d499eSDavid van Moolenbroek 	socklen_t addr_len __unused, endpoint_t user_endpt __unused, int flags)
637*ef8d499eSDavid van Moolenbroek {
638*ef8d499eSDavid van Moolenbroek 
639*ef8d499eSDavid van Moolenbroek 	if (flags != 0)
640*ef8d499eSDavid van Moolenbroek 		return EOPNOTSUPP;
641*ef8d499eSDavid van Moolenbroek 
642*ef8d499eSDavid van Moolenbroek 	if (addr != NULL)
643*ef8d499eSDavid van Moolenbroek 		return EISCONN;
644*ef8d499eSDavid van Moolenbroek 
645*ef8d499eSDavid van Moolenbroek 	/*
646*ef8d499eSDavid van Moolenbroek 	 * For the most basic failures - that is, we cannot even manage to
647*ef8d499eSDavid van Moolenbroek 	 * receive the request - we do not generate a reply message.
648*ef8d499eSDavid van Moolenbroek 	 */
649*ef8d499eSDavid van Moolenbroek 	if (len < sizeof(struct rt_msghdr))
650*ef8d499eSDavid van Moolenbroek 		return ENOBUFS;
651*ef8d499eSDavid van Moolenbroek 	if (len > RT_SNDBUF_MAX)
652*ef8d499eSDavid van Moolenbroek 		return EMSGSIZE;
653*ef8d499eSDavid van Moolenbroek 
654*ef8d499eSDavid van Moolenbroek 	return OK;
655*ef8d499eSDavid van Moolenbroek }
656*ef8d499eSDavid van Moolenbroek 
657*ef8d499eSDavid van Moolenbroek /*
658*ef8d499eSDavid van Moolenbroek  * Send data on a routing socket.
659*ef8d499eSDavid van Moolenbroek  */
660*ef8d499eSDavid van Moolenbroek static int
rtsock_send(struct sock * sock,const struct sockdriver_data * data,size_t len,size_t * offp,const struct sockdriver_data * ctl __unused,socklen_t ctl_len __unused,socklen_t * ctl_off __unused,const struct sockaddr * addr __unused,socklen_t addr_len __unused,endpoint_t user_endpt,int flags __unused,size_t min __unused)661*ef8d499eSDavid van Moolenbroek rtsock_send(struct sock * sock, const struct sockdriver_data * data,
662*ef8d499eSDavid van Moolenbroek 	size_t len, size_t * offp, const struct sockdriver_data * ctl __unused,
663*ef8d499eSDavid van Moolenbroek 	socklen_t ctl_len __unused, socklen_t * ctl_off __unused,
664*ef8d499eSDavid van Moolenbroek 	const struct sockaddr * addr __unused, socklen_t addr_len __unused,
665*ef8d499eSDavid van Moolenbroek 	endpoint_t user_endpt, int flags __unused, size_t min __unused)
666*ef8d499eSDavid van Moolenbroek {
667*ef8d499eSDavid van Moolenbroek 	struct rtsock *rt = (struct rtsock *)sock;
668*ef8d499eSDavid van Moolenbroek 	char buf[RT_SNDBUF_MAX] __aligned(4);
669*ef8d499eSDavid van Moolenbroek 	struct rt_msghdr rtm;
670*ef8d499eSDavid van Moolenbroek 	struct pbuf *pbuf;
671*ef8d499eSDavid van Moolenbroek 	uid_t euid;
672*ef8d499eSDavid van Moolenbroek 	int r, is_root;
673*ef8d499eSDavid van Moolenbroek 
674*ef8d499eSDavid van Moolenbroek 	/* Copy in the request, and adjust some fields right away. */
675*ef8d499eSDavid van Moolenbroek 	assert(len >= sizeof(rtm));
676*ef8d499eSDavid van Moolenbroek 	assert(len <= sizeof(buf));
677*ef8d499eSDavid van Moolenbroek 
678*ef8d499eSDavid van Moolenbroek 	if ((r = sockdriver_copyin(data, 0, buf, len)) != OK)
679*ef8d499eSDavid van Moolenbroek 		return r;
680*ef8d499eSDavid van Moolenbroek 
681*ef8d499eSDavid van Moolenbroek 	memcpy(&rtm, buf, sizeof(rtm));
682*ef8d499eSDavid van Moolenbroek 	rtm.rtm_errno = 0;
683*ef8d499eSDavid van Moolenbroek 	rtm.rtm_flags &= ~RTF_DONE;
684*ef8d499eSDavid van Moolenbroek 	rtm.rtm_pid = getepinfo(user_endpt, &euid, NULL /*gid*/);
685*ef8d499eSDavid van Moolenbroek 
686*ef8d499eSDavid van Moolenbroek 	is_root = (euid == ROOT_EUID);
687*ef8d499eSDavid van Moolenbroek 
688*ef8d499eSDavid van Moolenbroek 	/* Process the request. */
689*ef8d499eSDavid van Moolenbroek 	r = rtsock_process(rt, &rtm, buf, len, is_root);
690*ef8d499eSDavid van Moolenbroek 
691*ef8d499eSDavid van Moolenbroek 	/*
692*ef8d499eSDavid van Moolenbroek 	 * If the request has been processed successfully, a reply has been
693*ef8d499eSDavid van Moolenbroek 	 * sent already, possibly also to other routing sockets.  Here, we
694*ef8d499eSDavid van Moolenbroek 	 * handle the case that the request has resulted in failure, in which
695*ef8d499eSDavid van Moolenbroek 	 * case we send a reply to the caller only.  This behavior is different
696*ef8d499eSDavid van Moolenbroek 	 * from the traditional BSD behavior, which also sends failure replies
697*ef8d499eSDavid van Moolenbroek 	 * to other sockets.  Our motivation is that while other parties are
698*ef8d499eSDavid van Moolenbroek 	 * never going to be interested in failures anyway, it is in fact easy
699*ef8d499eSDavid van Moolenbroek 	 * for an unprivileged user process to abuse the failure-reply system
700*ef8d499eSDavid van Moolenbroek 	 * in order to fake other types of routing messages (e.g., RTM_IFINFO)
701*ef8d499eSDavid van Moolenbroek 	 * to other parties.  By sending failure replies only to the requestor,
702*ef8d499eSDavid van Moolenbroek 	 * we eliminate the need for security-sensitive request validation.
703*ef8d499eSDavid van Moolenbroek 	 */
704*ef8d499eSDavid van Moolenbroek 	if (r != OK && rtsock_can_send(rt, rt, AF_UNSPEC)) {
705*ef8d499eSDavid van Moolenbroek 		rtm.rtm_errno = -r;
706*ef8d499eSDavid van Moolenbroek 
707*ef8d499eSDavid van Moolenbroek 		if ((pbuf = rtsock_alloc(len)) == NULL)
708*ef8d499eSDavid van Moolenbroek 			return ENOMEM;
709*ef8d499eSDavid van Moolenbroek 
710*ef8d499eSDavid van Moolenbroek 		/* For the reply, reuse the request message largely as is. */
711*ef8d499eSDavid van Moolenbroek 		memcpy(pbuf->payload, &rtm, sizeof(rtm));
712*ef8d499eSDavid van Moolenbroek 		if (len > sizeof(rtm))
713*ef8d499eSDavid van Moolenbroek 			memcpy((uint8_t *)pbuf->payload + sizeof(rtm),
714*ef8d499eSDavid van Moolenbroek 			    buf + sizeof(rtm), len - sizeof(rtm));
715*ef8d499eSDavid van Moolenbroek 
716*ef8d499eSDavid van Moolenbroek 		rtsock_enqueue(rt, pbuf);
717*ef8d499eSDavid van Moolenbroek 	} else if (r == OK)
718*ef8d499eSDavid van Moolenbroek 		*offp = len;
719*ef8d499eSDavid van Moolenbroek 
720*ef8d499eSDavid van Moolenbroek 	return r;
721*ef8d499eSDavid van Moolenbroek }
722*ef8d499eSDavid van Moolenbroek 
723*ef8d499eSDavid van Moolenbroek /*
724*ef8d499eSDavid van Moolenbroek  * Perform preliminary checks on a receive request.
725*ef8d499eSDavid van Moolenbroek  */
726*ef8d499eSDavid van Moolenbroek static int
rtsock_pre_recv(struct sock * sock __unused,endpoint_t user_endpt __unused,int flags)727*ef8d499eSDavid van Moolenbroek rtsock_pre_recv(struct sock * sock __unused, endpoint_t user_endpt __unused,
728*ef8d499eSDavid van Moolenbroek 	int flags)
729*ef8d499eSDavid van Moolenbroek {
730*ef8d499eSDavid van Moolenbroek 
731*ef8d499eSDavid van Moolenbroek 	/*
732*ef8d499eSDavid van Moolenbroek 	 * We accept the same flags across all socket types in LWIP, and then
733*ef8d499eSDavid van Moolenbroek 	 * simply ignore the ones we do not support for routing sockets.
734*ef8d499eSDavid van Moolenbroek 	 */
735*ef8d499eSDavid van Moolenbroek 	if ((flags & ~(MSG_PEEK | MSG_WAITALL)) != 0)
736*ef8d499eSDavid van Moolenbroek 		return EOPNOTSUPP;
737*ef8d499eSDavid van Moolenbroek 
738*ef8d499eSDavid van Moolenbroek 	return OK;
739*ef8d499eSDavid van Moolenbroek }
740*ef8d499eSDavid van Moolenbroek 
741*ef8d499eSDavid van Moolenbroek /*
742*ef8d499eSDavid van Moolenbroek  * Receive data on a routing socket.
743*ef8d499eSDavid van Moolenbroek  */
744*ef8d499eSDavid van Moolenbroek static int
rtsock_recv(struct sock * sock,const struct sockdriver_data * data,size_t len,size_t * off,const struct sockdriver_data * ctl __unused,socklen_t ctl_len __unused,socklen_t * ctl_off __unused,struct sockaddr * addr,socklen_t * addr_len,endpoint_t user_endpt __unused,int flags,size_t min __unused,int * rflags)745*ef8d499eSDavid van Moolenbroek rtsock_recv(struct sock * sock, const struct sockdriver_data * data,
746*ef8d499eSDavid van Moolenbroek 	size_t len, size_t * off, const struct sockdriver_data * ctl __unused,
747*ef8d499eSDavid van Moolenbroek 	socklen_t ctl_len __unused, socklen_t * ctl_off __unused,
748*ef8d499eSDavid van Moolenbroek 	struct sockaddr * addr, socklen_t * addr_len,
749*ef8d499eSDavid van Moolenbroek 	endpoint_t user_endpt __unused, int flags, size_t min __unused,
750*ef8d499eSDavid van Moolenbroek 	int * rflags)
751*ef8d499eSDavid van Moolenbroek {
752*ef8d499eSDavid van Moolenbroek 	struct rtsock *rt = (struct rtsock *)sock;
753*ef8d499eSDavid van Moolenbroek 	struct pbuf *pbuf;
754*ef8d499eSDavid van Moolenbroek 	int r;
755*ef8d499eSDavid van Moolenbroek 
756*ef8d499eSDavid van Moolenbroek 	if ((pbuf = rt->rt_rcvhead) == NULL)
757*ef8d499eSDavid van Moolenbroek 		return SUSPEND;
758*ef8d499eSDavid van Moolenbroek 
759*ef8d499eSDavid van Moolenbroek 	/* Copy out the data to the calling user process. */
760*ef8d499eSDavid van Moolenbroek 	if (len >= pbuf->tot_len)
761*ef8d499eSDavid van Moolenbroek 		len = pbuf->tot_len;
762*ef8d499eSDavid van Moolenbroek 	else
763*ef8d499eSDavid van Moolenbroek 		*rflags |= MSG_TRUNC;
764*ef8d499eSDavid van Moolenbroek 
765*ef8d499eSDavid van Moolenbroek 	r = util_copy_data(data, len, 0, pbuf, 0, FALSE /*copy_in*/);
766*ef8d499eSDavid van Moolenbroek 
767*ef8d499eSDavid van Moolenbroek 	if (r != OK)
768*ef8d499eSDavid van Moolenbroek 		return r;
769*ef8d499eSDavid van Moolenbroek 
770*ef8d499eSDavid van Moolenbroek 	/* Generate a dummy source address. */
771*ef8d499eSDavid van Moolenbroek 	addr->sa_len = RTSOCK_ADDR_LEN;
772*ef8d499eSDavid van Moolenbroek 	addr->sa_family = AF_ROUTE;
773*ef8d499eSDavid van Moolenbroek 	*addr_len = RTSOCK_ADDR_LEN;
774*ef8d499eSDavid van Moolenbroek 
775*ef8d499eSDavid van Moolenbroek 	/* Discard the data now, unless we were instructed to peek only. */
776*ef8d499eSDavid van Moolenbroek 	if (!(flags & MSG_PEEK))
777*ef8d499eSDavid van Moolenbroek 		rtsock_dequeue(rt);
778*ef8d499eSDavid van Moolenbroek 
779*ef8d499eSDavid van Moolenbroek 	/* Return the received part of the data length. */
780*ef8d499eSDavid van Moolenbroek 	*off = len;
781*ef8d499eSDavid van Moolenbroek 	return OK;
782*ef8d499eSDavid van Moolenbroek }
783*ef8d499eSDavid van Moolenbroek 
784*ef8d499eSDavid van Moolenbroek /*
785*ef8d499eSDavid van Moolenbroek  * Test whether data can be received on a routing socket, and if so, how many
786*ef8d499eSDavid van Moolenbroek  * bytes of data.
787*ef8d499eSDavid van Moolenbroek  */
788*ef8d499eSDavid van Moolenbroek static int
rtsock_test_recv(struct sock * sock,size_t min __unused,size_t * size)789*ef8d499eSDavid van Moolenbroek rtsock_test_recv(struct sock * sock, size_t min __unused, size_t * size)
790*ef8d499eSDavid van Moolenbroek {
791*ef8d499eSDavid van Moolenbroek 	struct rtsock *rt = (struct rtsock *)sock;
792*ef8d499eSDavid van Moolenbroek 
793*ef8d499eSDavid van Moolenbroek 	if (rt->rt_rcvhead == NULL)
794*ef8d499eSDavid van Moolenbroek 		return SUSPEND;
795*ef8d499eSDavid van Moolenbroek 
796*ef8d499eSDavid van Moolenbroek 	if (size != NULL)
797*ef8d499eSDavid van Moolenbroek 		*size = rt->rt_rcvhead->tot_len;
798*ef8d499eSDavid van Moolenbroek 	return OK;
799*ef8d499eSDavid van Moolenbroek }
800*ef8d499eSDavid van Moolenbroek 
801*ef8d499eSDavid van Moolenbroek /*
802*ef8d499eSDavid van Moolenbroek  * Set socket options on a routing socket.
803*ef8d499eSDavid van Moolenbroek  */
804*ef8d499eSDavid van Moolenbroek static int
rtsock_setsockopt(struct sock * sock,int level,int name,const struct sockdriver_data * data,socklen_t len)805*ef8d499eSDavid van Moolenbroek rtsock_setsockopt(struct sock * sock, int level, int name,
806*ef8d499eSDavid van Moolenbroek 	const struct sockdriver_data * data, socklen_t len)
807*ef8d499eSDavid van Moolenbroek {
808*ef8d499eSDavid van Moolenbroek 	struct rtsock *rt = (struct rtsock *)sock;
809*ef8d499eSDavid van Moolenbroek 	int r, val;
810*ef8d499eSDavid van Moolenbroek 
811*ef8d499eSDavid van Moolenbroek 	if (level == SOL_SOCKET) {
812*ef8d499eSDavid van Moolenbroek 		switch (name) {
813*ef8d499eSDavid van Moolenbroek 		case SO_USELOOPBACK:
814*ef8d499eSDavid van Moolenbroek 			if ((r = sockdriver_copyin_opt(data, &val, sizeof(val),
815*ef8d499eSDavid van Moolenbroek 			    len)) != OK)
816*ef8d499eSDavid van Moolenbroek 				return r;
817*ef8d499eSDavid van Moolenbroek 
818*ef8d499eSDavid van Moolenbroek 			if (!val)
819*ef8d499eSDavid van Moolenbroek 				rt->rt_flags |= RTF_NOLOOPBACK;
820*ef8d499eSDavid van Moolenbroek 			else
821*ef8d499eSDavid van Moolenbroek 				rt->rt_flags &= ~RTF_NOLOOPBACK;
822*ef8d499eSDavid van Moolenbroek 
823*ef8d499eSDavid van Moolenbroek 			return OK;
824*ef8d499eSDavid van Moolenbroek 
825*ef8d499eSDavid van Moolenbroek 		case SO_RCVBUF:
826*ef8d499eSDavid van Moolenbroek 			if ((r = sockdriver_copyin_opt(data, &val, sizeof(val),
827*ef8d499eSDavid van Moolenbroek 			    len)) != OK)
828*ef8d499eSDavid van Moolenbroek 				return r;
829*ef8d499eSDavid van Moolenbroek 
830*ef8d499eSDavid van Moolenbroek 			if (val < RT_RCVBUF_MIN || val > RT_RCVBUF_MAX)
831*ef8d499eSDavid van Moolenbroek 				return EINVAL;
832*ef8d499eSDavid van Moolenbroek 
833*ef8d499eSDavid van Moolenbroek 			rt->rt_rcvbuf = (size_t)val;
834*ef8d499eSDavid van Moolenbroek 
835*ef8d499eSDavid van Moolenbroek 			return OK;
836*ef8d499eSDavid van Moolenbroek 		}
837*ef8d499eSDavid van Moolenbroek 	}
838*ef8d499eSDavid van Moolenbroek 
839*ef8d499eSDavid van Moolenbroek 	return ENOPROTOOPT;
840*ef8d499eSDavid van Moolenbroek }
841*ef8d499eSDavid van Moolenbroek 
842*ef8d499eSDavid van Moolenbroek /*
843*ef8d499eSDavid van Moolenbroek  * Retrieve socket options on a routing socket.
844*ef8d499eSDavid van Moolenbroek  */
845*ef8d499eSDavid van Moolenbroek static int
rtsock_getsockopt(struct sock * sock,int level,int name,const struct sockdriver_data * data,socklen_t * len)846*ef8d499eSDavid van Moolenbroek rtsock_getsockopt(struct sock * sock, int level, int name,
847*ef8d499eSDavid van Moolenbroek 	const struct sockdriver_data * data, socklen_t * len)
848*ef8d499eSDavid van Moolenbroek {
849*ef8d499eSDavid van Moolenbroek 	struct rtsock *rt = (struct rtsock *)sock;
850*ef8d499eSDavid van Moolenbroek 	int val;
851*ef8d499eSDavid van Moolenbroek 
852*ef8d499eSDavid van Moolenbroek 	if (level == SOL_SOCKET) {
853*ef8d499eSDavid van Moolenbroek 		switch (name) {
854*ef8d499eSDavid van Moolenbroek 		case SO_USELOOPBACK:
855*ef8d499eSDavid van Moolenbroek 			val = !(rt->rt_flags & RTF_NOLOOPBACK);
856*ef8d499eSDavid van Moolenbroek 
857*ef8d499eSDavid van Moolenbroek 			return sockdriver_copyout_opt(data, &val, sizeof(val),
858*ef8d499eSDavid van Moolenbroek 			    len);
859*ef8d499eSDavid van Moolenbroek 
860*ef8d499eSDavid van Moolenbroek 		case SO_RCVBUF:
861*ef8d499eSDavid van Moolenbroek 			val = rt->rt_rcvbuf;
862*ef8d499eSDavid van Moolenbroek 
863*ef8d499eSDavid van Moolenbroek 			return sockdriver_copyout_opt(data, &val, sizeof(val),
864*ef8d499eSDavid van Moolenbroek 			    len);
865*ef8d499eSDavid van Moolenbroek 		}
866*ef8d499eSDavid van Moolenbroek 	}
867*ef8d499eSDavid van Moolenbroek 
868*ef8d499eSDavid van Moolenbroek 	return ENOPROTOOPT;
869*ef8d499eSDavid van Moolenbroek }
870*ef8d499eSDavid van Moolenbroek 
871*ef8d499eSDavid van Moolenbroek /*
872*ef8d499eSDavid van Moolenbroek  * Retrieve the local or remote socket address of a routing socket.
873*ef8d499eSDavid van Moolenbroek  */
874*ef8d499eSDavid van Moolenbroek static int
rtsock_getname(struct sock * sock __unused,struct sockaddr * addr,socklen_t * addr_len)875*ef8d499eSDavid van Moolenbroek rtsock_getname(struct sock * sock __unused, struct sockaddr * addr,
876*ef8d499eSDavid van Moolenbroek 	socklen_t * addr_len)
877*ef8d499eSDavid van Moolenbroek {
878*ef8d499eSDavid van Moolenbroek 
879*ef8d499eSDavid van Moolenbroek 	/* This is entirely useless but apparently common between OSes. */
880*ef8d499eSDavid van Moolenbroek 	addr->sa_len = RTSOCK_ADDR_LEN;
881*ef8d499eSDavid van Moolenbroek 	addr->sa_family = AF_ROUTE;
882*ef8d499eSDavid van Moolenbroek 	*addr_len = RTSOCK_ADDR_LEN;
883*ef8d499eSDavid van Moolenbroek 
884*ef8d499eSDavid van Moolenbroek 	return OK;
885*ef8d499eSDavid van Moolenbroek }
886*ef8d499eSDavid van Moolenbroek 
887*ef8d499eSDavid van Moolenbroek /*
888*ef8d499eSDavid van Moolenbroek  * Drain the receive queue of a routing socket.
889*ef8d499eSDavid van Moolenbroek  */
890*ef8d499eSDavid van Moolenbroek static void
rtsock_drain(struct rtsock * rt)891*ef8d499eSDavid van Moolenbroek rtsock_drain(struct rtsock * rt)
892*ef8d499eSDavid van Moolenbroek {
893*ef8d499eSDavid van Moolenbroek 
894*ef8d499eSDavid van Moolenbroek 	while (rt->rt_rcvhead != NULL)
895*ef8d499eSDavid van Moolenbroek 		rtsock_dequeue(rt);
896*ef8d499eSDavid van Moolenbroek }
897*ef8d499eSDavid van Moolenbroek 
898*ef8d499eSDavid van Moolenbroek /*
899*ef8d499eSDavid van Moolenbroek  * Shut down a routing socket for reading and/or writing.
900*ef8d499eSDavid van Moolenbroek  */
901*ef8d499eSDavid van Moolenbroek static int
rtsock_shutdown(struct sock * sock,unsigned int mask)902*ef8d499eSDavid van Moolenbroek rtsock_shutdown(struct sock * sock, unsigned int mask)
903*ef8d499eSDavid van Moolenbroek {
904*ef8d499eSDavid van Moolenbroek 	struct rtsock *rt = (struct rtsock *)sock;
905*ef8d499eSDavid van Moolenbroek 
906*ef8d499eSDavid van Moolenbroek 	if (mask & SFL_SHUT_RD)
907*ef8d499eSDavid van Moolenbroek 		rtsock_drain(rt);
908*ef8d499eSDavid van Moolenbroek 
909*ef8d499eSDavid van Moolenbroek 	return OK;
910*ef8d499eSDavid van Moolenbroek }
911*ef8d499eSDavid van Moolenbroek 
912*ef8d499eSDavid van Moolenbroek /*
913*ef8d499eSDavid van Moolenbroek  * Close a routing socket.
914*ef8d499eSDavid van Moolenbroek  */
915*ef8d499eSDavid van Moolenbroek static int
rtsock_close(struct sock * sock,int force __unused)916*ef8d499eSDavid van Moolenbroek rtsock_close(struct sock * sock, int force __unused)
917*ef8d499eSDavid van Moolenbroek {
918*ef8d499eSDavid van Moolenbroek 	struct rtsock *rt = (struct rtsock *)sock;
919*ef8d499eSDavid van Moolenbroek 
920*ef8d499eSDavid van Moolenbroek 	rtsock_drain(rt);
921*ef8d499eSDavid van Moolenbroek 
922*ef8d499eSDavid van Moolenbroek 	return OK;
923*ef8d499eSDavid van Moolenbroek }
924*ef8d499eSDavid van Moolenbroek 
925*ef8d499eSDavid van Moolenbroek /*
926*ef8d499eSDavid van Moolenbroek  * Free up a closed routing socket.
927*ef8d499eSDavid van Moolenbroek  */
928*ef8d499eSDavid van Moolenbroek static void
rtsock_free(struct sock * sock)929*ef8d499eSDavid van Moolenbroek rtsock_free(struct sock * sock)
930*ef8d499eSDavid van Moolenbroek {
931*ef8d499eSDavid van Moolenbroek 	struct rtsock *rt = (struct rtsock *)sock;
932*ef8d499eSDavid van Moolenbroek 
933*ef8d499eSDavid van Moolenbroek 	TAILQ_REMOVE(&rt_activelist, rt, rt_next);
934*ef8d499eSDavid van Moolenbroek 
935*ef8d499eSDavid van Moolenbroek 	TAILQ_INSERT_HEAD(&rt_freelist, rt, rt_next);
936*ef8d499eSDavid van Moolenbroek }
937*ef8d499eSDavid van Moolenbroek 
938*ef8d499eSDavid van Moolenbroek static const struct sockevent_ops rtsock_ops = {
939*ef8d499eSDavid van Moolenbroek 	.sop_pre_send		= rtsock_pre_send,
940*ef8d499eSDavid van Moolenbroek 	.sop_send		= rtsock_send,
941*ef8d499eSDavid van Moolenbroek 	.sop_pre_recv		= rtsock_pre_recv,
942*ef8d499eSDavid van Moolenbroek 	.sop_recv		= rtsock_recv,
943*ef8d499eSDavid van Moolenbroek 	.sop_test_recv		= rtsock_test_recv,
944*ef8d499eSDavid van Moolenbroek 	.sop_setsockopt		= rtsock_setsockopt,
945*ef8d499eSDavid van Moolenbroek 	.sop_getsockopt		= rtsock_getsockopt,
946*ef8d499eSDavid van Moolenbroek 	.sop_getsockname	= rtsock_getname,
947*ef8d499eSDavid van Moolenbroek 	.sop_getpeername	= rtsock_getname,
948*ef8d499eSDavid van Moolenbroek 	.sop_shutdown		= rtsock_shutdown,
949*ef8d499eSDavid van Moolenbroek 	.sop_close		= rtsock_close,
950*ef8d499eSDavid van Moolenbroek 	.sop_free		= rtsock_free
951*ef8d499eSDavid van Moolenbroek };
952*ef8d499eSDavid van Moolenbroek 
953*ef8d499eSDavid van Moolenbroek /*
954*ef8d499eSDavid van Moolenbroek  * Send an interface announcement message about the given interface.  If
955*ef8d499eSDavid van Moolenbroek  * 'arrival' is set, the interface has just been created; otherwise, the
956*ef8d499eSDavid van Moolenbroek  * interface is about to be destroyed.
957*ef8d499eSDavid van Moolenbroek  */
958*ef8d499eSDavid van Moolenbroek void
rtsock_msg_ifannounce(struct ifdev * ifdev,int arrival)959*ef8d499eSDavid van Moolenbroek rtsock_msg_ifannounce(struct ifdev * ifdev, int arrival)
960*ef8d499eSDavid van Moolenbroek {
961*ef8d499eSDavid van Moolenbroek 	struct if_announcemsghdr ifan;
962*ef8d499eSDavid van Moolenbroek 	struct pbuf *pbuf;
963*ef8d499eSDavid van Moolenbroek 
964*ef8d499eSDavid van Moolenbroek 	if (!rtsock_msg_match(NULL /*rtsrc*/, AF_UNSPEC, NULL /*pbuf*/))
965*ef8d499eSDavid van Moolenbroek 		return;
966*ef8d499eSDavid van Moolenbroek 
967*ef8d499eSDavid van Moolenbroek 	memset(&ifan, 0, sizeof(ifan));
968*ef8d499eSDavid van Moolenbroek 	ifan.ifan_msglen = sizeof(ifan);
969*ef8d499eSDavid van Moolenbroek 	ifan.ifan_version = RTM_VERSION;
970*ef8d499eSDavid van Moolenbroek 	ifan.ifan_type = RTM_IFANNOUNCE;
971*ef8d499eSDavid van Moolenbroek 	ifan.ifan_index = ifdev_get_index(ifdev);
972*ef8d499eSDavid van Moolenbroek 	strlcpy(ifan.ifan_name, ifdev_get_name(ifdev), sizeof(ifan.ifan_name));
973*ef8d499eSDavid van Moolenbroek 	ifan.ifan_what = (arrival) ? IFAN_ARRIVAL : IFAN_DEPARTURE;
974*ef8d499eSDavid van Moolenbroek 
975*ef8d499eSDavid van Moolenbroek 	if ((pbuf = rtsock_alloc(sizeof(ifan))) == NULL)
976*ef8d499eSDavid van Moolenbroek 		return;
977*ef8d499eSDavid van Moolenbroek 	memcpy(pbuf->payload, &ifan, sizeof(ifan));
978*ef8d499eSDavid van Moolenbroek 
979*ef8d499eSDavid van Moolenbroek 	rtsock_msg_match(NULL /*rtsrc*/, AF_UNSPEC, pbuf);
980*ef8d499eSDavid van Moolenbroek }
981*ef8d499eSDavid van Moolenbroek 
982*ef8d499eSDavid van Moolenbroek /*
983*ef8d499eSDavid van Moolenbroek  * Send an interface information routing message.
984*ef8d499eSDavid van Moolenbroek  */
985*ef8d499eSDavid van Moolenbroek void
rtsock_msg_ifinfo(struct ifdev * ifdev)986*ef8d499eSDavid van Moolenbroek rtsock_msg_ifinfo(struct ifdev * ifdev)
987*ef8d499eSDavid van Moolenbroek {
988*ef8d499eSDavid van Moolenbroek 	struct if_msghdr ifm;
989*ef8d499eSDavid van Moolenbroek 	struct pbuf *pbuf;
990*ef8d499eSDavid van Moolenbroek 
991*ef8d499eSDavid van Moolenbroek 	if (!rtsock_msg_match(NULL /*rtsrc*/, AF_UNSPEC, NULL /*pbuf*/))
992*ef8d499eSDavid van Moolenbroek 		return;
993*ef8d499eSDavid van Moolenbroek 
994*ef8d499eSDavid van Moolenbroek 	memset(&ifm, 0, sizeof(ifm));
995*ef8d499eSDavid van Moolenbroek 	ifm.ifm_msglen = sizeof(ifm);
996*ef8d499eSDavid van Moolenbroek 	ifm.ifm_version = RTM_VERSION;
997*ef8d499eSDavid van Moolenbroek 	ifm.ifm_type = RTM_IFINFO;
998*ef8d499eSDavid van Moolenbroek 	ifm.ifm_addrs = 0;
999*ef8d499eSDavid van Moolenbroek 	ifm.ifm_flags = ifdev_get_ifflags(ifdev);
1000*ef8d499eSDavid van Moolenbroek 	ifm.ifm_index = ifdev_get_index(ifdev);
1001*ef8d499eSDavid van Moolenbroek 	memcpy(&ifm.ifm_data, ifdev_get_ifdata(ifdev), sizeof(ifm.ifm_data));
1002*ef8d499eSDavid van Moolenbroek 
1003*ef8d499eSDavid van Moolenbroek 	if ((pbuf = rtsock_alloc(sizeof(ifm))) == NULL)
1004*ef8d499eSDavid van Moolenbroek 		return;
1005*ef8d499eSDavid van Moolenbroek 	memcpy(pbuf->payload, &ifm, sizeof(ifm));
1006*ef8d499eSDavid van Moolenbroek 
1007*ef8d499eSDavid van Moolenbroek 	rtsock_msg_match(NULL /*rtsrc*/, AF_UNSPEC, pbuf);
1008*ef8d499eSDavid van Moolenbroek }
1009*ef8d499eSDavid van Moolenbroek 
1010*ef8d499eSDavid van Moolenbroek /*
1011*ef8d499eSDavid van Moolenbroek  * Set up a RTA map and an interface address structure for use in a RTM_xxxADDR
1012*ef8d499eSDavid van Moolenbroek  * routing message.
1013*ef8d499eSDavid van Moolenbroek  */
1014*ef8d499eSDavid van Moolenbroek static void
rtsock_rta_init_ifam(struct rtsock_rta * rta,struct ifa_msghdr * ifam,struct ifdev * ifdev,unsigned int type,struct sockaddr_dlx * sdlx)1015*ef8d499eSDavid van Moolenbroek rtsock_rta_init_ifam(struct rtsock_rta * rta, struct ifa_msghdr * ifam,
1016*ef8d499eSDavid van Moolenbroek 	struct ifdev * ifdev, unsigned int type, struct sockaddr_dlx * sdlx)
1017*ef8d499eSDavid van Moolenbroek {
1018*ef8d499eSDavid van Moolenbroek 
1019*ef8d499eSDavid van Moolenbroek 	memset(ifam, 0, sizeof(*ifam));
1020*ef8d499eSDavid van Moolenbroek 	ifam->ifam_version = RTM_VERSION;
1021*ef8d499eSDavid van Moolenbroek 	ifam->ifam_type = type;
1022*ef8d499eSDavid van Moolenbroek 	ifam->ifam_flags = 0;
1023*ef8d499eSDavid van Moolenbroek 	ifam->ifam_index = ifdev_get_index(ifdev);
1024*ef8d499eSDavid van Moolenbroek 	ifam->ifam_metric = ifdev_get_metric(ifdev);
1025*ef8d499eSDavid van Moolenbroek 
1026*ef8d499eSDavid van Moolenbroek 	rtsock_rta_init(rta);
1027*ef8d499eSDavid van Moolenbroek 
1028*ef8d499eSDavid van Moolenbroek 	ifaddr_dl_get(ifdev, (ifaddr_dl_num_t)0, sdlx);
1029*ef8d499eSDavid van Moolenbroek 
1030*ef8d499eSDavid van Moolenbroek 	rtsock_rta_set(rta, RTAX_IFP, sdlx, sdlx->sdlx_len);
1031*ef8d499eSDavid van Moolenbroek }
1032*ef8d499eSDavid van Moolenbroek 
1033*ef8d499eSDavid van Moolenbroek /*
1034*ef8d499eSDavid van Moolenbroek  * Add a specific link-layer address for an interface to the given RTA map.
1035*ef8d499eSDavid van Moolenbroek  */
1036*ef8d499eSDavid van Moolenbroek static void
rtsock_rta_add_dl(struct rtsock_rta * rta,struct ifdev * ifdev,ifaddr_dl_num_t num,struct sockaddr_dlx * sdlx)1037*ef8d499eSDavid van Moolenbroek rtsock_rta_add_dl(struct rtsock_rta * rta, struct ifdev * ifdev,
1038*ef8d499eSDavid van Moolenbroek 	ifaddr_dl_num_t num, struct sockaddr_dlx * sdlx)
1039*ef8d499eSDavid van Moolenbroek {
1040*ef8d499eSDavid van Moolenbroek 
1041*ef8d499eSDavid van Moolenbroek 	/* Obtain the address data. */
1042*ef8d499eSDavid van Moolenbroek 	ifaddr_dl_get(ifdev, num, sdlx);
1043*ef8d499eSDavid van Moolenbroek 
1044*ef8d499eSDavid van Moolenbroek 	/* Add the interface address. */
1045*ef8d499eSDavid van Moolenbroek 	rtsock_rta_set(rta, RTAX_IFA, sdlx, sdlx->sdlx_len);
1046*ef8d499eSDavid van Moolenbroek 
1047*ef8d499eSDavid van Moolenbroek 	/*
1048*ef8d499eSDavid van Moolenbroek 	 * NetBSD also adds a RTAX_NETMASK entry here.  At this moment it is
1049*ef8d499eSDavid van Moolenbroek 	 * not clear to me why, and it is a pain to make, so for now we do not.
1050*ef8d499eSDavid van Moolenbroek 	 */
1051*ef8d499eSDavid van Moolenbroek }
1052*ef8d499eSDavid van Moolenbroek 
1053*ef8d499eSDavid van Moolenbroek /*
1054*ef8d499eSDavid van Moolenbroek  * Send a routing message about a new, changed, or deleted datalink address for
1055*ef8d499eSDavid van Moolenbroek  * the given interface.
1056*ef8d499eSDavid van Moolenbroek  */
1057*ef8d499eSDavid van Moolenbroek void
rtsock_msg_addr_dl(struct ifdev * ifdev,unsigned int type,ifaddr_dl_num_t num)1058*ef8d499eSDavid van Moolenbroek rtsock_msg_addr_dl(struct ifdev * ifdev, unsigned int type,
1059*ef8d499eSDavid van Moolenbroek 	ifaddr_dl_num_t num)
1060*ef8d499eSDavid van Moolenbroek {
1061*ef8d499eSDavid van Moolenbroek 	struct rtsock_rta rta;
1062*ef8d499eSDavid van Moolenbroek 	struct ifa_msghdr ifam;
1063*ef8d499eSDavid van Moolenbroek 	struct sockaddr_dlx name, addr;
1064*ef8d499eSDavid van Moolenbroek 	struct pbuf *pbuf;
1065*ef8d499eSDavid van Moolenbroek 
1066*ef8d499eSDavid van Moolenbroek 	if (!rtsock_msg_match(NULL /*rtsrc*/, AF_LINK, NULL /*pbuf*/))
1067*ef8d499eSDavid van Moolenbroek 		return;
1068*ef8d499eSDavid van Moolenbroek 
1069*ef8d499eSDavid van Moolenbroek 	rtsock_rta_init_ifam(&rta, &ifam, ifdev, type, &name);
1070*ef8d499eSDavid van Moolenbroek 
1071*ef8d499eSDavid van Moolenbroek 	rtsock_rta_add_dl(&rta, ifdev, num, &addr);
1072*ef8d499eSDavid van Moolenbroek 
1073*ef8d499eSDavid van Moolenbroek 	if (rtsock_rta_finalize(&ifam, sizeof(ifam), &ifam.ifam_msglen,
1074*ef8d499eSDavid van Moolenbroek 	    &ifam.ifam_addrs, &rta, &pbuf, NULL, 0) > 0)
1075*ef8d499eSDavid van Moolenbroek 		rtsock_msg_match(NULL /*rtsrc*/, AF_LINK, pbuf);
1076*ef8d499eSDavid van Moolenbroek }
1077*ef8d499eSDavid van Moolenbroek 
1078*ef8d499eSDavid van Moolenbroek /*
1079*ef8d499eSDavid van Moolenbroek  * Add a specific IPv4 address for an interface to the given RTA map.
1080*ef8d499eSDavid van Moolenbroek  */
1081*ef8d499eSDavid van Moolenbroek static void
rtsock_rta_add_v4(struct rtsock_rta * rta,struct ifdev * ifdev,ifaddr_v4_num_t num,struct sockaddr_in sin[4])1082*ef8d499eSDavid van Moolenbroek rtsock_rta_add_v4(struct rtsock_rta * rta, struct ifdev * ifdev,
1083*ef8d499eSDavid van Moolenbroek 	ifaddr_v4_num_t num, struct sockaddr_in sin[4])
1084*ef8d499eSDavid van Moolenbroek {
1085*ef8d499eSDavid van Moolenbroek 
1086*ef8d499eSDavid van Moolenbroek 	/* Obtain the address data. */
1087*ef8d499eSDavid van Moolenbroek 	(void)ifaddr_v4_get(ifdev, num, &sin[0], &sin[1], &sin[2], &sin[3]);
1088*ef8d499eSDavid van Moolenbroek 
1089*ef8d499eSDavid van Moolenbroek 	/* Add the interface address. */
1090*ef8d499eSDavid van Moolenbroek 	rtsock_rta_set(rta, RTAX_IFA, &sin[0], sin[0].sin_len);
1091*ef8d499eSDavid van Moolenbroek 
1092*ef8d499eSDavid van Moolenbroek 	/* Add the netmask, after compressing it. */
1093*ef8d499eSDavid van Moolenbroek 	rtsock_compress_netmask((struct sockaddr *)&sin[1]);
1094*ef8d499eSDavid van Moolenbroek 
1095*ef8d499eSDavid van Moolenbroek 	rtsock_rta_set(rta, RTAX_NETMASK, &sin[1], sin[1].sin_len);
1096*ef8d499eSDavid van Moolenbroek 
1097*ef8d499eSDavid van Moolenbroek 	/* Possibly add a broadcast or destination address. */
1098*ef8d499eSDavid van Moolenbroek 	if (sin[2].sin_len != 0)
1099*ef8d499eSDavid van Moolenbroek 		rtsock_rta_set(rta, RTAX_BRD, &sin[2], sin[2].sin_len);
1100*ef8d499eSDavid van Moolenbroek 	else if (sin[3].sin_len != 0)
1101*ef8d499eSDavid van Moolenbroek 		rtsock_rta_set(rta, RTAX_DST, &sin[3], sin[3].sin_len);
1102*ef8d499eSDavid van Moolenbroek }
1103*ef8d499eSDavid van Moolenbroek 
1104*ef8d499eSDavid van Moolenbroek /*
1105*ef8d499eSDavid van Moolenbroek  * Send a routing message about a new or deleted IPv4 address for the given
1106*ef8d499eSDavid van Moolenbroek  * interface.
1107*ef8d499eSDavid van Moolenbroek  */
1108*ef8d499eSDavid van Moolenbroek void
rtsock_msg_addr_v4(struct ifdev * ifdev,unsigned int type,ifaddr_v4_num_t num)1109*ef8d499eSDavid van Moolenbroek rtsock_msg_addr_v4(struct ifdev * ifdev, unsigned int type,
1110*ef8d499eSDavid van Moolenbroek 	ifaddr_v4_num_t num)
1111*ef8d499eSDavid van Moolenbroek {
1112*ef8d499eSDavid van Moolenbroek 	struct rtsock_rta rta;
1113*ef8d499eSDavid van Moolenbroek 	struct ifa_msghdr ifam;
1114*ef8d499eSDavid van Moolenbroek 	struct sockaddr_dlx name;
1115*ef8d499eSDavid van Moolenbroek 	struct sockaddr_in sin[4];
1116*ef8d499eSDavid van Moolenbroek 	struct pbuf *pbuf;
1117*ef8d499eSDavid van Moolenbroek 
1118*ef8d499eSDavid van Moolenbroek 	if (!rtsock_msg_match(NULL /*rtsrc*/, AF_INET, NULL /*pbuf*/))
1119*ef8d499eSDavid van Moolenbroek 		return;
1120*ef8d499eSDavid van Moolenbroek 
1121*ef8d499eSDavid van Moolenbroek 	rtsock_rta_init_ifam(&rta, &ifam, ifdev, type, &name);
1122*ef8d499eSDavid van Moolenbroek 
1123*ef8d499eSDavid van Moolenbroek 	rtsock_rta_add_v4(&rta, ifdev, num, sin);
1124*ef8d499eSDavid van Moolenbroek 
1125*ef8d499eSDavid van Moolenbroek 	if (rtsock_rta_finalize(&ifam, sizeof(ifam), &ifam.ifam_msglen,
1126*ef8d499eSDavid van Moolenbroek 	    &ifam.ifam_addrs, &rta, &pbuf, NULL, 0) > 0)
1127*ef8d499eSDavid van Moolenbroek 		rtsock_msg_match(NULL /*rtsrc*/, AF_INET, pbuf);
1128*ef8d499eSDavid van Moolenbroek }
1129*ef8d499eSDavid van Moolenbroek 
1130*ef8d499eSDavid van Moolenbroek /*
1131*ef8d499eSDavid van Moolenbroek  * Add a specific IPv6 address for an interface to the given RTA map.
1132*ef8d499eSDavid van Moolenbroek  */
1133*ef8d499eSDavid van Moolenbroek static void
rtsock_rta_add_v6(struct rtsock_rta * rta,struct ifdev * ifdev,ifaddr_v6_num_t num,struct sockaddr_in6 sin6[3])1134*ef8d499eSDavid van Moolenbroek rtsock_rta_add_v6(struct rtsock_rta * rta, struct ifdev * ifdev,
1135*ef8d499eSDavid van Moolenbroek 	ifaddr_v6_num_t num, struct sockaddr_in6 sin6[3])
1136*ef8d499eSDavid van Moolenbroek {
1137*ef8d499eSDavid van Moolenbroek 
1138*ef8d499eSDavid van Moolenbroek 	/* Obtain the address data. */
1139*ef8d499eSDavid van Moolenbroek 	ifaddr_v6_get(ifdev, num, &sin6[0], &sin6[1], &sin6[2]);
1140*ef8d499eSDavid van Moolenbroek 
1141*ef8d499eSDavid van Moolenbroek 	/* Add the interface address. */
1142*ef8d499eSDavid van Moolenbroek 	rtsock_rta_set(rta, RTAX_IFA, &sin6[0], sin6[0].sin6_len);
1143*ef8d499eSDavid van Moolenbroek 
1144*ef8d499eSDavid van Moolenbroek 	/* Add the netmask, after compressing it (a no-op at the moment). */
1145*ef8d499eSDavid van Moolenbroek 	rtsock_compress_netmask((struct sockaddr *)&sin6[1]);
1146*ef8d499eSDavid van Moolenbroek 
1147*ef8d499eSDavid van Moolenbroek 	rtsock_rta_set(rta, RTAX_NETMASK, &sin6[1], sin6[1].sin6_len);
1148*ef8d499eSDavid van Moolenbroek 
1149*ef8d499eSDavid van Moolenbroek 	/* Possibly add a destination address. */
1150*ef8d499eSDavid van Moolenbroek 	if (sin6[2].sin6_len != 0)
1151*ef8d499eSDavid van Moolenbroek 		rtsock_rta_set(rta, RTAX_DST, &sin6[2], sin6[2].sin6_len);
1152*ef8d499eSDavid van Moolenbroek }
1153*ef8d499eSDavid van Moolenbroek 
1154*ef8d499eSDavid van Moolenbroek /*
1155*ef8d499eSDavid van Moolenbroek  * Send a routing message about a new or deleted IPv6 address for the given
1156*ef8d499eSDavid van Moolenbroek  * interface.
1157*ef8d499eSDavid van Moolenbroek  */
1158*ef8d499eSDavid van Moolenbroek void
rtsock_msg_addr_v6(struct ifdev * ifdev,unsigned int type,ifaddr_v6_num_t num)1159*ef8d499eSDavid van Moolenbroek rtsock_msg_addr_v6(struct ifdev * ifdev, unsigned int type,
1160*ef8d499eSDavid van Moolenbroek 	ifaddr_v6_num_t num)
1161*ef8d499eSDavid van Moolenbroek {
1162*ef8d499eSDavid van Moolenbroek 	struct rtsock_rta rta;
1163*ef8d499eSDavid van Moolenbroek 	struct ifa_msghdr ifam;
1164*ef8d499eSDavid van Moolenbroek 	struct sockaddr_dlx name;
1165*ef8d499eSDavid van Moolenbroek 	struct sockaddr_in6 sin6[3];
1166*ef8d499eSDavid van Moolenbroek 	struct pbuf *pbuf;
1167*ef8d499eSDavid van Moolenbroek 
1168*ef8d499eSDavid van Moolenbroek 	if (!rtsock_msg_match(NULL /*rtsrc*/, AF_INET6, NULL /*pbuf*/))
1169*ef8d499eSDavid van Moolenbroek 		return;
1170*ef8d499eSDavid van Moolenbroek 
1171*ef8d499eSDavid van Moolenbroek 	rtsock_rta_init_ifam(&rta, &ifam, ifdev, type, &name);
1172*ef8d499eSDavid van Moolenbroek 
1173*ef8d499eSDavid van Moolenbroek 	rtsock_rta_add_v6(&rta, ifdev, num, sin6);
1174*ef8d499eSDavid van Moolenbroek 
1175*ef8d499eSDavid van Moolenbroek 	if (rtsock_rta_finalize(&ifam, sizeof(ifam), &ifam.ifam_msglen,
1176*ef8d499eSDavid van Moolenbroek 	    &ifam.ifam_addrs, &rta, &pbuf, NULL, 0) > 0)
1177*ef8d499eSDavid van Moolenbroek 		rtsock_msg_match(NULL /*rtsrc*/, AF_INET6, pbuf);
1178*ef8d499eSDavid van Moolenbroek }
1179*ef8d499eSDavid van Moolenbroek 
1180*ef8d499eSDavid van Moolenbroek /*
1181*ef8d499eSDavid van Moolenbroek  * Send an RTM_MISS routing message about an address for which no route was
1182*ef8d499eSDavid van Moolenbroek  * found.  The caller must provide the address in the appropriate form and
1183*ef8d499eSDavid van Moolenbroek  * perform any per-address rate limiting.
1184*ef8d499eSDavid van Moolenbroek  */
1185*ef8d499eSDavid van Moolenbroek void
rtsock_msg_miss(const struct sockaddr * addr)1186*ef8d499eSDavid van Moolenbroek rtsock_msg_miss(const struct sockaddr * addr)
1187*ef8d499eSDavid van Moolenbroek {
1188*ef8d499eSDavid van Moolenbroek 	struct rt_msghdr rtm;
1189*ef8d499eSDavid van Moolenbroek 	struct rtsock_rta rta;
1190*ef8d499eSDavid van Moolenbroek 	struct pbuf *pbuf;
1191*ef8d499eSDavid van Moolenbroek 
1192*ef8d499eSDavid van Moolenbroek 	/*
1193*ef8d499eSDavid van Moolenbroek 	 * Unfortunately the destination address has already been generated (as
1194*ef8d499eSDavid van Moolenbroek 	 * 'addr'), which is a big part of the work.  Still, skip the rest if
1195*ef8d499eSDavid van Moolenbroek 	 * there is no routing socket to deliver the message to.
1196*ef8d499eSDavid van Moolenbroek 	 */
1197*ef8d499eSDavid van Moolenbroek 	if (!rtsock_msg_match(NULL /*rtsrc*/, addr->sa_family, NULL /*pbuf*/))
1198*ef8d499eSDavid van Moolenbroek 		return;
1199*ef8d499eSDavid van Moolenbroek 
1200*ef8d499eSDavid van Moolenbroek 	memset(&rtm, 0, sizeof(rtm));
1201*ef8d499eSDavid van Moolenbroek 	rtm.rtm_version = RTM_VERSION;
1202*ef8d499eSDavid van Moolenbroek 	rtm.rtm_type = RTM_MISS;
1203*ef8d499eSDavid van Moolenbroek 
1204*ef8d499eSDavid van Moolenbroek 	rtsock_rta_init(&rta);
1205*ef8d499eSDavid van Moolenbroek 
1206*ef8d499eSDavid van Moolenbroek 	rtsock_rta_set(&rta, RTAX_DST, addr, addr->sa_len);
1207*ef8d499eSDavid van Moolenbroek 
1208*ef8d499eSDavid van Moolenbroek 	if (rtsock_rta_finalize(&rtm, sizeof(rtm), &rtm.rtm_msglen,
1209*ef8d499eSDavid van Moolenbroek 	    &rtm.rtm_addrs, &rta, &pbuf, NULL, 0) > 0)
1210*ef8d499eSDavid van Moolenbroek 		rtsock_msg_match(NULL /*rtsrc*/, addr->sa_family, pbuf);
1211*ef8d499eSDavid van Moolenbroek }
1212*ef8d499eSDavid van Moolenbroek 
1213*ef8d499eSDavid van Moolenbroek /*
1214*ef8d499eSDavid van Moolenbroek  * Generate routing socket data for a route, for either routing socket
1215*ef8d499eSDavid van Moolenbroek  * broadcasting or a sysctl(7) request.  The route is given as 'route'.  The
1216*ef8d499eSDavid van Moolenbroek  * type of the message (RTM_) is given as 'type'.  The resulting routing
1217*ef8d499eSDavid van Moolenbroek  * message header is stored in 'rtm' and an address vector is stored in 'rta'.
1218*ef8d499eSDavid van Moolenbroek  * The latter may point to addresses generated in 'addr', 'mask', 'gateway',
1219*ef8d499eSDavid van Moolenbroek  * and optionally (if not NULL) 'ifp' and 'ifa'.  The caller is responsible for
1220*ef8d499eSDavid van Moolenbroek  * combining the results into an appropriate routing message.
1221*ef8d499eSDavid van Moolenbroek  */
1222*ef8d499eSDavid van Moolenbroek static void
rtsock_get_route(struct rt_msghdr * rtm,struct rtsock_rta * rta,union sockaddr_any * addr,union sockaddr_any * mask,union sockaddr_any * gateway,union sockaddr_any * ifp,union sockaddr_any * ifa,const struct route_entry * route,unsigned int type)1223*ef8d499eSDavid van Moolenbroek rtsock_get_route(struct rt_msghdr * rtm, struct rtsock_rta * rta,
1224*ef8d499eSDavid van Moolenbroek 	union sockaddr_any * addr, union sockaddr_any * mask,
1225*ef8d499eSDavid van Moolenbroek 	union sockaddr_any * gateway, union sockaddr_any * ifp,
1226*ef8d499eSDavid van Moolenbroek 	union sockaddr_any * ifa, const struct route_entry * route,
1227*ef8d499eSDavid van Moolenbroek 	unsigned int type)
1228*ef8d499eSDavid van Moolenbroek {
1229*ef8d499eSDavid van Moolenbroek 	struct ifdev *ifdev;
1230*ef8d499eSDavid van Moolenbroek 	unsigned int flags, use;
1231*ef8d499eSDavid van Moolenbroek 
1232*ef8d499eSDavid van Moolenbroek 	route_get(route, addr, mask, gateway, ifp, ifa, &ifdev, &flags, &use);
1233*ef8d499eSDavid van Moolenbroek 
1234*ef8d499eSDavid van Moolenbroek 	memset(rtm, 0, sizeof(*rtm));
1235*ef8d499eSDavid van Moolenbroek 	rtm->rtm_version = RTM_VERSION;
1236*ef8d499eSDavid van Moolenbroek 	rtm->rtm_type = type;
1237*ef8d499eSDavid van Moolenbroek 	rtm->rtm_flags = flags;
1238*ef8d499eSDavid van Moolenbroek 	rtm->rtm_index = ifdev_get_index(ifdev);
1239*ef8d499eSDavid van Moolenbroek 	rtm->rtm_use = use;
1240*ef8d499eSDavid van Moolenbroek 
1241*ef8d499eSDavid van Moolenbroek 	rtsock_rta_init(rta);
1242*ef8d499eSDavid van Moolenbroek 
1243*ef8d499eSDavid van Moolenbroek 	rtsock_rta_set(rta, RTAX_DST, addr, addr->sa.sa_len);
1244*ef8d499eSDavid van Moolenbroek 
1245*ef8d499eSDavid van Moolenbroek 	if (!(flags & RTF_HOST)) {
1246*ef8d499eSDavid van Moolenbroek 		rtsock_compress_netmask(&mask->sa);
1247*ef8d499eSDavid van Moolenbroek 
1248*ef8d499eSDavid van Moolenbroek 		rtsock_rta_set(rta, RTAX_NETMASK, mask, mask->sa.sa_len);
1249*ef8d499eSDavid van Moolenbroek 	}
1250*ef8d499eSDavid van Moolenbroek 
1251*ef8d499eSDavid van Moolenbroek 	rtsock_rta_set(rta, RTAX_GATEWAY, gateway, gateway->sa.sa_len);
1252*ef8d499eSDavid van Moolenbroek 
1253*ef8d499eSDavid van Moolenbroek 	if (ifp != NULL)
1254*ef8d499eSDavid van Moolenbroek 		rtsock_rta_set(rta, RTAX_IFP, ifp, ifp->sa.sa_len);
1255*ef8d499eSDavid van Moolenbroek 
1256*ef8d499eSDavid van Moolenbroek 	if (ifa != NULL)
1257*ef8d499eSDavid van Moolenbroek 		rtsock_rta_set(rta, RTAX_IFA, ifa, ifa->sa.sa_len);
1258*ef8d499eSDavid van Moolenbroek }
1259*ef8d499eSDavid van Moolenbroek 
1260*ef8d499eSDavid van Moolenbroek /*
1261*ef8d499eSDavid van Moolenbroek  * Send a routing message about a route, with the given type which may be one
1262*ef8d499eSDavid van Moolenbroek  * of RTM_ADD, RTM_CHANGE, RTM_DELETE, RTM_LOCK, and RTM_GET.  The routing
1263*ef8d499eSDavid van Moolenbroek  * socket request information 'rtr', if not NULL, provides additional
1264*ef8d499eSDavid van Moolenbroek  * information about the routing socket that was the source of the request (if
1265*ef8d499eSDavid van Moolenbroek  * any), various fields that should be echoed, and (for RTM_GET) whether to
1266*ef8d499eSDavid van Moolenbroek  * add interface information to the output.
1267*ef8d499eSDavid van Moolenbroek  */
1268*ef8d499eSDavid van Moolenbroek void
rtsock_msg_route(const struct route_entry * route,unsigned int type,const struct rtsock_request * rtr)1269*ef8d499eSDavid van Moolenbroek rtsock_msg_route(const struct route_entry * route, unsigned int type,
1270*ef8d499eSDavid van Moolenbroek 	const struct rtsock_request * rtr)
1271*ef8d499eSDavid van Moolenbroek {
1272*ef8d499eSDavid van Moolenbroek 	union sockaddr_any addr, mask, gateway, ifp, ifa;
1273*ef8d499eSDavid van Moolenbroek 	struct rt_msghdr rtm;
1274*ef8d499eSDavid van Moolenbroek 	struct rtsock_rta rta;
1275*ef8d499eSDavid van Moolenbroek 	struct rtsock *rtsrc;
1276*ef8d499eSDavid van Moolenbroek 	struct pbuf *pbuf;
1277*ef8d499eSDavid van Moolenbroek 	int family, getif;
1278*ef8d499eSDavid van Moolenbroek 
1279*ef8d499eSDavid van Moolenbroek 	rtsrc = (rtr != NULL) ? rtr->rtr_src : NULL;
1280*ef8d499eSDavid van Moolenbroek 	family = (route_is_ipv6(route)) ? AF_INET6 : AF_INET;
1281*ef8d499eSDavid van Moolenbroek 
1282*ef8d499eSDavid van Moolenbroek 	if (!rtsock_msg_match(rtsrc, family, NULL /*pbuf*/))
1283*ef8d499eSDavid van Moolenbroek 		return;
1284*ef8d499eSDavid van Moolenbroek 
1285*ef8d499eSDavid van Moolenbroek 	getif = (rtr != NULL && rtr->rtr_getif);
1286*ef8d499eSDavid van Moolenbroek 
1287*ef8d499eSDavid van Moolenbroek 	rtsock_get_route(&rtm, &rta, &addr, &mask, &gateway,
1288*ef8d499eSDavid van Moolenbroek 	    (getif) ? &ifp : NULL, (getif) ? &ifa : NULL, route, type);
1289*ef8d499eSDavid van Moolenbroek 
1290*ef8d499eSDavid van Moolenbroek 	if (rtr != NULL) {
1291*ef8d499eSDavid van Moolenbroek 		rtm.rtm_flags |= RTF_DONE;
1292*ef8d499eSDavid van Moolenbroek 		rtm.rtm_pid = rtr->rtr_pid;
1293*ef8d499eSDavid van Moolenbroek 		rtm.rtm_seq = rtr->rtr_seq;
1294*ef8d499eSDavid van Moolenbroek 	}
1295*ef8d499eSDavid van Moolenbroek 
1296*ef8d499eSDavid van Moolenbroek 	if (rtsock_rta_finalize(&rtm, sizeof(rtm), &rtm.rtm_msglen,
1297*ef8d499eSDavid van Moolenbroek 	    &rtm.rtm_addrs, &rta, &pbuf, NULL, 0) > 0)
1298*ef8d499eSDavid van Moolenbroek 		rtsock_msg_match(rtsrc, family, pbuf);
1299*ef8d499eSDavid van Moolenbroek }
1300*ef8d499eSDavid van Moolenbroek 
1301*ef8d499eSDavid van Moolenbroek /*
1302*ef8d499eSDavid van Moolenbroek  * Generate sysctl(7) output or length for the given routing table entry
1303*ef8d499eSDavid van Moolenbroek  * 'route', provided that the route passes the flags filter 'filter'.  The
1304*ef8d499eSDavid van Moolenbroek  * address length 'addr_len' is used to compute a cheap length estimate.  On
1305*ef8d499eSDavid van Moolenbroek  * success, return the byte size of the output.  If the route was not a match
1306*ef8d499eSDavid van Moolenbroek  * for the filter, return zero.  On failure, return a negative error code.
1307*ef8d499eSDavid van Moolenbroek  */
1308*ef8d499eSDavid van Moolenbroek static ssize_t
rtsock_info_rtable_entry(const struct route_entry * route,unsigned int filter,socklen_t addr_len,struct rmib_oldp * oldp,size_t off)1309*ef8d499eSDavid van Moolenbroek rtsock_info_rtable_entry(const struct route_entry * route, unsigned int filter,
1310*ef8d499eSDavid van Moolenbroek 	socklen_t addr_len, struct rmib_oldp * oldp, size_t off)
1311*ef8d499eSDavid van Moolenbroek {
1312*ef8d499eSDavid van Moolenbroek 	union sockaddr_any addr, mask, gateway;
1313*ef8d499eSDavid van Moolenbroek 	struct rt_msghdr rtm;
1314*ef8d499eSDavid van Moolenbroek 	struct rtsock_rta rta;
1315*ef8d499eSDavid van Moolenbroek 	unsigned int flags;
1316*ef8d499eSDavid van Moolenbroek 	ssize_t len;
1317*ef8d499eSDavid van Moolenbroek 
1318*ef8d499eSDavid van Moolenbroek 	flags = route_get_flags(route);
1319*ef8d499eSDavid van Moolenbroek 
1320*ef8d499eSDavid van Moolenbroek 	/* Apparently, matching any of the flags (if given) is sufficient. */
1321*ef8d499eSDavid van Moolenbroek 	if (filter != 0 && (filter & flags) != 0)
1322*ef8d499eSDavid van Moolenbroek 		return 0;
1323*ef8d499eSDavid van Moolenbroek 
1324*ef8d499eSDavid van Moolenbroek 	/* Size (over)estimation shortcut. */
1325*ef8d499eSDavid van Moolenbroek 	if (oldp == NULL) {
1326*ef8d499eSDavid van Moolenbroek 		len = sizeof(rtm) + RT_ROUNDUP(addr_len) +
1327*ef8d499eSDavid van Moolenbroek 		    RT_ROUNDUP(sizeof(gateway));
1328*ef8d499eSDavid van Moolenbroek 
1329*ef8d499eSDavid van Moolenbroek 		if (!(flags & RTF_HOST))
1330*ef8d499eSDavid van Moolenbroek 			len += RT_ROUNDUP(addr_len);
1331*ef8d499eSDavid van Moolenbroek 
1332*ef8d499eSDavid van Moolenbroek 		return len;
1333*ef8d499eSDavid van Moolenbroek 	}
1334*ef8d499eSDavid van Moolenbroek 
1335*ef8d499eSDavid van Moolenbroek 	rtsock_get_route(&rtm, &rta, &addr, &mask, &gateway, NULL /*ifp*/,
1336*ef8d499eSDavid van Moolenbroek 	    NULL /*ifa*/, route, RTM_GET);
1337*ef8d499eSDavid van Moolenbroek 
1338*ef8d499eSDavid van Moolenbroek 	return rtsock_rta_finalize(&rtm, sizeof(rtm), &rtm.rtm_msglen,
1339*ef8d499eSDavid van Moolenbroek 	    &rtm.rtm_addrs, &rta, NULL /*pbuf*/, oldp, off);
1340*ef8d499eSDavid van Moolenbroek }
1341*ef8d499eSDavid van Moolenbroek 
1342*ef8d499eSDavid van Moolenbroek /*
1343*ef8d499eSDavid van Moolenbroek  * Obtain routing table entries.
1344*ef8d499eSDavid van Moolenbroek  */
1345*ef8d499eSDavid van Moolenbroek static ssize_t
rtsock_info_rtable(struct rmib_oldp * oldp,int family,int filter)1346*ef8d499eSDavid van Moolenbroek rtsock_info_rtable(struct rmib_oldp * oldp, int family, int filter)
1347*ef8d499eSDavid van Moolenbroek {
1348*ef8d499eSDavid van Moolenbroek 	struct route_entry *route;
1349*ef8d499eSDavid van Moolenbroek 	ssize_t r, off;
1350*ef8d499eSDavid van Moolenbroek 
1351*ef8d499eSDavid van Moolenbroek 	off = 0;
1352*ef8d499eSDavid van Moolenbroek 
1353*ef8d499eSDavid van Moolenbroek 	if (family == AF_UNSPEC || family == AF_INET) {
1354*ef8d499eSDavid van Moolenbroek 		for (route = NULL; (route = route_enum_v4(route)) != NULL; ) {
1355*ef8d499eSDavid van Moolenbroek 			if ((r = rtsock_info_rtable_entry(route,
1356*ef8d499eSDavid van Moolenbroek 			    (unsigned int)filter, sizeof(struct sockaddr_in),
1357*ef8d499eSDavid van Moolenbroek 			    oldp, off)) < 0)
1358*ef8d499eSDavid van Moolenbroek 				return r;
1359*ef8d499eSDavid van Moolenbroek 			off += r;
1360*ef8d499eSDavid van Moolenbroek 		}
1361*ef8d499eSDavid van Moolenbroek 	}
1362*ef8d499eSDavid van Moolenbroek 
1363*ef8d499eSDavid van Moolenbroek 	if (family == AF_UNSPEC || family == AF_INET6) {
1364*ef8d499eSDavid van Moolenbroek 		for (route = NULL; (route = route_enum_v6(route)) != NULL; ) {
1365*ef8d499eSDavid van Moolenbroek 			if ((r = rtsock_info_rtable_entry(route,
1366*ef8d499eSDavid van Moolenbroek 			    (unsigned int)filter, sizeof(struct sockaddr_in6),
1367*ef8d499eSDavid van Moolenbroek 			    oldp, off)) < 0)
1368*ef8d499eSDavid van Moolenbroek 				return r;
1369*ef8d499eSDavid van Moolenbroek 			off += r;
1370*ef8d499eSDavid van Moolenbroek 		}
1371*ef8d499eSDavid van Moolenbroek 	}
1372*ef8d499eSDavid van Moolenbroek 
1373*ef8d499eSDavid van Moolenbroek 	/* TODO: should we add slack here? */
1374*ef8d499eSDavid van Moolenbroek 	return off;
1375*ef8d499eSDavid van Moolenbroek }
1376*ef8d499eSDavid van Moolenbroek 
1377*ef8d499eSDavid van Moolenbroek /*
1378*ef8d499eSDavid van Moolenbroek  * Generate routing socket data for an ARP table entry, for either routing
1379*ef8d499eSDavid van Moolenbroek  * socket broadcasting or a sysctl(7) request.  The ARP table entry number is
1380*ef8d499eSDavid van Moolenbroek  * given as 'num'.  The type of the message (RTM_) is given as 'type'.  The
1381*ef8d499eSDavid van Moolenbroek  * resulting routing message header is stored in 'rtm' and an address vector is
1382*ef8d499eSDavid van Moolenbroek  * stored in 'rta'.  The latter may point to addresses generated in 'addr' and
1383*ef8d499eSDavid van Moolenbroek  * 'gateway'.  The caller is responsible for combining the results into an
1384*ef8d499eSDavid van Moolenbroek  * appropriate routing message.
1385*ef8d499eSDavid van Moolenbroek  */
1386*ef8d499eSDavid van Moolenbroek static void
rtsock_get_arp(struct rt_msghdr * rtm,struct rtsock_rta * rta,struct sockaddr_in * addr,struct sockaddr_dlx * gateway,lldata_arp_num_t num,unsigned int type)1387*ef8d499eSDavid van Moolenbroek rtsock_get_arp(struct rt_msghdr * rtm, struct rtsock_rta * rta,
1388*ef8d499eSDavid van Moolenbroek 	struct sockaddr_in * addr, struct sockaddr_dlx * gateway,
1389*ef8d499eSDavid van Moolenbroek 	lldata_arp_num_t num, unsigned int type)
1390*ef8d499eSDavid van Moolenbroek {
1391*ef8d499eSDavid van Moolenbroek 	struct ifdev *ifdev;
1392*ef8d499eSDavid van Moolenbroek 	unsigned int flags;
1393*ef8d499eSDavid van Moolenbroek 
1394*ef8d499eSDavid van Moolenbroek 	lldata_arp_get(num, addr, gateway, &ifdev, &flags);
1395*ef8d499eSDavid van Moolenbroek 
1396*ef8d499eSDavid van Moolenbroek 	memset(rtm, 0, sizeof(*rtm));
1397*ef8d499eSDavid van Moolenbroek 	rtm->rtm_version = RTM_VERSION;
1398*ef8d499eSDavid van Moolenbroek 	rtm->rtm_type = type;
1399*ef8d499eSDavid van Moolenbroek 	rtm->rtm_flags = flags;
1400*ef8d499eSDavid van Moolenbroek 	rtm->rtm_index = ifdev_get_index(ifdev);
1401*ef8d499eSDavid van Moolenbroek 
1402*ef8d499eSDavid van Moolenbroek 	/* TODO: obtaining and reporting the proper expiry time, if any. */
1403*ef8d499eSDavid van Moolenbroek 	if (!(flags & RTF_STATIC))
1404*ef8d499eSDavid van Moolenbroek 		rtm->rtm_rmx.rmx_expire = (time_t)-1;
1405*ef8d499eSDavid van Moolenbroek 
1406*ef8d499eSDavid van Moolenbroek 	rtsock_rta_init(rta);
1407*ef8d499eSDavid van Moolenbroek 
1408*ef8d499eSDavid van Moolenbroek 	rtsock_rta_set(rta, RTAX_DST, addr, addr->sin_len);
1409*ef8d499eSDavid van Moolenbroek 
1410*ef8d499eSDavid van Moolenbroek 	rtsock_rta_set(rta, RTAX_GATEWAY, gateway, gateway->sdlx_len);
1411*ef8d499eSDavid van Moolenbroek }
1412*ef8d499eSDavid van Moolenbroek 
1413*ef8d499eSDavid van Moolenbroek /*
1414*ef8d499eSDavid van Moolenbroek  * Send a routing message about an ARP table entry, with the given type which
1415*ef8d499eSDavid van Moolenbroek  * may be one of RTM_ADD, RTM_CHANGE, RTM_DELETE, RTM_LOCK, and RTM_GET.  The
1416*ef8d499eSDavid van Moolenbroek  * routing socket request information 'rtr', if not NULL, provides additional
1417*ef8d499eSDavid van Moolenbroek  * information about the routing socket that was the source of the request (if
1418*ef8d499eSDavid van Moolenbroek  * any) and various fields that should be echoed.
1419*ef8d499eSDavid van Moolenbroek  */
1420*ef8d499eSDavid van Moolenbroek void
rtsock_msg_arp(lldata_arp_num_t num,unsigned int type,const struct rtsock_request * rtr)1421*ef8d499eSDavid van Moolenbroek rtsock_msg_arp(lldata_arp_num_t num, unsigned int type,
1422*ef8d499eSDavid van Moolenbroek 	const struct rtsock_request * rtr)
1423*ef8d499eSDavid van Moolenbroek {
1424*ef8d499eSDavid van Moolenbroek 	struct sockaddr_in addr;
1425*ef8d499eSDavid van Moolenbroek 	struct sockaddr_dlx gateway;
1426*ef8d499eSDavid van Moolenbroek 	struct rt_msghdr rtm;
1427*ef8d499eSDavid van Moolenbroek 	struct rtsock_rta rta;
1428*ef8d499eSDavid van Moolenbroek 	struct pbuf *pbuf;
1429*ef8d499eSDavid van Moolenbroek 
1430*ef8d499eSDavid van Moolenbroek 	assert(rtr != NULL);
1431*ef8d499eSDavid van Moolenbroek 
1432*ef8d499eSDavid van Moolenbroek 	/*
1433*ef8d499eSDavid van Moolenbroek 	 * We do not maintain the link-local tables ourselves, and thus, we do
1434*ef8d499eSDavid van Moolenbroek 	 * not have a complete view of modifications to them.  In order not to
1435*ef8d499eSDavid van Moolenbroek 	 * confuse userland with inconsistent updates (e.g., deletion of
1436*ef8d499eSDavid van Moolenbroek 	 * previously unreported entries), send these routing messages to the
1437*ef8d499eSDavid van Moolenbroek 	 * source of the routing request only.
1438*ef8d499eSDavid van Moolenbroek 	 */
1439*ef8d499eSDavid van Moolenbroek 	if (!rtsock_msg_one(rtr->rtr_src, AF_INET, NULL /*pbuf*/))
1440*ef8d499eSDavid van Moolenbroek 		return;
1441*ef8d499eSDavid van Moolenbroek 
1442*ef8d499eSDavid van Moolenbroek 	rtsock_get_arp(&rtm, &rta, &addr, &gateway, num, type);
1443*ef8d499eSDavid van Moolenbroek 
1444*ef8d499eSDavid van Moolenbroek 	if (rtr != NULL) {
1445*ef8d499eSDavid van Moolenbroek 		rtm.rtm_flags |= RTF_DONE;
1446*ef8d499eSDavid van Moolenbroek 		rtm.rtm_pid = rtr->rtr_pid;
1447*ef8d499eSDavid van Moolenbroek 		rtm.rtm_seq = rtr->rtr_seq;
1448*ef8d499eSDavid van Moolenbroek 	}
1449*ef8d499eSDavid van Moolenbroek 
1450*ef8d499eSDavid van Moolenbroek 	if (rtsock_rta_finalize(&rtm, sizeof(rtm), &rtm.rtm_msglen,
1451*ef8d499eSDavid van Moolenbroek 	    &rtm.rtm_addrs, &rta, &pbuf, NULL, 0) > 0)
1452*ef8d499eSDavid van Moolenbroek 		rtsock_msg_one(rtr->rtr_src, AF_INET, pbuf);
1453*ef8d499eSDavid van Moolenbroek }
1454*ef8d499eSDavid van Moolenbroek 
1455*ef8d499eSDavid van Moolenbroek /*
1456*ef8d499eSDavid van Moolenbroek  * Obtain ARP table entries.
1457*ef8d499eSDavid van Moolenbroek  */
1458*ef8d499eSDavid van Moolenbroek static ssize_t
rtsock_info_lltable_arp(struct rmib_oldp * oldp)1459*ef8d499eSDavid van Moolenbroek rtsock_info_lltable_arp(struct rmib_oldp * oldp)
1460*ef8d499eSDavid van Moolenbroek {
1461*ef8d499eSDavid van Moolenbroek 	struct sockaddr_in addr;
1462*ef8d499eSDavid van Moolenbroek 	struct sockaddr_dlx gateway;
1463*ef8d499eSDavid van Moolenbroek 	struct rt_msghdr rtm;
1464*ef8d499eSDavid van Moolenbroek 	struct rtsock_rta rta;
1465*ef8d499eSDavid van Moolenbroek 	lldata_arp_num_t num;
1466*ef8d499eSDavid van Moolenbroek 	ssize_t r, off;
1467*ef8d499eSDavid van Moolenbroek 
1468*ef8d499eSDavid van Moolenbroek 	off = 0;
1469*ef8d499eSDavid van Moolenbroek 
1470*ef8d499eSDavid van Moolenbroek 	for (num = 0; lldata_arp_enum(&num); num++) {
1471*ef8d499eSDavid van Moolenbroek 		/* Size (over)estimation shortcut. */
1472*ef8d499eSDavid van Moolenbroek 		if (oldp == NULL) {
1473*ef8d499eSDavid van Moolenbroek 			off += sizeof(struct rt_msghdr) +
1474*ef8d499eSDavid van Moolenbroek 			    RT_ROUNDUP(sizeof(addr)) +
1475*ef8d499eSDavid van Moolenbroek 			    RT_ROUNDUP(sizeof(gateway));
1476*ef8d499eSDavid van Moolenbroek 
1477*ef8d499eSDavid van Moolenbroek 			continue;
1478*ef8d499eSDavid van Moolenbroek 		}
1479*ef8d499eSDavid van Moolenbroek 
1480*ef8d499eSDavid van Moolenbroek 		rtsock_get_arp(&rtm, &rta, &addr, &gateway, num, RTM_GET);
1481*ef8d499eSDavid van Moolenbroek 
1482*ef8d499eSDavid van Moolenbroek 		if ((r = rtsock_rta_finalize(&rtm, sizeof(rtm),
1483*ef8d499eSDavid van Moolenbroek 		    &rtm.rtm_msglen, &rtm.rtm_addrs, &rta, NULL /*pbuf*/, oldp,
1484*ef8d499eSDavid van Moolenbroek 		    off)) < 0)
1485*ef8d499eSDavid van Moolenbroek 			return r;
1486*ef8d499eSDavid van Moolenbroek 		off += r;
1487*ef8d499eSDavid van Moolenbroek 	}
1488*ef8d499eSDavid van Moolenbroek 
1489*ef8d499eSDavid van Moolenbroek 	/* TODO: should we add slack here? */
1490*ef8d499eSDavid van Moolenbroek 	return off;
1491*ef8d499eSDavid van Moolenbroek }
1492*ef8d499eSDavid van Moolenbroek 
1493*ef8d499eSDavid van Moolenbroek /*
1494*ef8d499eSDavid van Moolenbroek  * Generate routing socket data for an NDP table entry, for either routing
1495*ef8d499eSDavid van Moolenbroek  * socket broadcasting or a sysctl(7) request.  The NDP table entry number is
1496*ef8d499eSDavid van Moolenbroek  * given as 'num'.  The type of the message (RTM_) is given as 'type'.  The
1497*ef8d499eSDavid van Moolenbroek  * resulting routing message header is stored in 'rtm' and an address vector is
1498*ef8d499eSDavid van Moolenbroek  * stored in 'rta'.  The latter may point to addresses generated in 'addr' and
1499*ef8d499eSDavid van Moolenbroek  * 'gateway'.  The caller is responsible for combining the results into an
1500*ef8d499eSDavid van Moolenbroek  * appropriate routing message.
1501*ef8d499eSDavid van Moolenbroek  */
1502*ef8d499eSDavid van Moolenbroek static void
rtsock_get_ndp(struct rt_msghdr * rtm,struct rtsock_rta * rta,struct sockaddr_in6 * addr,struct sockaddr_dlx * gateway,lldata_ndp_num_t num,unsigned int type)1503*ef8d499eSDavid van Moolenbroek rtsock_get_ndp(struct rt_msghdr * rtm, struct rtsock_rta * rta,
1504*ef8d499eSDavid van Moolenbroek 	struct sockaddr_in6 * addr, struct sockaddr_dlx * gateway,
1505*ef8d499eSDavid van Moolenbroek 	lldata_ndp_num_t num, unsigned int type)
1506*ef8d499eSDavid van Moolenbroek {
1507*ef8d499eSDavid van Moolenbroek 	struct ifdev *ifdev;
1508*ef8d499eSDavid van Moolenbroek 	unsigned int flags;
1509*ef8d499eSDavid van Moolenbroek 
1510*ef8d499eSDavid van Moolenbroek 	lldata_ndp_get(num, addr, gateway, &ifdev, &flags);
1511*ef8d499eSDavid van Moolenbroek 
1512*ef8d499eSDavid van Moolenbroek 	memset(rtm, 0, sizeof(*rtm));
1513*ef8d499eSDavid van Moolenbroek 	rtm->rtm_version = RTM_VERSION;
1514*ef8d499eSDavid van Moolenbroek 	rtm->rtm_type = type;
1515*ef8d499eSDavid van Moolenbroek 	rtm->rtm_flags = flags;
1516*ef8d499eSDavid van Moolenbroek 	rtm->rtm_index = ifdev_get_index(ifdev);
1517*ef8d499eSDavid van Moolenbroek 
1518*ef8d499eSDavid van Moolenbroek 	rtsock_rta_init(rta);
1519*ef8d499eSDavid van Moolenbroek 
1520*ef8d499eSDavid van Moolenbroek 	rtsock_rta_set(rta, RTAX_DST, addr, addr->sin6_len);
1521*ef8d499eSDavid van Moolenbroek 
1522*ef8d499eSDavid van Moolenbroek 	rtsock_rta_set(rta, RTAX_GATEWAY, gateway, gateway->sdlx_len);
1523*ef8d499eSDavid van Moolenbroek }
1524*ef8d499eSDavid van Moolenbroek 
1525*ef8d499eSDavid van Moolenbroek /*
1526*ef8d499eSDavid van Moolenbroek  * Send a routing message about an NDP table entry, with the given type which
1527*ef8d499eSDavid van Moolenbroek  * may be one of RTM_ADD, RTM_CHANGE, RTM_DELETE, RTM_LOCK, and RTM_GET.  The
1528*ef8d499eSDavid van Moolenbroek  * routing socket request information 'rtr', if not NULL, provides additional
1529*ef8d499eSDavid van Moolenbroek  * information about the routing socket that was the source of the request (if
1530*ef8d499eSDavid van Moolenbroek  * any) and various fields that should be echoed.
1531*ef8d499eSDavid van Moolenbroek  */
1532*ef8d499eSDavid van Moolenbroek void
rtsock_msg_ndp(lldata_ndp_num_t num,unsigned int type,const struct rtsock_request * rtr)1533*ef8d499eSDavid van Moolenbroek rtsock_msg_ndp(lldata_ndp_num_t num, unsigned int type,
1534*ef8d499eSDavid van Moolenbroek 	const struct rtsock_request * rtr)
1535*ef8d499eSDavid van Moolenbroek {
1536*ef8d499eSDavid van Moolenbroek 	struct sockaddr_in6 addr;
1537*ef8d499eSDavid van Moolenbroek 	struct sockaddr_dlx gateway;
1538*ef8d499eSDavid van Moolenbroek 	struct rt_msghdr rtm;
1539*ef8d499eSDavid van Moolenbroek 	struct rtsock_rta rta;
1540*ef8d499eSDavid van Moolenbroek 	struct pbuf *pbuf;
1541*ef8d499eSDavid van Moolenbroek 
1542*ef8d499eSDavid van Moolenbroek 	assert(rtr != NULL);
1543*ef8d499eSDavid van Moolenbroek 
1544*ef8d499eSDavid van Moolenbroek 	/*
1545*ef8d499eSDavid van Moolenbroek 	 * We do not maintain the link-local tables ourselves, and thus, we do
1546*ef8d499eSDavid van Moolenbroek 	 * not have a complete view of modifications to them.  In order not to
1547*ef8d499eSDavid van Moolenbroek 	 * confuse userland with inconsistent updates (e.g., deletion of
1548*ef8d499eSDavid van Moolenbroek 	 * previously unreported entries), send these routing messages to the
1549*ef8d499eSDavid van Moolenbroek 	 * source of the routing request only.
1550*ef8d499eSDavid van Moolenbroek 	 */
1551*ef8d499eSDavid van Moolenbroek 	if (!rtsock_msg_one(rtr->rtr_src, AF_INET6, NULL /*pbuf*/))
1552*ef8d499eSDavid van Moolenbroek 		return;
1553*ef8d499eSDavid van Moolenbroek 
1554*ef8d499eSDavid van Moolenbroek 	rtsock_get_ndp(&rtm, &rta, &addr, &gateway, num, type);
1555*ef8d499eSDavid van Moolenbroek 
1556*ef8d499eSDavid van Moolenbroek 	if (rtr != NULL) {
1557*ef8d499eSDavid van Moolenbroek 		rtm.rtm_flags |= RTF_DONE;
1558*ef8d499eSDavid van Moolenbroek 		rtm.rtm_pid = rtr->rtr_pid;
1559*ef8d499eSDavid van Moolenbroek 		rtm.rtm_seq = rtr->rtr_seq;
1560*ef8d499eSDavid van Moolenbroek 	}
1561*ef8d499eSDavid van Moolenbroek 
1562*ef8d499eSDavid van Moolenbroek 	if (rtsock_rta_finalize(&rtm, sizeof(rtm), &rtm.rtm_msglen,
1563*ef8d499eSDavid van Moolenbroek 	    &rtm.rtm_addrs, &rta, &pbuf, NULL, 0) > 0)
1564*ef8d499eSDavid van Moolenbroek 		rtsock_msg_one(rtr->rtr_src, AF_INET6, pbuf);
1565*ef8d499eSDavid van Moolenbroek }
1566*ef8d499eSDavid van Moolenbroek 
1567*ef8d499eSDavid van Moolenbroek /*
1568*ef8d499eSDavid van Moolenbroek  * Obtain NDP table entries.
1569*ef8d499eSDavid van Moolenbroek  */
1570*ef8d499eSDavid van Moolenbroek static ssize_t
rtsock_info_lltable_ndp(struct rmib_oldp * oldp)1571*ef8d499eSDavid van Moolenbroek rtsock_info_lltable_ndp(struct rmib_oldp * oldp)
1572*ef8d499eSDavid van Moolenbroek {
1573*ef8d499eSDavid van Moolenbroek 	struct rt_msghdr rtm;
1574*ef8d499eSDavid van Moolenbroek 	struct rtsock_rta rta;
1575*ef8d499eSDavid van Moolenbroek 	struct sockaddr_in6 addr;
1576*ef8d499eSDavid van Moolenbroek 	struct sockaddr_dlx gateway;
1577*ef8d499eSDavid van Moolenbroek 	lldata_ndp_num_t num;
1578*ef8d499eSDavid van Moolenbroek 	ssize_t r, off;
1579*ef8d499eSDavid van Moolenbroek 
1580*ef8d499eSDavid van Moolenbroek 	off = 0;
1581*ef8d499eSDavid van Moolenbroek 
1582*ef8d499eSDavid van Moolenbroek 	for (num = 0; lldata_ndp_enum(&num); num++) {
1583*ef8d499eSDavid van Moolenbroek 		/* Size (over)estimation shortcut. */
1584*ef8d499eSDavid van Moolenbroek 		if (oldp == NULL) {
1585*ef8d499eSDavid van Moolenbroek 			off += sizeof(struct rt_msghdr) +
1586*ef8d499eSDavid van Moolenbroek 			    RT_ROUNDUP(sizeof(addr)) +
1587*ef8d499eSDavid van Moolenbroek 			    RT_ROUNDUP(sizeof(gateway));
1588*ef8d499eSDavid van Moolenbroek 
1589*ef8d499eSDavid van Moolenbroek 			continue;
1590*ef8d499eSDavid van Moolenbroek 		}
1591*ef8d499eSDavid van Moolenbroek 
1592*ef8d499eSDavid van Moolenbroek 		rtsock_get_ndp(&rtm, &rta, &addr, &gateway, num, RTM_GET);
1593*ef8d499eSDavid van Moolenbroek 
1594*ef8d499eSDavid van Moolenbroek 		if ((r = rtsock_rta_finalize(&rtm, sizeof(rtm),
1595*ef8d499eSDavid van Moolenbroek 		    &rtm.rtm_msglen, &rtm.rtm_addrs, &rta, NULL /*pbuf*/, oldp,
1596*ef8d499eSDavid van Moolenbroek 		    off)) < 0)
1597*ef8d499eSDavid van Moolenbroek 			return r;
1598*ef8d499eSDavid van Moolenbroek 		off += r;
1599*ef8d499eSDavid van Moolenbroek 	}
1600*ef8d499eSDavid van Moolenbroek 
1601*ef8d499eSDavid van Moolenbroek 	/* TODO: should we add slack here? */
1602*ef8d499eSDavid van Moolenbroek 	return off;
1603*ef8d499eSDavid van Moolenbroek }
1604*ef8d499eSDavid van Moolenbroek 
1605*ef8d499eSDavid van Moolenbroek /*
1606*ef8d499eSDavid van Moolenbroek  * Obtain link-layer (ARP, NDP) table entries.
1607*ef8d499eSDavid van Moolenbroek  */
1608*ef8d499eSDavid van Moolenbroek static ssize_t
rtsock_info_lltable(struct rmib_oldp * oldp,int family)1609*ef8d499eSDavid van Moolenbroek rtsock_info_lltable(struct rmib_oldp * oldp, int family)
1610*ef8d499eSDavid van Moolenbroek {
1611*ef8d499eSDavid van Moolenbroek 
1612*ef8d499eSDavid van Moolenbroek 	switch (family) {
1613*ef8d499eSDavid van Moolenbroek 	case AF_INET:
1614*ef8d499eSDavid van Moolenbroek 		return rtsock_info_lltable_arp(oldp);
1615*ef8d499eSDavid van Moolenbroek 
1616*ef8d499eSDavid van Moolenbroek 	case AF_INET6:
1617*ef8d499eSDavid van Moolenbroek 		return rtsock_info_lltable_ndp(oldp);
1618*ef8d499eSDavid van Moolenbroek 
1619*ef8d499eSDavid van Moolenbroek 	default:
1620*ef8d499eSDavid van Moolenbroek 		return 0;
1621*ef8d499eSDavid van Moolenbroek 	}
1622*ef8d499eSDavid van Moolenbroek }
1623*ef8d499eSDavid van Moolenbroek 
1624*ef8d499eSDavid van Moolenbroek /*
1625*ef8d499eSDavid van Moolenbroek  * Obtain link-layer address information for one specific interface.
1626*ef8d499eSDavid van Moolenbroek  */
1627*ef8d499eSDavid van Moolenbroek static ssize_t
rtsock_info_if_dl(struct ifdev * ifdev,struct ifa_msghdr * ifam,struct rmib_oldp * oldp,ssize_t off)1628*ef8d499eSDavid van Moolenbroek rtsock_info_if_dl(struct ifdev * ifdev, struct ifa_msghdr * ifam,
1629*ef8d499eSDavid van Moolenbroek 	struct rmib_oldp * oldp, ssize_t off)
1630*ef8d499eSDavid van Moolenbroek {
1631*ef8d499eSDavid van Moolenbroek 	struct rtsock_rta rta;
1632*ef8d499eSDavid van Moolenbroek 	struct sockaddr_dlx sdlx;
1633*ef8d499eSDavid van Moolenbroek 	ifaddr_dl_num_t num;
1634*ef8d499eSDavid van Moolenbroek 	ssize_t r, len;
1635*ef8d499eSDavid van Moolenbroek 
1636*ef8d499eSDavid van Moolenbroek 	len = 0;
1637*ef8d499eSDavid van Moolenbroek 
1638*ef8d499eSDavid van Moolenbroek 	for (num = 0; ifaddr_dl_enum(ifdev, &num); num++) {
1639*ef8d499eSDavid van Moolenbroek 		if (oldp == NULL) {
1640*ef8d499eSDavid van Moolenbroek 			len += sizeof(*ifam) + RT_ROUNDUP(sizeof(sdlx));
1641*ef8d499eSDavid van Moolenbroek 
1642*ef8d499eSDavid van Moolenbroek 			continue;
1643*ef8d499eSDavid van Moolenbroek 		}
1644*ef8d499eSDavid van Moolenbroek 
1645*ef8d499eSDavid van Moolenbroek 		rtsock_rta_init(&rta);
1646*ef8d499eSDavid van Moolenbroek 
1647*ef8d499eSDavid van Moolenbroek 		rtsock_rta_add_dl(&rta, ifdev, num, &sdlx);
1648*ef8d499eSDavid van Moolenbroek 
1649*ef8d499eSDavid van Moolenbroek 		if ((r = rtsock_rta_finalize(ifam, sizeof(*ifam),
1650*ef8d499eSDavid van Moolenbroek 		    &ifam->ifam_msglen, &ifam->ifam_addrs, &rta, NULL /*pbuf*/,
1651*ef8d499eSDavid van Moolenbroek 		    oldp, off + len)) < 0)
1652*ef8d499eSDavid van Moolenbroek 			return r;
1653*ef8d499eSDavid van Moolenbroek 		len += r;
1654*ef8d499eSDavid van Moolenbroek 	}
1655*ef8d499eSDavid van Moolenbroek 
1656*ef8d499eSDavid van Moolenbroek 	return len;
1657*ef8d499eSDavid van Moolenbroek }
1658*ef8d499eSDavid van Moolenbroek 
1659*ef8d499eSDavid van Moolenbroek /*
1660*ef8d499eSDavid van Moolenbroek  * Obtain IPv4 address information for one specific interface.
1661*ef8d499eSDavid van Moolenbroek  */
1662*ef8d499eSDavid van Moolenbroek static ssize_t
rtsock_info_if_v4(struct ifdev * ifdev,struct ifa_msghdr * ifam,struct rmib_oldp * oldp,ssize_t off)1663*ef8d499eSDavid van Moolenbroek rtsock_info_if_v4(struct ifdev * ifdev, struct ifa_msghdr * ifam,
1664*ef8d499eSDavid van Moolenbroek 	struct rmib_oldp * oldp, ssize_t off)
1665*ef8d499eSDavid van Moolenbroek {
1666*ef8d499eSDavid van Moolenbroek 	struct sockaddr_in sin[4];
1667*ef8d499eSDavid van Moolenbroek 	struct rtsock_rta rta;
1668*ef8d499eSDavid van Moolenbroek 	ifaddr_v4_num_t num;
1669*ef8d499eSDavid van Moolenbroek 	ssize_t r, len;
1670*ef8d499eSDavid van Moolenbroek 
1671*ef8d499eSDavid van Moolenbroek 	len = 0;
1672*ef8d499eSDavid van Moolenbroek 
1673*ef8d499eSDavid van Moolenbroek 	/*
1674*ef8d499eSDavid van Moolenbroek 	 * Mostly for future compatibility, we support multiple IPv4 interface
1675*ef8d499eSDavid van Moolenbroek 	 * addresses here.  Every interface has an interface address and a
1676*ef8d499eSDavid van Moolenbroek 	 * netmask.  In addition, an interface may have either a broadcast or a
1677*ef8d499eSDavid van Moolenbroek 	 * destination address.
1678*ef8d499eSDavid van Moolenbroek 	 */
1679*ef8d499eSDavid van Moolenbroek 	for (num = 0; ifaddr_v4_enum(ifdev, &num); num++) {
1680*ef8d499eSDavid van Moolenbroek 		/* Size (over)estimation shortcut. */
1681*ef8d499eSDavid van Moolenbroek 		if (oldp == NULL) {
1682*ef8d499eSDavid van Moolenbroek 			len += sizeof(*ifam) + RT_ROUNDUP(sizeof(sin[0])) * 3;
1683*ef8d499eSDavid van Moolenbroek 
1684*ef8d499eSDavid van Moolenbroek 			continue;
1685*ef8d499eSDavid van Moolenbroek 		}
1686*ef8d499eSDavid van Moolenbroek 
1687*ef8d499eSDavid van Moolenbroek 		rtsock_rta_init(&rta);
1688*ef8d499eSDavid van Moolenbroek 
1689*ef8d499eSDavid van Moolenbroek 		rtsock_rta_add_v4(&rta, ifdev, num, sin);
1690*ef8d499eSDavid van Moolenbroek 
1691*ef8d499eSDavid van Moolenbroek 		if ((r = rtsock_rta_finalize(ifam, sizeof(*ifam),
1692*ef8d499eSDavid van Moolenbroek 		    &ifam->ifam_msglen, &ifam->ifam_addrs, &rta, NULL /*pbuf*/,
1693*ef8d499eSDavid van Moolenbroek 		    oldp, off + len)) < 0)
1694*ef8d499eSDavid van Moolenbroek 			return r;
1695*ef8d499eSDavid van Moolenbroek 		len += r;
1696*ef8d499eSDavid van Moolenbroek 	}
1697*ef8d499eSDavid van Moolenbroek 
1698*ef8d499eSDavid van Moolenbroek 	return len;
1699*ef8d499eSDavid van Moolenbroek }
1700*ef8d499eSDavid van Moolenbroek 
1701*ef8d499eSDavid van Moolenbroek /*
1702*ef8d499eSDavid van Moolenbroek  * Obtain IPv6 address information for one specific interface.
1703*ef8d499eSDavid van Moolenbroek  */
1704*ef8d499eSDavid van Moolenbroek static ssize_t
rtsock_info_if_v6(struct ifdev * ifdev,struct ifa_msghdr * ifam,struct rmib_oldp * oldp,ssize_t off)1705*ef8d499eSDavid van Moolenbroek rtsock_info_if_v6(struct ifdev * ifdev, struct ifa_msghdr * ifam,
1706*ef8d499eSDavid van Moolenbroek 	struct rmib_oldp * oldp, ssize_t off)
1707*ef8d499eSDavid van Moolenbroek {
1708*ef8d499eSDavid van Moolenbroek 	struct sockaddr_in6 sin6[3];
1709*ef8d499eSDavid van Moolenbroek 	struct rtsock_rta rta;
1710*ef8d499eSDavid van Moolenbroek 	ifaddr_v6_num_t num;
1711*ef8d499eSDavid van Moolenbroek 	ssize_t r, len;
1712*ef8d499eSDavid van Moolenbroek 
1713*ef8d499eSDavid van Moolenbroek 	len = 0;
1714*ef8d499eSDavid van Moolenbroek 
1715*ef8d499eSDavid van Moolenbroek 	/* As with IPv4, except that IPv6 has no broadcast addresses. */
1716*ef8d499eSDavid van Moolenbroek 	for (num = 0; ifaddr_v6_enum(ifdev, &num); num++) {
1717*ef8d499eSDavid van Moolenbroek 		/* Size (over)estimation shortcut. */
1718*ef8d499eSDavid van Moolenbroek 		if (oldp == NULL) {
1719*ef8d499eSDavid van Moolenbroek 			len += sizeof(*ifam) + RT_ROUNDUP(sizeof(sin6[0])) * 3;
1720*ef8d499eSDavid van Moolenbroek 
1721*ef8d499eSDavid van Moolenbroek 			continue;
1722*ef8d499eSDavid van Moolenbroek 		}
1723*ef8d499eSDavid van Moolenbroek 
1724*ef8d499eSDavid van Moolenbroek 		rtsock_rta_init(&rta);
1725*ef8d499eSDavid van Moolenbroek 
1726*ef8d499eSDavid van Moolenbroek 		rtsock_rta_add_v6(&rta, ifdev, num, sin6);
1727*ef8d499eSDavid van Moolenbroek 
1728*ef8d499eSDavid van Moolenbroek 		if ((r = rtsock_rta_finalize(ifam, sizeof(*ifam),
1729*ef8d499eSDavid van Moolenbroek 		    &ifam->ifam_msglen, &ifam->ifam_addrs, &rta, NULL /*pbuf*/,
1730*ef8d499eSDavid van Moolenbroek 		    oldp, off + len)) < 0)
1731*ef8d499eSDavid van Moolenbroek 			return r;
1732*ef8d499eSDavid van Moolenbroek 		len += r;
1733*ef8d499eSDavid van Moolenbroek 	}
1734*ef8d499eSDavid van Moolenbroek 
1735*ef8d499eSDavid van Moolenbroek 	return len;
1736*ef8d499eSDavid van Moolenbroek }
1737*ef8d499eSDavid van Moolenbroek 
1738*ef8d499eSDavid van Moolenbroek /*
1739*ef8d499eSDavid van Moolenbroek  * Obtain information for one specific interface.
1740*ef8d499eSDavid van Moolenbroek  */
1741*ef8d499eSDavid van Moolenbroek static ssize_t
rtsock_info_if(struct ifdev * ifdev,struct rmib_oldp * oldp,ssize_t off,int family)1742*ef8d499eSDavid van Moolenbroek rtsock_info_if(struct ifdev * ifdev, struct rmib_oldp * oldp, ssize_t off,
1743*ef8d499eSDavid van Moolenbroek 	int family)
1744*ef8d499eSDavid van Moolenbroek {
1745*ef8d499eSDavid van Moolenbroek 	struct rtsock_rta rta;
1746*ef8d499eSDavid van Moolenbroek 	struct sockaddr_dlx sdlx;
1747*ef8d499eSDavid van Moolenbroek 	struct if_msghdr ifm;
1748*ef8d499eSDavid van Moolenbroek 	struct ifa_msghdr ifam;
1749*ef8d499eSDavid van Moolenbroek 	unsigned int ifflags;
1750*ef8d499eSDavid van Moolenbroek 	ssize_t r, len, sdlxsize;
1751*ef8d499eSDavid van Moolenbroek 
1752*ef8d499eSDavid van Moolenbroek 	len = 0;
1753*ef8d499eSDavid van Moolenbroek 
1754*ef8d499eSDavid van Moolenbroek 	ifflags = ifdev_get_ifflags(ifdev);
1755*ef8d499eSDavid van Moolenbroek 
1756*ef8d499eSDavid van Moolenbroek 	/* Create an interface information entry. */
1757*ef8d499eSDavid van Moolenbroek 	rtsock_rta_init(&rta);
1758*ef8d499eSDavid van Moolenbroek 
1759*ef8d499eSDavid van Moolenbroek 	if (oldp != NULL) {
1760*ef8d499eSDavid van Moolenbroek 		memset(&ifm, 0, sizeof(ifm));
1761*ef8d499eSDavid van Moolenbroek 		ifm.ifm_version = RTM_VERSION;
1762*ef8d499eSDavid van Moolenbroek 		ifm.ifm_type = RTM_IFINFO;
1763*ef8d499eSDavid van Moolenbroek 		ifm.ifm_flags = ifflags;
1764*ef8d499eSDavid van Moolenbroek 		ifm.ifm_index = ifdev_get_index(ifdev);
1765*ef8d499eSDavid van Moolenbroek 		memcpy(&ifm.ifm_data, ifdev_get_ifdata(ifdev),
1766*ef8d499eSDavid van Moolenbroek 		    sizeof(ifm.ifm_data));
1767*ef8d499eSDavid van Moolenbroek 	}
1768*ef8d499eSDavid van Moolenbroek 
1769*ef8d499eSDavid van Moolenbroek 	/*
1770*ef8d499eSDavid van Moolenbroek 	 * Generate a datalink socket address structure.  TODO: see if it is
1771*ef8d499eSDavid van Moolenbroek 	 * worth obtaining just the length for the (oldp == NULL) case here.
1772*ef8d499eSDavid van Moolenbroek 	 */
1773*ef8d499eSDavid van Moolenbroek 	memset(&sdlx, 0, sizeof(sdlx));
1774*ef8d499eSDavid van Moolenbroek 
1775*ef8d499eSDavid van Moolenbroek 	ifaddr_dl_get(ifdev, 0, &sdlx);
1776*ef8d499eSDavid van Moolenbroek 
1777*ef8d499eSDavid van Moolenbroek 	sdlxsize = RT_ROUNDUP(sdlx.sdlx_len);
1778*ef8d499eSDavid van Moolenbroek 
1779*ef8d499eSDavid van Moolenbroek 	rtsock_rta_set(&rta, RTAX_IFP, &sdlx, sdlxsize);
1780*ef8d499eSDavid van Moolenbroek 
1781*ef8d499eSDavid van Moolenbroek 	if ((r = rtsock_rta_finalize(&ifm, sizeof(ifm), &ifm.ifm_msglen,
1782*ef8d499eSDavid van Moolenbroek 	    &ifm.ifm_addrs, &rta, NULL /*pbuf*/, oldp, off + len)) < 0)
1783*ef8d499eSDavid van Moolenbroek 		return r;
1784*ef8d499eSDavid van Moolenbroek 	len += r;
1785*ef8d499eSDavid van Moolenbroek 
1786*ef8d499eSDavid van Moolenbroek 	/* Generate a header for all addresses once. */
1787*ef8d499eSDavid van Moolenbroek 	if (oldp != NULL) {
1788*ef8d499eSDavid van Moolenbroek 		memset(&ifam, 0, sizeof(ifam));
1789*ef8d499eSDavid van Moolenbroek 		ifam.ifam_version = RTM_VERSION;
1790*ef8d499eSDavid van Moolenbroek 		ifam.ifam_type = RTM_NEWADDR;
1791*ef8d499eSDavid van Moolenbroek 		ifam.ifam_flags = 0;
1792*ef8d499eSDavid van Moolenbroek 		ifam.ifam_index = ifdev_get_index(ifdev);
1793*ef8d499eSDavid van Moolenbroek 		ifam.ifam_metric = ifdev_get_metric(ifdev);
1794*ef8d499eSDavid van Moolenbroek 	}
1795*ef8d499eSDavid van Moolenbroek 
1796*ef8d499eSDavid van Moolenbroek 	/* If requested and applicable, add any datalink addresses. */
1797*ef8d499eSDavid van Moolenbroek 	if (family == AF_UNSPEC || family == AF_LINK) {
1798*ef8d499eSDavid van Moolenbroek 		if ((r = rtsock_info_if_dl(ifdev, &ifam, oldp, off + len)) < 0)
1799*ef8d499eSDavid van Moolenbroek 			return r;
1800*ef8d499eSDavid van Moolenbroek 		len += r;
1801*ef8d499eSDavid van Moolenbroek 	}
1802*ef8d499eSDavid van Moolenbroek 
1803*ef8d499eSDavid van Moolenbroek 	/* If requested and applicable, add any IPv4 addresses. */
1804*ef8d499eSDavid van Moolenbroek 	if (family == AF_UNSPEC || family == AF_INET) {
1805*ef8d499eSDavid van Moolenbroek 		if ((r = rtsock_info_if_v4(ifdev, &ifam, oldp, off + len)) < 0)
1806*ef8d499eSDavid van Moolenbroek 			return r;
1807*ef8d499eSDavid van Moolenbroek 		len += r;
1808*ef8d499eSDavid van Moolenbroek 	}
1809*ef8d499eSDavid van Moolenbroek 
1810*ef8d499eSDavid van Moolenbroek 	/* If requested and applicable, add any IPv6 addresses. */
1811*ef8d499eSDavid van Moolenbroek 	if (family == AF_UNSPEC || family == AF_INET6) {
1812*ef8d499eSDavid van Moolenbroek 		if ((r = rtsock_info_if_v6(ifdev, &ifam, oldp, off + len)) < 0)
1813*ef8d499eSDavid van Moolenbroek 			return r;
1814*ef8d499eSDavid van Moolenbroek 		len += r;
1815*ef8d499eSDavid van Moolenbroek 	}
1816*ef8d499eSDavid van Moolenbroek 
1817*ef8d499eSDavid van Moolenbroek 	return len;
1818*ef8d499eSDavid van Moolenbroek }
1819*ef8d499eSDavid van Moolenbroek 
1820*ef8d499eSDavid van Moolenbroek /*
1821*ef8d499eSDavid van Moolenbroek  * Obtain interface information.
1822*ef8d499eSDavid van Moolenbroek  */
1823*ef8d499eSDavid van Moolenbroek static ssize_t
rtsock_info_iflist(struct rmib_oldp * oldp,int family,uint32_t ifindex)1824*ef8d499eSDavid van Moolenbroek rtsock_info_iflist(struct rmib_oldp * oldp, int family, uint32_t ifindex)
1825*ef8d499eSDavid van Moolenbroek {
1826*ef8d499eSDavid van Moolenbroek 	struct ifdev *ifdev;
1827*ef8d499eSDavid van Moolenbroek 	ssize_t r, off;
1828*ef8d499eSDavid van Moolenbroek 
1829*ef8d499eSDavid van Moolenbroek 	/*
1830*ef8d499eSDavid van Moolenbroek 	 * If information about a specific interface index is requested, then
1831*ef8d499eSDavid van Moolenbroek 	 * return information for just that interface.
1832*ef8d499eSDavid van Moolenbroek 	 */
1833*ef8d499eSDavid van Moolenbroek 	if (ifindex != 0) {
1834*ef8d499eSDavid van Moolenbroek 		if ((ifdev = ifdev_get_by_index(ifindex)) != NULL)
1835*ef8d499eSDavid van Moolenbroek 			return rtsock_info_if(ifdev, oldp, 0, family);
1836*ef8d499eSDavid van Moolenbroek 		else
1837*ef8d499eSDavid van Moolenbroek 			return 0;
1838*ef8d499eSDavid van Moolenbroek 	}
1839*ef8d499eSDavid van Moolenbroek 
1840*ef8d499eSDavid van Moolenbroek 	/* Otherwise, iterate through the list of all interfaces. */
1841*ef8d499eSDavid van Moolenbroek 	off = 0;
1842*ef8d499eSDavid van Moolenbroek 
1843*ef8d499eSDavid van Moolenbroek 	for (ifdev = ifdev_enum(NULL); ifdev != NULL;
1844*ef8d499eSDavid van Moolenbroek 	    ifdev = ifdev_enum(ifdev)) {
1845*ef8d499eSDavid van Moolenbroek 
1846*ef8d499eSDavid van Moolenbroek 		/* Avoid generating results that are never copied out. */
1847*ef8d499eSDavid van Moolenbroek 		if (oldp != NULL && !rmib_inrange(oldp, off))
1848*ef8d499eSDavid van Moolenbroek 			oldp = NULL;
1849*ef8d499eSDavid van Moolenbroek 
1850*ef8d499eSDavid van Moolenbroek 		if ((r = rtsock_info_if(ifdev, oldp, off, family)) < 0)
1851*ef8d499eSDavid van Moolenbroek 			return r;
1852*ef8d499eSDavid van Moolenbroek 
1853*ef8d499eSDavid van Moolenbroek 		off += r;
1854*ef8d499eSDavid van Moolenbroek 	}
1855*ef8d499eSDavid van Moolenbroek 
1856*ef8d499eSDavid van Moolenbroek 	/* TODO: should we add slack here? */
1857*ef8d499eSDavid van Moolenbroek 	return off;
1858*ef8d499eSDavid van Moolenbroek }
1859*ef8d499eSDavid van Moolenbroek 
1860*ef8d499eSDavid van Moolenbroek /*
1861*ef8d499eSDavid van Moolenbroek  * Obtain routing table, ARP cache, and interface information through
1862*ef8d499eSDavid van Moolenbroek  * sysctl(7).  Return the (produced, or if oldp is NULL, estimated) byte size
1863*ef8d499eSDavid van Moolenbroek  * of the output on success, or a negative error code on failure.
1864*ef8d499eSDavid van Moolenbroek  */
1865*ef8d499eSDavid van Moolenbroek static ssize_t
rtsock_info(struct rmib_call * call,struct rmib_node * node __unused,struct rmib_oldp * oldp,struct rmib_newp * newp __unused)1866*ef8d499eSDavid van Moolenbroek rtsock_info(struct rmib_call * call, struct rmib_node * node __unused,
1867*ef8d499eSDavid van Moolenbroek 	struct rmib_oldp * oldp, struct rmib_newp * newp __unused)
1868*ef8d499eSDavid van Moolenbroek {
1869*ef8d499eSDavid van Moolenbroek 	int family, filter;
1870*ef8d499eSDavid van Moolenbroek 
1871*ef8d499eSDavid van Moolenbroek 	if (call->call_namelen != 3)
1872*ef8d499eSDavid van Moolenbroek 		return EINVAL;
1873*ef8d499eSDavid van Moolenbroek 
1874*ef8d499eSDavid van Moolenbroek 	family = call->call_name[0];
1875*ef8d499eSDavid van Moolenbroek 	filter = call->call_name[2];
1876*ef8d499eSDavid van Moolenbroek 
1877*ef8d499eSDavid van Moolenbroek 	switch (call->call_name[1]) {
1878*ef8d499eSDavid van Moolenbroek 	case NET_RT_FLAGS:
1879*ef8d499eSDavid van Moolenbroek 		/*
1880*ef8d499eSDavid van Moolenbroek 		 * Preliminary support for changes as of NetBSD 8, where by
1881*ef8d499eSDavid van Moolenbroek 		 * default, the use of this subcall implies an ARP/NDP-only
1882*ef8d499eSDavid van Moolenbroek 		 * request.
1883*ef8d499eSDavid van Moolenbroek 		 */
1884*ef8d499eSDavid van Moolenbroek 		if (filter == 0)
1885*ef8d499eSDavid van Moolenbroek 			filter |= RTF_LLDATA;
1886*ef8d499eSDavid van Moolenbroek 
1887*ef8d499eSDavid van Moolenbroek 		if (filter & RTF_LLDATA) {
1888*ef8d499eSDavid van Moolenbroek 			if (family == AF_UNSPEC)
1889*ef8d499eSDavid van Moolenbroek 				return EINVAL;
1890*ef8d499eSDavid van Moolenbroek 
1891*ef8d499eSDavid van Moolenbroek 			/*
1892*ef8d499eSDavid van Moolenbroek 			 * Split off ARP/NDP handling from the normal routing
1893*ef8d499eSDavid van Moolenbroek 			 * table listing, as done since NetBSD 8.  We generate
1894*ef8d499eSDavid van Moolenbroek 			 * the ARP/NDP listing from here, and keep those
1895*ef8d499eSDavid van Moolenbroek 			 * entries out of the routing table dump below.  Since
1896*ef8d499eSDavid van Moolenbroek 			 * the filter is of a match-any type, and we have just
1897*ef8d499eSDavid van Moolenbroek 			 * matched a flag, no further filtering is needed here.
1898*ef8d499eSDavid van Moolenbroek 			 */
1899*ef8d499eSDavid van Moolenbroek 			return rtsock_info_lltable(oldp, family);
1900*ef8d499eSDavid van Moolenbroek 		}
1901*ef8d499eSDavid van Moolenbroek 
1902*ef8d499eSDavid van Moolenbroek 		/* FALLTHROUGH */
1903*ef8d499eSDavid van Moolenbroek 	case NET_RT_DUMP:
1904*ef8d499eSDavid van Moolenbroek 		return rtsock_info_rtable(oldp, family, filter);
1905*ef8d499eSDavid van Moolenbroek 
1906*ef8d499eSDavid van Moolenbroek 	case NET_RT_IFLIST:
1907*ef8d499eSDavid van Moolenbroek 		return rtsock_info_iflist(oldp, family, filter);
1908*ef8d499eSDavid van Moolenbroek 
1909*ef8d499eSDavid van Moolenbroek 	default:
1910*ef8d499eSDavid van Moolenbroek 		return EINVAL;
1911*ef8d499eSDavid van Moolenbroek 	}
1912*ef8d499eSDavid van Moolenbroek }
1913