xref: /minix3/minix/net/lwip/pktsock.c (revision ef8d499e2d2af900e9b2ab297171d7b088652482)
1*ef8d499eSDavid van Moolenbroek /* LWIP service - pktsock.c - packet code shared between UDP and RAW */
2*ef8d499eSDavid van Moolenbroek 
3*ef8d499eSDavid van Moolenbroek #include "lwip.h"
4*ef8d499eSDavid van Moolenbroek #include "pktsock.h"
5*ef8d499eSDavid van Moolenbroek #include "ifaddr.h"
6*ef8d499eSDavid van Moolenbroek 
7*ef8d499eSDavid van Moolenbroek /*
8*ef8d499eSDavid van Moolenbroek  * This buffer should be much bigger (at least 10KB, according to RFC 3542),
9*ef8d499eSDavid van Moolenbroek  * but we do not support the ancillary options that take so much space anyway.
10*ef8d499eSDavid van Moolenbroek  */
11*ef8d499eSDavid van Moolenbroek #define PKTSOCK_CTLBUF_SIZE		256
12*ef8d499eSDavid van Moolenbroek 
13*ef8d499eSDavid van Moolenbroek static char pktsock_ctlbuf[PKTSOCK_CTLBUF_SIZE];
14*ef8d499eSDavid van Moolenbroek 
15*ef8d499eSDavid van Moolenbroek /*
16*ef8d499eSDavid van Moolenbroek  * Header structures with ancillary data for received packets.  The reason that
17*ef8d499eSDavid van Moolenbroek  * we do not simply use a generic pkthdr structure with ip_addr_t source and
18*ef8d499eSDavid van Moolenbroek  * destination addresses, is that for UDP packets, we put this structure in
19*ef8d499eSDavid van Moolenbroek  * place of the received (ethernet and IP headers), and such a full structure
20*ef8d499eSDavid van Moolenbroek  * (including IPv6-size addresses) would not fit in the header space for IPv4
21*ef8d499eSDavid van Moolenbroek  * packets.  So instead we use two address structures, one for IPv4 and one for
22*ef8d499eSDavid van Moolenbroek  * IPv6, and a generic header structure on top of it, which also identifies
23*ef8d499eSDavid van Moolenbroek  * which address structure is underneath.  The combination of the address
24*ef8d499eSDavid van Moolenbroek  * structure and the header structure must fit in the IP header.  The IPv6
25*ef8d499eSDavid van Moolenbroek  * packet header is already so close to the limit here that we have to use
26*ef8d499eSDavid van Moolenbroek  * packed addresses.  For IPv4 we use the regular addresses for simplicity.
27*ef8d499eSDavid van Moolenbroek  */
28*ef8d499eSDavid van Moolenbroek struct pkthdr {
29*ef8d499eSDavid van Moolenbroek 	uint16_t port;			/* source port number (UDP only) */
30*ef8d499eSDavid van Moolenbroek 	uint8_t dstif;			/* interface that received the pkt */
31*ef8d499eSDavid van Moolenbroek 	uint8_t addrif;			/* interface that accepted the pkt */
32*ef8d499eSDavid van Moolenbroek 	uint8_t tos;			/* TOS/TC value from the IP header */
33*ef8d499eSDavid van Moolenbroek 	uint8_t ttl;			/* TTL/HL value from the IP header */
34*ef8d499eSDavid van Moolenbroek 	uint8_t flags;			/* packet flags (PKTHF_) */
35*ef8d499eSDavid van Moolenbroek 	uint8_t _unused;		/* all that is still available.. */
36*ef8d499eSDavid van Moolenbroek };
37*ef8d499eSDavid van Moolenbroek 
38*ef8d499eSDavid van Moolenbroek #define PKTHF_IPV6		0x01	/* packet has IPv6 header */
39*ef8d499eSDavid van Moolenbroek #define PKTHF_MCAST		0x02	/* packet has multicast destination */
40*ef8d499eSDavid van Moolenbroek #define PKTHF_BCAST		0x04	/* packet has broadcast destination */
41*ef8d499eSDavid van Moolenbroek 
42*ef8d499eSDavid van Moolenbroek struct pktaddr4 {
43*ef8d499eSDavid van Moolenbroek 	ip4_addr_t srcaddr;
44*ef8d499eSDavid van Moolenbroek 	ip4_addr_t dstaddr;
45*ef8d499eSDavid van Moolenbroek };
46*ef8d499eSDavid van Moolenbroek 
47*ef8d499eSDavid van Moolenbroek struct pktaddr6 {
48*ef8d499eSDavid van Moolenbroek 	ip6_addr_p_t srcaddr;
49*ef8d499eSDavid van Moolenbroek 	ip6_addr_p_t dstaddr;
50*ef8d499eSDavid van Moolenbroek };
51*ef8d499eSDavid van Moolenbroek 
52*ef8d499eSDavid van Moolenbroek /*
53*ef8d499eSDavid van Moolenbroek  * Create a packet socket.  Relay parameters and return values to and from the
54*ef8d499eSDavid van Moolenbroek  * IP module's socket creation function.  This function must not allocate any
55*ef8d499eSDavid van Moolenbroek  * resources in any form, as socket creation may still fail later, in which
56*ef8d499eSDavid van Moolenbroek  * case no destruction function is called.
57*ef8d499eSDavid van Moolenbroek  */
58*ef8d499eSDavid van Moolenbroek int
pktsock_socket(struct pktsock * pkt,int domain,size_t sndbuf,size_t rcvbuf,struct sock ** sockp)59*ef8d499eSDavid van Moolenbroek pktsock_socket(struct pktsock * pkt, int domain, size_t sndbuf, size_t rcvbuf,
60*ef8d499eSDavid van Moolenbroek 	struct sock ** sockp)
61*ef8d499eSDavid van Moolenbroek {
62*ef8d499eSDavid van Moolenbroek 
63*ef8d499eSDavid van Moolenbroek 	pkt->pkt_rcvhead = NULL;
64*ef8d499eSDavid van Moolenbroek 	pkt->pkt_rcvtailp = &pkt->pkt_rcvhead;
65*ef8d499eSDavid van Moolenbroek 	pkt->pkt_rcvlen = 0;
66*ef8d499eSDavid van Moolenbroek 
67*ef8d499eSDavid van Moolenbroek 	mcast_reset(&pkt->pkt_mcast);
68*ef8d499eSDavid van Moolenbroek 
69*ef8d499eSDavid van Moolenbroek 	memset(&pkt->pkt_srcaddr, 0, sizeof(pkt->pkt_srcaddr));
70*ef8d499eSDavid van Moolenbroek 	pkt->pkt_ifindex = 0;
71*ef8d499eSDavid van Moolenbroek 
72*ef8d499eSDavid van Moolenbroek 	/*
73*ef8d499eSDavid van Moolenbroek 	 * Any PKTF_ type flags should be initialized on the socket only after
74*ef8d499eSDavid van Moolenbroek 	 * the following call, as this call will clear the flags field.  For
75*ef8d499eSDavid van Moolenbroek 	 * now, no PKTF_ flags need to be set by default, though.
76*ef8d499eSDavid van Moolenbroek 	 */
77*ef8d499eSDavid van Moolenbroek 	return ipsock_socket(&pkt->pkt_ipsock, domain, sndbuf, rcvbuf, sockp);
78*ef8d499eSDavid van Moolenbroek }
79*ef8d499eSDavid van Moolenbroek 
80*ef8d499eSDavid van Moolenbroek /*
81*ef8d499eSDavid van Moolenbroek  * Return TRUE if the given packet can and should be received on the given
82*ef8d499eSDavid van Moolenbroek  * socket, or FALSE if there is a reason not to receive the packet.
83*ef8d499eSDavid van Moolenbroek  */
84*ef8d499eSDavid van Moolenbroek static int
pktsock_may_recv(struct pktsock * pkt,struct pbuf * pbuf)85*ef8d499eSDavid van Moolenbroek pktsock_may_recv(struct pktsock * pkt, struct pbuf * pbuf)
86*ef8d499eSDavid van Moolenbroek {
87*ef8d499eSDavid van Moolenbroek 
88*ef8d499eSDavid van Moolenbroek 	/*
89*ef8d499eSDavid van Moolenbroek 	 * By policy, multicast packets should not be received on sockets of
90*ef8d499eSDavid van Moolenbroek 	 * which the owning application is not multicast aware.
91*ef8d499eSDavid van Moolenbroek 	 */
92*ef8d499eSDavid van Moolenbroek 	if (ip_addr_ismulticast(ip_current_dest_addr()) &&
93*ef8d499eSDavid van Moolenbroek 	    !(ipsock_get_flag(&pkt->pkt_ipsock, PKTF_MCAWARE)))
94*ef8d499eSDavid van Moolenbroek 		return FALSE;
95*ef8d499eSDavid van Moolenbroek 
96*ef8d499eSDavid van Moolenbroek 	/*
97*ef8d499eSDavid van Moolenbroek 	 * Due to fragment reassembly, we might end up with packets that take
98*ef8d499eSDavid van Moolenbroek 	 * up more buffer space than their byte size, even after rounding up
99*ef8d499eSDavid van Moolenbroek 	 * the latter.  The user probably does not want packets to get dropped
100*ef8d499eSDavid van Moolenbroek 	 * for that reason, e.g. when they set a 64K limit and the packet ends
101*ef8d499eSDavid van Moolenbroek 	 * up being estimated as 65K and dropped.  So, we test against
102*ef8d499eSDavid van Moolenbroek 	 * 'pbuf->tot_len' rather than the rounded-up packet size.  However,
103*ef8d499eSDavid van Moolenbroek 	 * 'pkt->pkt_rcvlen' itself is increased by the rounded-up packet size
104*ef8d499eSDavid van Moolenbroek 	 * when enqueuing the packet, so that we still count the memory
105*ef8d499eSDavid van Moolenbroek 	 * consumption (generally) conservatively, which is what we want.
106*ef8d499eSDavid van Moolenbroek 	 */
107*ef8d499eSDavid van Moolenbroek 	return (pkt->pkt_rcvlen + pbuf->tot_len <=
108*ef8d499eSDavid van Moolenbroek 	    ipsock_get_rcvbuf(&pkt->pkt_ipsock));
109*ef8d499eSDavid van Moolenbroek }
110*ef8d499eSDavid van Moolenbroek 
111*ef8d499eSDavid van Moolenbroek /*
112*ef8d499eSDavid van Moolenbroek  * Check whether the given packet can and should be received on the given
113*ef8d499eSDavid van Moolenbroek  * socket.  If so, return the amount of space for ancillary information that
114*ef8d499eSDavid van Moolenbroek  * will be necessary for the packet.  If not, return a negative value.
115*ef8d499eSDavid van Moolenbroek  */
116*ef8d499eSDavid van Moolenbroek int
pktsock_test_input(struct pktsock * pkt,struct pbuf * pbuf)117*ef8d499eSDavid van Moolenbroek pktsock_test_input(struct pktsock * pkt, struct pbuf * pbuf)
118*ef8d499eSDavid van Moolenbroek {
119*ef8d499eSDavid van Moolenbroek 
120*ef8d499eSDavid van Moolenbroek 	/*
121*ef8d499eSDavid van Moolenbroek 	 * This check will be done again in pktsock_input(), but this function
122*ef8d499eSDavid van Moolenbroek 	 * is called for raw packets only (not for UDP packets) and, if this
123*ef8d499eSDavid van Moolenbroek 	 * (cheap) check fails, we can avoid a (rather expensive) packet copy.
124*ef8d499eSDavid van Moolenbroek 	 */
125*ef8d499eSDavid van Moolenbroek 	if (!pktsock_may_recv(pkt, pbuf))
126*ef8d499eSDavid van Moolenbroek 		return -1;
127*ef8d499eSDavid van Moolenbroek 
128*ef8d499eSDavid van Moolenbroek 	if (ip_current_is_v6())
129*ef8d499eSDavid van Moolenbroek 		return (int)(sizeof(struct pktaddr6) + sizeof(struct pkthdr));
130*ef8d499eSDavid van Moolenbroek 	else
131*ef8d499eSDavid van Moolenbroek 		return (int)(sizeof(struct pktaddr4) + sizeof(struct pkthdr));
132*ef8d499eSDavid van Moolenbroek }
133*ef8d499eSDavid van Moolenbroek 
134*ef8d499eSDavid van Moolenbroek /*
135*ef8d499eSDavid van Moolenbroek  * A packet has arrived on a packet socket.  We own the given packet buffer,
136*ef8d499eSDavid van Moolenbroek  * and so we must free it if we do not want to keep it.
137*ef8d499eSDavid van Moolenbroek  */
138*ef8d499eSDavid van Moolenbroek void
pktsock_input(struct pktsock * pkt,struct pbuf * pbuf,const ip_addr_t * srcaddr,uint16_t port)139*ef8d499eSDavid van Moolenbroek pktsock_input(struct pktsock * pkt, struct pbuf * pbuf,
140*ef8d499eSDavid van Moolenbroek 	const ip_addr_t * srcaddr, uint16_t port)
141*ef8d499eSDavid van Moolenbroek {
142*ef8d499eSDavid van Moolenbroek 	struct pktaddr4 pktaddr4;
143*ef8d499eSDavid van Moolenbroek 	struct pktaddr6 pktaddr6;
144*ef8d499eSDavid van Moolenbroek 	struct pkthdr pkthdr;
145*ef8d499eSDavid van Moolenbroek 	void *pktaddr;
146*ef8d499eSDavid van Moolenbroek 	struct ifdev *ifdev;
147*ef8d499eSDavid van Moolenbroek 	size_t pktaddrlen;
148*ef8d499eSDavid van Moolenbroek 
149*ef8d499eSDavid van Moolenbroek 	/*
150*ef8d499eSDavid van Moolenbroek 	 * We are going to mess with the packet's header and contents, so we
151*ef8d499eSDavid van Moolenbroek 	 * must be the exclusive owner of the packet.  For UDP packets, lwIP
152*ef8d499eSDavid van Moolenbroek 	 * must have made a copy for us in case of non-exclusive delivery
153*ef8d499eSDavid van Moolenbroek 	 * (e.g., multicast packets).  For raw packets, we have made a copy of
154*ef8d499eSDavid van Moolenbroek 	 * the packet ourselves just before the call to this function.
155*ef8d499eSDavid van Moolenbroek 	 */
156*ef8d499eSDavid van Moolenbroek 	if (pbuf->ref != 1)
157*ef8d499eSDavid van Moolenbroek 		panic("input packet has multiple references!");
158*ef8d499eSDavid van Moolenbroek 
159*ef8d499eSDavid van Moolenbroek 	/* If the packet should not be received on this socket, drop it. */
160*ef8d499eSDavid van Moolenbroek 	if (!pktsock_may_recv(pkt, pbuf)) {
161*ef8d499eSDavid van Moolenbroek 		pbuf_free(pbuf);
162*ef8d499eSDavid van Moolenbroek 
163*ef8d499eSDavid van Moolenbroek 		return;
164*ef8d499eSDavid van Moolenbroek 	}
165*ef8d499eSDavid van Moolenbroek 
166*ef8d499eSDavid van Moolenbroek 	/*
167*ef8d499eSDavid van Moolenbroek 	 * Enqueue the packet.  Overwrite the leading IP header with packet
168*ef8d499eSDavid van Moolenbroek 	 * information that is used at the time of receipt by userland.  The
169*ef8d499eSDavid van Moolenbroek 	 * data structures are such that the information always fits in what
170*ef8d499eSDavid van Moolenbroek 	 * was the IP header.  The reference count check earlier ensures that
171*ef8d499eSDavid van Moolenbroek 	 * we never overwrite part of a packet that is still in use elsewhere.
172*ef8d499eSDavid van Moolenbroek 	 */
173*ef8d499eSDavid van Moolenbroek 	if (ip_current_is_v6()) {
174*ef8d499eSDavid van Moolenbroek 		assert(IP_IS_V6(srcaddr));
175*ef8d499eSDavid van Moolenbroek 		assert(ip6_current_dest_addr() != NULL);
176*ef8d499eSDavid van Moolenbroek 
177*ef8d499eSDavid van Moolenbroek 		ip6_addr_copy_to_packed(pktaddr6.srcaddr, *ip_2_ip6(srcaddr));
178*ef8d499eSDavid van Moolenbroek 		ip6_addr_copy_to_packed(pktaddr6.dstaddr,
179*ef8d499eSDavid van Moolenbroek 		    *ip6_current_dest_addr());
180*ef8d499eSDavid van Moolenbroek 		pktaddr = &pktaddr6;
181*ef8d499eSDavid van Moolenbroek 		pktaddrlen = sizeof(pktaddr6);
182*ef8d499eSDavid van Moolenbroek 
183*ef8d499eSDavid van Moolenbroek 		assert(pktaddrlen + sizeof(pkthdr) <= IP6_HLEN);
184*ef8d499eSDavid van Moolenbroek 
185*ef8d499eSDavid van Moolenbroek 		pkthdr.tos = IP6H_TC(ip6_current_header());
186*ef8d499eSDavid van Moolenbroek 		pkthdr.ttl = IP6H_HOPLIM(ip6_current_header());
187*ef8d499eSDavid van Moolenbroek 		pkthdr.flags = PKTHF_IPV6;
188*ef8d499eSDavid van Moolenbroek 	} else {
189*ef8d499eSDavid van Moolenbroek 		assert(IP_IS_V4(srcaddr));
190*ef8d499eSDavid van Moolenbroek 		assert(ip4_current_dest_addr() != NULL);
191*ef8d499eSDavid van Moolenbroek 
192*ef8d499eSDavid van Moolenbroek 		memcpy(&pktaddr4.srcaddr, ip_2_ip4(srcaddr),
193*ef8d499eSDavid van Moolenbroek 		    sizeof(pktaddr4.srcaddr));
194*ef8d499eSDavid van Moolenbroek 		memcpy(&pktaddr4.dstaddr, ip4_current_dest_addr(),
195*ef8d499eSDavid van Moolenbroek 		    sizeof(pktaddr4.srcaddr));
196*ef8d499eSDavid van Moolenbroek 		pktaddr = &pktaddr4;
197*ef8d499eSDavid van Moolenbroek 		pktaddrlen = sizeof(pktaddr4);
198*ef8d499eSDavid van Moolenbroek 
199*ef8d499eSDavid van Moolenbroek 		assert(pktaddrlen + sizeof(pkthdr) <= IP_HLEN);
200*ef8d499eSDavid van Moolenbroek 
201*ef8d499eSDavid van Moolenbroek 		pkthdr.tos = IPH_TOS(ip4_current_header());
202*ef8d499eSDavid van Moolenbroek 		pkthdr.ttl = IPH_TTL(ip4_current_header());
203*ef8d499eSDavid van Moolenbroek 		pkthdr.flags = 0;
204*ef8d499eSDavid van Moolenbroek 	}
205*ef8d499eSDavid van Moolenbroek 
206*ef8d499eSDavid van Moolenbroek 	/*
207*ef8d499eSDavid van Moolenbroek 	 * Save both the interface on which the packet was received (for
208*ef8d499eSDavid van Moolenbroek 	 * PKTINFO) and the interface that owns the destination address of the
209*ef8d499eSDavid van Moolenbroek 	 * packet (for the source address's zone ID).
210*ef8d499eSDavid van Moolenbroek 	 */
211*ef8d499eSDavid van Moolenbroek 	assert(ip_current_input_netif() != NULL);
212*ef8d499eSDavid van Moolenbroek 	ifdev = netif_get_ifdev(ip_current_input_netif());
213*ef8d499eSDavid van Moolenbroek 	pkthdr.dstif = (uint16_t)ifdev_get_index(ifdev);
214*ef8d499eSDavid van Moolenbroek 
215*ef8d499eSDavid van Moolenbroek 	assert(ip_current_netif() != NULL);
216*ef8d499eSDavid van Moolenbroek 	ifdev = netif_get_ifdev(ip_current_netif());
217*ef8d499eSDavid van Moolenbroek 	pkthdr.addrif = (uint16_t)ifdev_get_index(ifdev);
218*ef8d499eSDavid van Moolenbroek 
219*ef8d499eSDavid van Moolenbroek 	if ((pbuf->flags & PBUF_FLAG_LLMCAST) ||
220*ef8d499eSDavid van Moolenbroek 	    ip_addr_ismulticast(ip_current_dest_addr()))
221*ef8d499eSDavid van Moolenbroek 		pkthdr.flags |= PKTHF_MCAST;
222*ef8d499eSDavid van Moolenbroek 	else if ((pbuf->flags & PBUF_FLAG_LLBCAST) ||
223*ef8d499eSDavid van Moolenbroek 	    ip_addr_isbroadcast(ip_current_dest_addr(), ip_current_netif()))
224*ef8d499eSDavid van Moolenbroek 		pkthdr.flags |= PKTHF_BCAST;
225*ef8d499eSDavid van Moolenbroek 
226*ef8d499eSDavid van Moolenbroek 	pkthdr.port = port;
227*ef8d499eSDavid van Moolenbroek 
228*ef8d499eSDavid van Moolenbroek 	util_pbuf_header(pbuf, sizeof(pkthdr));
229*ef8d499eSDavid van Moolenbroek 
230*ef8d499eSDavid van Moolenbroek 	memcpy(pbuf->payload, &pkthdr, sizeof(pkthdr));
231*ef8d499eSDavid van Moolenbroek 
232*ef8d499eSDavid van Moolenbroek 	util_pbuf_header(pbuf, pktaddrlen);
233*ef8d499eSDavid van Moolenbroek 
234*ef8d499eSDavid van Moolenbroek 	memcpy(pbuf->payload, pktaddr, pktaddrlen);
235*ef8d499eSDavid van Moolenbroek 
236*ef8d499eSDavid van Moolenbroek 	util_pbuf_header(pbuf, -(int)(sizeof(pkthdr) + pktaddrlen));
237*ef8d499eSDavid van Moolenbroek 
238*ef8d499eSDavid van Moolenbroek 	*pkt->pkt_rcvtailp = pbuf;
239*ef8d499eSDavid van Moolenbroek 	pkt->pkt_rcvtailp = pchain_end(pbuf);
240*ef8d499eSDavid van Moolenbroek 	pkt->pkt_rcvlen += pchain_size(pbuf);
241*ef8d499eSDavid van Moolenbroek 
242*ef8d499eSDavid van Moolenbroek 	sockevent_raise(ipsock_get_sock(&pkt->pkt_ipsock), SEV_RECV);
243*ef8d499eSDavid van Moolenbroek }
244*ef8d499eSDavid van Moolenbroek 
245*ef8d499eSDavid van Moolenbroek /*
246*ef8d499eSDavid van Moolenbroek  * Obtain interface and source address information for an outgoing packet.  In
247*ef8d499eSDavid van Moolenbroek  * particular, parse any IPV6_PKTINFO options provided as either sticky options
248*ef8d499eSDavid van Moolenbroek  * on the socket 'pkt' or as ancillary options in the packet options 'pkto'.
249*ef8d499eSDavid van Moolenbroek  * On success, return OK, with 'ifdevp' set to either the outgoing interface to
250*ef8d499eSDavid van Moolenbroek  * use for the packet, or NULL if no outgoing interface was specified using
251*ef8d499eSDavid van Moolenbroek  * either of the aforementioned options.  If, and only if, 'ifdevp' is set to
252*ef8d499eSDavid van Moolenbroek  * an actual interface (i.e., not NULL), then 'src_addrp' is filled with either
253*ef8d499eSDavid van Moolenbroek  * a locally owned, validated, unicast address to use as source of the packet,
254*ef8d499eSDavid van Moolenbroek  * or the unspecified ('any') address if no source address was specified using
255*ef8d499eSDavid van Moolenbroek  * the options.  On failure, return a negative error code.
256*ef8d499eSDavid van Moolenbroek  */
257*ef8d499eSDavid van Moolenbroek int
pktsock_get_pktinfo(struct pktsock * pkt,struct pktopt * pkto,struct ifdev ** ifdevp,ip_addr_t * src_addrp)258*ef8d499eSDavid van Moolenbroek pktsock_get_pktinfo(struct pktsock * pkt, struct pktopt * pkto,
259*ef8d499eSDavid van Moolenbroek 	struct ifdev ** ifdevp, ip_addr_t * src_addrp)
260*ef8d499eSDavid van Moolenbroek {
261*ef8d499eSDavid van Moolenbroek 	struct ifdev *ifdev, *ifdev2;
262*ef8d499eSDavid van Moolenbroek 	ip_addr_t ipaddr;
263*ef8d499eSDavid van Moolenbroek 	uint32_t ifindex;
264*ef8d499eSDavid van Moolenbroek 	int r;
265*ef8d499eSDavid van Moolenbroek 
266*ef8d499eSDavid van Moolenbroek 	/* We support only IPV6_PKTINFO.  IP_PKTINFO is not supported. */
267*ef8d499eSDavid van Moolenbroek 	if (!ipsock_is_ipv6(&pkt->pkt_ipsock)) {
268*ef8d499eSDavid van Moolenbroek 		*ifdevp = NULL;
269*ef8d499eSDavid van Moolenbroek 		return OK;
270*ef8d499eSDavid van Moolenbroek 	}
271*ef8d499eSDavid van Moolenbroek 
272*ef8d499eSDavid van Moolenbroek 	/*
273*ef8d499eSDavid van Moolenbroek 	 * TODO: we are spending a lot of effort on initializing and copying
274*ef8d499eSDavid van Moolenbroek 	 * stuff around, even just to find out whether there is anything to do
275*ef8d499eSDavid van Moolenbroek 	 * at all here.  See if this can be optimized.
276*ef8d499eSDavid van Moolenbroek 	 */
277*ef8d499eSDavid van Moolenbroek 	ip_addr_set_zero_ip6(&ipaddr);
278*ef8d499eSDavid van Moolenbroek 
279*ef8d499eSDavid van Moolenbroek 	/*
280*ef8d499eSDavid van Moolenbroek 	 * Ancillary data takes precedence over sticky options.  We treat the
281*ef8d499eSDavid van Moolenbroek 	 * source address and interface index fields as separate, overriding
282*ef8d499eSDavid van Moolenbroek 	 * each earlier value only if non-zero.  TODO: is that correct?
283*ef8d499eSDavid van Moolenbroek 	 */
284*ef8d499eSDavid van Moolenbroek 	if (pkto->pkto_flags & PKTOF_PKTINFO) {
285*ef8d499eSDavid van Moolenbroek 		memcpy(ip_2_ip6(&ipaddr)->addr, &pkto->pkto_srcaddr.addr,
286*ef8d499eSDavid van Moolenbroek 		    sizeof(ip_2_ip6(&ipaddr)->addr));
287*ef8d499eSDavid van Moolenbroek 		ifindex = pkto->pkto_ifindex;
288*ef8d499eSDavid van Moolenbroek 	} else
289*ef8d499eSDavid van Moolenbroek 		ifindex = 0;
290*ef8d499eSDavid van Moolenbroek 
291*ef8d499eSDavid van Moolenbroek 	if (ip6_addr_isany(ip_2_ip6(&ipaddr)))
292*ef8d499eSDavid van Moolenbroek 		memcpy(ip_2_ip6(&ipaddr)->addr, &pkt->pkt_srcaddr.addr,
293*ef8d499eSDavid van Moolenbroek 		    sizeof(ip_2_ip6(&ipaddr)->addr));
294*ef8d499eSDavid van Moolenbroek 	if (ifindex == 0)
295*ef8d499eSDavid van Moolenbroek 		ifindex = pkt->pkt_ifindex;
296*ef8d499eSDavid van Moolenbroek 
297*ef8d499eSDavid van Moolenbroek 	/* If both fields are blank, there is nothing more to do. */
298*ef8d499eSDavid van Moolenbroek 	if (ip6_addr_isany(ip_2_ip6(&ipaddr)) && ifindex == 0) {
299*ef8d499eSDavid van Moolenbroek 		*ifdevp = NULL;
300*ef8d499eSDavid van Moolenbroek 		return OK;
301*ef8d499eSDavid van Moolenbroek 	}
302*ef8d499eSDavid van Moolenbroek 
303*ef8d499eSDavid van Moolenbroek 	/* If an interface index is specified, it must be valid. */
304*ef8d499eSDavid van Moolenbroek 	ifdev = NULL;
305*ef8d499eSDavid van Moolenbroek 
306*ef8d499eSDavid van Moolenbroek 	if (ifindex != 0 && (ifdev = ifdev_get_by_index(ifindex)) == NULL)
307*ef8d499eSDavid van Moolenbroek 		return ENXIO;
308*ef8d499eSDavid van Moolenbroek 
309*ef8d499eSDavid van Moolenbroek 	/*
310*ef8d499eSDavid van Moolenbroek 	 * Use the interface index to set a zone on the source address, if the
311*ef8d499eSDavid van Moolenbroek 	 * source address has a scope.
312*ef8d499eSDavid van Moolenbroek 	 */
313*ef8d499eSDavid van Moolenbroek 	if (ip6_addr_has_scope(ip_2_ip6(&ipaddr), IP6_UNKNOWN)) {
314*ef8d499eSDavid van Moolenbroek 		if (ifindex == 0)
315*ef8d499eSDavid van Moolenbroek 			return EADDRNOTAVAIL;
316*ef8d499eSDavid van Moolenbroek 
317*ef8d499eSDavid van Moolenbroek 		ip6_addr_set_zone(ip_2_ip6(&ipaddr), ifindex);
318*ef8d499eSDavid van Moolenbroek 	}
319*ef8d499eSDavid van Moolenbroek 
320*ef8d499eSDavid van Moolenbroek 	/*
321*ef8d499eSDavid van Moolenbroek 	 * We need to validate the given address just as thoroughly as an
322*ef8d499eSDavid van Moolenbroek 	 * address given through bind().  If we don't, we could allow forged
323*ef8d499eSDavid van Moolenbroek 	 * source addresses etcetera.  To be sure: this call may change the
324*ef8d499eSDavid van Moolenbroek 	 * address to an IPv4 type address if needed.
325*ef8d499eSDavid van Moolenbroek 	 */
326*ef8d499eSDavid van Moolenbroek 	if ((r = ipsock_check_src_addr(pktsock_get_ipsock(pkt), &ipaddr,
327*ef8d499eSDavid van Moolenbroek 	    FALSE /*allow_mcast*/, &ifdev2)) != OK)
328*ef8d499eSDavid van Moolenbroek 		return r;
329*ef8d499eSDavid van Moolenbroek 
330*ef8d499eSDavid van Moolenbroek 	if (ifdev2 != NULL) {
331*ef8d499eSDavid van Moolenbroek 		if (ifdev == NULL)
332*ef8d499eSDavid van Moolenbroek 			ifdev = ifdev2;
333*ef8d499eSDavid van Moolenbroek 		else if (ifdev != ifdev2)
334*ef8d499eSDavid van Moolenbroek 			return EADDRNOTAVAIL;
335*ef8d499eSDavid van Moolenbroek 	} else {
336*ef8d499eSDavid van Moolenbroek 		/*
337*ef8d499eSDavid van Moolenbroek 		 * There should be no cases where the (non-multicast) address
338*ef8d499eSDavid van Moolenbroek 		 * successfully parsed, is not unspecified, and yet did not map
339*ef8d499eSDavid van Moolenbroek 		 * to an interface.  Eliminate the possibility anyway by
340*ef8d499eSDavid van Moolenbroek 		 * throwing an error for this case.  As a result, we are left
341*ef8d499eSDavid van Moolenbroek 		 * with one of two cases:
342*ef8d499eSDavid van Moolenbroek 		 *
343*ef8d499eSDavid van Moolenbroek 		 * 1) ifdevp is not NULL, and src_addrp is unspecified;
344*ef8d499eSDavid van Moolenbroek 		 * 2) ifdevp is not NULL, and src_addrp is a locally assigned
345*ef8d499eSDavid van Moolenbroek 		 *    (unicast) address.
346*ef8d499eSDavid van Moolenbroek 		 *
347*ef8d499eSDavid van Moolenbroek 		 * This is why we need not fill src_addrp when ifdevp is NULL.
348*ef8d499eSDavid van Moolenbroek 		 */
349*ef8d499eSDavid van Moolenbroek 		if (!ip_addr_isany(&ipaddr))
350*ef8d499eSDavid van Moolenbroek 			return EADDRNOTAVAIL;
351*ef8d499eSDavid van Moolenbroek 	}
352*ef8d499eSDavid van Moolenbroek 
353*ef8d499eSDavid van Moolenbroek 	*ifdevp = ifdev;
354*ef8d499eSDavid van Moolenbroek 	if (ifdev != NULL)
355*ef8d499eSDavid van Moolenbroek 		*src_addrp = ipaddr;
356*ef8d499eSDavid van Moolenbroek 	return OK;
357*ef8d499eSDavid van Moolenbroek }
358*ef8d499eSDavid van Moolenbroek 
359*ef8d499eSDavid van Moolenbroek /*
360*ef8d499eSDavid van Moolenbroek  * Parse a chunk of user-provided control data, on an IPv4 socket provided as
361*ef8d499eSDavid van Moolenbroek  * 'pkt'.  The control chunk is given as 'cmsg', and the length of the data
362*ef8d499eSDavid van Moolenbroek  * following the control header (possibly zero) is given as 'len'.  On success,
363*ef8d499eSDavid van Moolenbroek  * return OK, with any parsed options merged into the set of packet options
364*ef8d499eSDavid van Moolenbroek  * 'pkto'.  On failure, return a negative error code.
365*ef8d499eSDavid van Moolenbroek  */
366*ef8d499eSDavid van Moolenbroek static int
pktsock_parse_ctl_v4(struct pktsock * pkt __unused,struct cmsghdr * cmsg,socklen_t len,struct pktopt * pkto)367*ef8d499eSDavid van Moolenbroek pktsock_parse_ctl_v4(struct pktsock * pkt __unused, struct cmsghdr * cmsg,
368*ef8d499eSDavid van Moolenbroek 	socklen_t len, struct pktopt * pkto)
369*ef8d499eSDavid van Moolenbroek {
370*ef8d499eSDavid van Moolenbroek 	uint8_t byte;
371*ef8d499eSDavid van Moolenbroek 	int val;
372*ef8d499eSDavid van Moolenbroek 
373*ef8d499eSDavid van Moolenbroek 	if (cmsg->cmsg_level != IPPROTO_IP)
374*ef8d499eSDavid van Moolenbroek 		return EAFNOSUPPORT;
375*ef8d499eSDavid van Moolenbroek 
376*ef8d499eSDavid van Moolenbroek 	switch (cmsg->cmsg_type) {
377*ef8d499eSDavid van Moolenbroek 	case IP_TOS:
378*ef8d499eSDavid van Moolenbroek 		/*
379*ef8d499eSDavid van Moolenbroek 		 * Some userland code (bind's libisc in particular) supplies
380*ef8d499eSDavid van Moolenbroek 		 * a single byte instead of a full integer for this option.
381*ef8d499eSDavid van Moolenbroek 		 * We go out of our way to accept that format, too.
382*ef8d499eSDavid van Moolenbroek 		 */
383*ef8d499eSDavid van Moolenbroek 		if (len != sizeof(val) && len != sizeof(byte))
384*ef8d499eSDavid van Moolenbroek 			return EINVAL;
385*ef8d499eSDavid van Moolenbroek 
386*ef8d499eSDavid van Moolenbroek 		if (len == sizeof(byte)) {
387*ef8d499eSDavid van Moolenbroek 			memcpy(&byte, CMSG_DATA(cmsg), sizeof(byte));
388*ef8d499eSDavid van Moolenbroek 			val = (int)byte;
389*ef8d499eSDavid van Moolenbroek 		} else
390*ef8d499eSDavid van Moolenbroek 			memcpy(&val, CMSG_DATA(cmsg), sizeof(val));
391*ef8d499eSDavid van Moolenbroek 
392*ef8d499eSDavid van Moolenbroek 		if (val < 0 || val > UINT8_MAX)
393*ef8d499eSDavid van Moolenbroek 			return EINVAL;
394*ef8d499eSDavid van Moolenbroek 
395*ef8d499eSDavid van Moolenbroek 		pkto->pkto_flags |= PKTOF_TOS;
396*ef8d499eSDavid van Moolenbroek 		pkto->pkto_tos = (uint8_t)val;
397*ef8d499eSDavid van Moolenbroek 
398*ef8d499eSDavid van Moolenbroek 		return OK;
399*ef8d499eSDavid van Moolenbroek 
400*ef8d499eSDavid van Moolenbroek 	case IP_TTL:
401*ef8d499eSDavid van Moolenbroek 		if (len != sizeof(val))
402*ef8d499eSDavid van Moolenbroek 			return EINVAL;
403*ef8d499eSDavid van Moolenbroek 
404*ef8d499eSDavid van Moolenbroek 		memcpy(&val, CMSG_DATA(cmsg), sizeof(val));
405*ef8d499eSDavid van Moolenbroek 
406*ef8d499eSDavid van Moolenbroek 		if (val < 0 || val > UINT8_MAX)
407*ef8d499eSDavid van Moolenbroek 			return EINVAL;
408*ef8d499eSDavid van Moolenbroek 
409*ef8d499eSDavid van Moolenbroek 		pkto->pkto_flags |= PKTOF_TTL;
410*ef8d499eSDavid van Moolenbroek 		pkto->pkto_ttl = (uint8_t)val;
411*ef8d499eSDavid van Moolenbroek 
412*ef8d499eSDavid van Moolenbroek 		return OK;
413*ef8d499eSDavid van Moolenbroek 
414*ef8d499eSDavid van Moolenbroek 	/*
415*ef8d499eSDavid van Moolenbroek 	 * Implementing IP_PKTINFO might be a bit harder than its IPV6_PKTINFO
416*ef8d499eSDavid van Moolenbroek 	 * sibling, because it would require the use of zone IDs (interface
417*ef8d499eSDavid van Moolenbroek 	 * indices) for IPv4, which is not supported yet.
418*ef8d499eSDavid van Moolenbroek 	 */
419*ef8d499eSDavid van Moolenbroek 	}
420*ef8d499eSDavid van Moolenbroek 
421*ef8d499eSDavid van Moolenbroek 	return EINVAL;
422*ef8d499eSDavid van Moolenbroek }
423*ef8d499eSDavid van Moolenbroek 
424*ef8d499eSDavid van Moolenbroek /*
425*ef8d499eSDavid van Moolenbroek  * Parse a chunk of user-provided control data, on an IPv6 socket provided as
426*ef8d499eSDavid van Moolenbroek  * 'pkt'.  The control chunk is given as 'cmsg', and the length of the data
427*ef8d499eSDavid van Moolenbroek  * following the control header (possibly zero) is given as 'len'.  On success,
428*ef8d499eSDavid van Moolenbroek  * return OK, with any parsed options merged into the set of packet options
429*ef8d499eSDavid van Moolenbroek  * 'pkto'.  On failure, return a negative error code.
430*ef8d499eSDavid van Moolenbroek  */
431*ef8d499eSDavid van Moolenbroek static int
pktsock_parse_ctl_v6(struct pktsock * pkt,struct cmsghdr * cmsg,socklen_t len,struct pktopt * pkto)432*ef8d499eSDavid van Moolenbroek pktsock_parse_ctl_v6(struct pktsock * pkt, struct cmsghdr * cmsg,
433*ef8d499eSDavid van Moolenbroek 	socklen_t len, struct pktopt * pkto)
434*ef8d499eSDavid van Moolenbroek {
435*ef8d499eSDavid van Moolenbroek 	struct in6_pktinfo ipi6;
436*ef8d499eSDavid van Moolenbroek 	int val;
437*ef8d499eSDavid van Moolenbroek 
438*ef8d499eSDavid van Moolenbroek 	if (cmsg->cmsg_level != IPPROTO_IPV6)
439*ef8d499eSDavid van Moolenbroek 		return EAFNOSUPPORT;
440*ef8d499eSDavid van Moolenbroek 
441*ef8d499eSDavid van Moolenbroek 	switch (cmsg->cmsg_type) {
442*ef8d499eSDavid van Moolenbroek 	case IPV6_TCLASS:
443*ef8d499eSDavid van Moolenbroek 		if (len != sizeof(val))
444*ef8d499eSDavid van Moolenbroek 			return EINVAL;
445*ef8d499eSDavid van Moolenbroek 
446*ef8d499eSDavid van Moolenbroek 		memcpy(&val, CMSG_DATA(cmsg), sizeof(val));
447*ef8d499eSDavid van Moolenbroek 
448*ef8d499eSDavid van Moolenbroek 		if (val < -1 || val > UINT8_MAX)
449*ef8d499eSDavid van Moolenbroek 			return EINVAL;
450*ef8d499eSDavid van Moolenbroek 
451*ef8d499eSDavid van Moolenbroek 		if (val == -1)
452*ef8d499eSDavid van Moolenbroek 			val = 0;
453*ef8d499eSDavid van Moolenbroek 
454*ef8d499eSDavid van Moolenbroek 		pkto->pkto_flags |= PKTOF_TOS;
455*ef8d499eSDavid van Moolenbroek 		pkto->pkto_tos = (uint8_t)val;
456*ef8d499eSDavid van Moolenbroek 
457*ef8d499eSDavid van Moolenbroek 		return OK;
458*ef8d499eSDavid van Moolenbroek 
459*ef8d499eSDavid van Moolenbroek 	case IPV6_HOPLIMIT:
460*ef8d499eSDavid van Moolenbroek 		if (len != sizeof(val))
461*ef8d499eSDavid van Moolenbroek 			return EINVAL;
462*ef8d499eSDavid van Moolenbroek 
463*ef8d499eSDavid van Moolenbroek 		memcpy(&val, CMSG_DATA(cmsg), sizeof(val));
464*ef8d499eSDavid van Moolenbroek 
465*ef8d499eSDavid van Moolenbroek 		if (val < -1 || val > UINT8_MAX)
466*ef8d499eSDavid van Moolenbroek 			return EINVAL;
467*ef8d499eSDavid van Moolenbroek 
468*ef8d499eSDavid van Moolenbroek 		if (val == -1)
469*ef8d499eSDavid van Moolenbroek 			val = IP_DEFAULT_TTL;
470*ef8d499eSDavid van Moolenbroek 
471*ef8d499eSDavid van Moolenbroek 		pkto->pkto_flags |= PKTOF_TTL;
472*ef8d499eSDavid van Moolenbroek 		pkto->pkto_ttl = (uint8_t)val;
473*ef8d499eSDavid van Moolenbroek 
474*ef8d499eSDavid van Moolenbroek 		return OK;
475*ef8d499eSDavid van Moolenbroek 
476*ef8d499eSDavid van Moolenbroek 	case IPV6_PKTINFO:
477*ef8d499eSDavid van Moolenbroek 		if (len != sizeof(ipi6))
478*ef8d499eSDavid van Moolenbroek 			return EINVAL;
479*ef8d499eSDavid van Moolenbroek 
480*ef8d499eSDavid van Moolenbroek 		memcpy(&ipi6, CMSG_DATA(cmsg), sizeof(ipi6));
481*ef8d499eSDavid van Moolenbroek 
482*ef8d499eSDavid van Moolenbroek 		pkto->pkto_flags |= PKTOF_PKTINFO;
483*ef8d499eSDavid van Moolenbroek 		memcpy(&pkto->pkto_srcaddr.addr, &ipi6.ipi6_addr,
484*ef8d499eSDavid van Moolenbroek 		    sizeof(pkto->pkto_srcaddr.addr));
485*ef8d499eSDavid van Moolenbroek 		pkto->pkto_ifindex = ipi6.ipi6_ifindex;
486*ef8d499eSDavid van Moolenbroek 
487*ef8d499eSDavid van Moolenbroek 		return OK;
488*ef8d499eSDavid van Moolenbroek 
489*ef8d499eSDavid van Moolenbroek 	case IPV6_USE_MIN_MTU:
490*ef8d499eSDavid van Moolenbroek 		if (len != sizeof(int))
491*ef8d499eSDavid van Moolenbroek 			return EINVAL;
492*ef8d499eSDavid van Moolenbroek 
493*ef8d499eSDavid van Moolenbroek 		memcpy(&val, CMSG_DATA(cmsg), sizeof(val));
494*ef8d499eSDavid van Moolenbroek 
495*ef8d499eSDavid van Moolenbroek 		if (val < -1 || val > 1)
496*ef8d499eSDavid van Moolenbroek 			return EINVAL;
497*ef8d499eSDavid van Moolenbroek 
498*ef8d499eSDavid van Moolenbroek 		/* TODO: not supported by lwIP, but needed by applications. */
499*ef8d499eSDavid van Moolenbroek 		return OK;
500*ef8d499eSDavid van Moolenbroek 	}
501*ef8d499eSDavid van Moolenbroek 
502*ef8d499eSDavid van Moolenbroek 	return EINVAL;
503*ef8d499eSDavid van Moolenbroek }
504*ef8d499eSDavid van Moolenbroek 
505*ef8d499eSDavid van Moolenbroek /*
506*ef8d499eSDavid van Moolenbroek  * Copy in and parse control data, as part of sending a packet on socket 'pkt'.
507*ef8d499eSDavid van Moolenbroek  * The control data is accessible through 'ctl', with a user-provided length of
508*ef8d499eSDavid van Moolenbroek  * 'ctl_len'.  On success, return OK, with any parsed packet options stored in
509*ef8d499eSDavid van Moolenbroek  * 'pkto'.  On failure, return a negative error code.
510*ef8d499eSDavid van Moolenbroek  */
511*ef8d499eSDavid van Moolenbroek int
pktsock_get_ctl(struct pktsock * pkt,const struct sockdriver_data * ctl,socklen_t ctl_len,struct pktopt * pkto)512*ef8d499eSDavid van Moolenbroek pktsock_get_ctl(struct pktsock * pkt, const struct sockdriver_data * ctl,
513*ef8d499eSDavid van Moolenbroek 	socklen_t ctl_len, struct pktopt * pkto)
514*ef8d499eSDavid van Moolenbroek {
515*ef8d499eSDavid van Moolenbroek 	struct msghdr msghdr;
516*ef8d499eSDavid van Moolenbroek 	struct cmsghdr *cmsg;
517*ef8d499eSDavid van Moolenbroek 	socklen_t left, len;
518*ef8d499eSDavid van Moolenbroek 	int r;
519*ef8d499eSDavid van Moolenbroek 
520*ef8d499eSDavid van Moolenbroek 	/* The default: no packet options are being overridden. */
521*ef8d499eSDavid van Moolenbroek 	assert(pkto->pkto_flags == 0);
522*ef8d499eSDavid van Moolenbroek 
523*ef8d499eSDavid van Moolenbroek 	/* If no control length is given, we are done here. */
524*ef8d499eSDavid van Moolenbroek 	if (ctl_len == 0)
525*ef8d499eSDavid van Moolenbroek 		return OK;
526*ef8d499eSDavid van Moolenbroek 
527*ef8d499eSDavid van Moolenbroek 	/*
528*ef8d499eSDavid van Moolenbroek 	 * For now, we put a rather aggressive limit on the size of the control
529*ef8d499eSDavid van Moolenbroek 	 * data.  We copy in and parse the whole thing in a single buffer.
530*ef8d499eSDavid van Moolenbroek 	 */
531*ef8d499eSDavid van Moolenbroek 	if (ctl_len > sizeof(pktsock_ctlbuf)) {
532*ef8d499eSDavid van Moolenbroek 		printf("LWIP: too much control data given (%u bytes)\n",
533*ef8d499eSDavid van Moolenbroek 		    ctl_len);
534*ef8d499eSDavid van Moolenbroek 
535*ef8d499eSDavid van Moolenbroek 		return ENOBUFS;
536*ef8d499eSDavid van Moolenbroek 	}
537*ef8d499eSDavid van Moolenbroek 
538*ef8d499eSDavid van Moolenbroek 	if ((r = sockdriver_copyin(ctl, 0, pktsock_ctlbuf, ctl_len)) != OK)
539*ef8d499eSDavid van Moolenbroek 		return r;
540*ef8d499eSDavid van Moolenbroek 
541*ef8d499eSDavid van Moolenbroek 	memset(&msghdr, 0, sizeof(msghdr));
542*ef8d499eSDavid van Moolenbroek 	msghdr.msg_control = pktsock_ctlbuf;
543*ef8d499eSDavid van Moolenbroek 	msghdr.msg_controllen = ctl_len;
544*ef8d499eSDavid van Moolenbroek 
545*ef8d499eSDavid van Moolenbroek 	for (cmsg = CMSG_FIRSTHDR(&msghdr); cmsg != NULL;
546*ef8d499eSDavid van Moolenbroek 	    cmsg = CMSG_NXTHDR(&msghdr, cmsg)) {
547*ef8d499eSDavid van Moolenbroek 		/* Check for bogus lengths. */
548*ef8d499eSDavid van Moolenbroek 		assert((socklen_t)((char *)cmsg - pktsock_ctlbuf) <= ctl_len);
549*ef8d499eSDavid van Moolenbroek 		left = ctl_len - (socklen_t)((char *)cmsg - pktsock_ctlbuf);
550*ef8d499eSDavid van Moolenbroek 		assert(left >= CMSG_LEN(0)); /* guaranteed by CMSG_xxHDR */
551*ef8d499eSDavid van Moolenbroek 
552*ef8d499eSDavid van Moolenbroek 		if (cmsg->cmsg_len < CMSG_LEN(0) || cmsg->cmsg_len > left) {
553*ef8d499eSDavid van Moolenbroek 			printf("LWIP: malformed control data rejected\n");
554*ef8d499eSDavid van Moolenbroek 
555*ef8d499eSDavid van Moolenbroek 			return EINVAL;
556*ef8d499eSDavid van Moolenbroek 		}
557*ef8d499eSDavid van Moolenbroek 
558*ef8d499eSDavid van Moolenbroek 		len = cmsg->cmsg_len - CMSG_LEN(0);
559*ef8d499eSDavid van Moolenbroek 
560*ef8d499eSDavid van Moolenbroek 		if (ipsock_is_ipv6(&pkt->pkt_ipsock))
561*ef8d499eSDavid van Moolenbroek 			r = pktsock_parse_ctl_v6(pkt, cmsg, len, pkto);
562*ef8d499eSDavid van Moolenbroek 		else
563*ef8d499eSDavid van Moolenbroek 			r = pktsock_parse_ctl_v4(pkt, cmsg, len, pkto);
564*ef8d499eSDavid van Moolenbroek 
565*ef8d499eSDavid van Moolenbroek 		if (r != OK)
566*ef8d499eSDavid van Moolenbroek 			return r;
567*ef8d499eSDavid van Moolenbroek 	}
568*ef8d499eSDavid van Moolenbroek 
569*ef8d499eSDavid van Moolenbroek 	return OK;
570*ef8d499eSDavid van Moolenbroek }
571*ef8d499eSDavid van Moolenbroek 
572*ef8d499eSDavid van Moolenbroek /*
573*ef8d499eSDavid van Moolenbroek  * Copy in the packet data from the calling user process, and store it in the
574*ef8d499eSDavid van Moolenbroek  * buffer 'pbuf' that must already have been allocated with the appropriate
575*ef8d499eSDavid van Moolenbroek  * size.
576*ef8d499eSDavid van Moolenbroek  */
577*ef8d499eSDavid van Moolenbroek int
pktsock_get_data(struct pktsock * pkt,const struct sockdriver_data * data,size_t len,struct pbuf * pbuf)578*ef8d499eSDavid van Moolenbroek pktsock_get_data(struct pktsock * pkt, const struct sockdriver_data * data,
579*ef8d499eSDavid van Moolenbroek 	size_t len, struct pbuf * pbuf)
580*ef8d499eSDavid van Moolenbroek 
581*ef8d499eSDavid van Moolenbroek {
582*ef8d499eSDavid van Moolenbroek 
583*ef8d499eSDavid van Moolenbroek 	return util_copy_data(data, len, 0, pbuf, 0, TRUE /*copy_in*/);
584*ef8d499eSDavid van Moolenbroek }
585*ef8d499eSDavid van Moolenbroek 
586*ef8d499eSDavid van Moolenbroek /*
587*ef8d499eSDavid van Moolenbroek  * Dequeue and free the head of the receive queue of a packet socket.
588*ef8d499eSDavid van Moolenbroek  */
589*ef8d499eSDavid van Moolenbroek static void
pktsock_dequeue(struct pktsock * pkt)590*ef8d499eSDavid van Moolenbroek pktsock_dequeue(struct pktsock * pkt)
591*ef8d499eSDavid van Moolenbroek {
592*ef8d499eSDavid van Moolenbroek 	struct pbuf *pbuf, **pnext;
593*ef8d499eSDavid van Moolenbroek 	size_t size;
594*ef8d499eSDavid van Moolenbroek 
595*ef8d499eSDavid van Moolenbroek 	pbuf = pkt->pkt_rcvhead;
596*ef8d499eSDavid van Moolenbroek 	assert(pbuf != NULL);
597*ef8d499eSDavid van Moolenbroek 
598*ef8d499eSDavid van Moolenbroek 	pnext = pchain_end(pbuf);
599*ef8d499eSDavid van Moolenbroek 	size = pchain_size(pbuf);
600*ef8d499eSDavid van Moolenbroek 
601*ef8d499eSDavid van Moolenbroek 	if ((pkt->pkt_rcvhead = *pnext) == NULL)
602*ef8d499eSDavid van Moolenbroek 		pkt->pkt_rcvtailp = &pkt->pkt_rcvhead;
603*ef8d499eSDavid van Moolenbroek 
604*ef8d499eSDavid van Moolenbroek 	assert(pkt->pkt_rcvlen >= size);
605*ef8d499eSDavid van Moolenbroek 	pkt->pkt_rcvlen -= size;
606*ef8d499eSDavid van Moolenbroek 
607*ef8d499eSDavid van Moolenbroek 	*pnext = NULL;
608*ef8d499eSDavid van Moolenbroek 	pbuf_free(pbuf);
609*ef8d499eSDavid van Moolenbroek }
610*ef8d499eSDavid van Moolenbroek 
611*ef8d499eSDavid van Moolenbroek /*
612*ef8d499eSDavid van Moolenbroek  * Perform preliminary checks on a receive request.
613*ef8d499eSDavid van Moolenbroek  */
614*ef8d499eSDavid van Moolenbroek int
pktsock_pre_recv(struct sock * sock __unused,endpoint_t user_endpt __unused,int flags)615*ef8d499eSDavid van Moolenbroek pktsock_pre_recv(struct sock * sock __unused, endpoint_t user_endpt __unused,
616*ef8d499eSDavid van Moolenbroek 	int flags)
617*ef8d499eSDavid van Moolenbroek {
618*ef8d499eSDavid van Moolenbroek 
619*ef8d499eSDavid van Moolenbroek 	/*
620*ef8d499eSDavid van Moolenbroek 	 * We accept the same flags across all socket types in LWIP, and then
621*ef8d499eSDavid van Moolenbroek 	 * simply ignore the ones we do not support for packet sockets.
622*ef8d499eSDavid van Moolenbroek 	 */
623*ef8d499eSDavid van Moolenbroek 	if ((flags & ~(MSG_PEEK | MSG_WAITALL)) != 0)
624*ef8d499eSDavid van Moolenbroek 		return EOPNOTSUPP;
625*ef8d499eSDavid van Moolenbroek 
626*ef8d499eSDavid van Moolenbroek 	return OK;
627*ef8d499eSDavid van Moolenbroek }
628*ef8d499eSDavid van Moolenbroek 
629*ef8d499eSDavid van Moolenbroek /*
630*ef8d499eSDavid van Moolenbroek  * Add a chunk of control data to the global control buffer, starting from
631*ef8d499eSDavid van Moolenbroek  * offset 'off'.  The chunk has the given level and type, and its data is given
632*ef8d499eSDavid van Moolenbroek  * in the buffer 'ptr' with size 'len'.  Return the (padded) size of the chunk
633*ef8d499eSDavid van Moolenbroek  * that was generated as a result.
634*ef8d499eSDavid van Moolenbroek  */
635*ef8d499eSDavid van Moolenbroek static size_t
pktsock_add_ctl(int level,int type,void * ptr,socklen_t len,size_t off)636*ef8d499eSDavid van Moolenbroek pktsock_add_ctl(int level, int type, void * ptr, socklen_t len, size_t off)
637*ef8d499eSDavid van Moolenbroek {
638*ef8d499eSDavid van Moolenbroek 	struct cmsghdr cmsg;
639*ef8d499eSDavid van Moolenbroek 	size_t size;
640*ef8d499eSDavid van Moolenbroek 
641*ef8d499eSDavid van Moolenbroek 	size = CMSG_SPACE(len);
642*ef8d499eSDavid van Moolenbroek 
643*ef8d499eSDavid van Moolenbroek 	/*
644*ef8d499eSDavid van Moolenbroek 	 * The global control buffer must be large enough to store one chunk
645*ef8d499eSDavid van Moolenbroek 	 * of each of the supported options.  If this panic triggers, increase
646*ef8d499eSDavid van Moolenbroek 	 * PKTSOCK_CTLBUF_SIZE by as much as needed.
647*ef8d499eSDavid van Moolenbroek 	 */
648*ef8d499eSDavid van Moolenbroek 	if (off + size > sizeof(pktsock_ctlbuf))
649*ef8d499eSDavid van Moolenbroek 		panic("control buffer too small, increase "
650*ef8d499eSDavid van Moolenbroek 		    "PKTSOCK_CTLBUF_SIZE");
651*ef8d499eSDavid van Moolenbroek 
652*ef8d499eSDavid van Moolenbroek 	memset(&cmsg, 0, sizeof(cmsg));
653*ef8d499eSDavid van Moolenbroek 	cmsg.cmsg_len = CMSG_LEN(len);
654*ef8d499eSDavid van Moolenbroek 	cmsg.cmsg_level = level;
655*ef8d499eSDavid van Moolenbroek 	cmsg.cmsg_type = type;
656*ef8d499eSDavid van Moolenbroek 
657*ef8d499eSDavid van Moolenbroek 	/*
658*ef8d499eSDavid van Moolenbroek 	 * Clear any padding space.  This can be optimized, but in any case we
659*ef8d499eSDavid van Moolenbroek 	 * must be careful not to copy out any bytes that have not been
660*ef8d499eSDavid van Moolenbroek 	 * initialized at all.
661*ef8d499eSDavid van Moolenbroek 	 */
662*ef8d499eSDavid van Moolenbroek 	memset(&pktsock_ctlbuf[off], 0, size);
663*ef8d499eSDavid van Moolenbroek 
664*ef8d499eSDavid van Moolenbroek 	memcpy(&pktsock_ctlbuf[off], &cmsg, sizeof(cmsg));
665*ef8d499eSDavid van Moolenbroek 	memcpy(CMSG_DATA((struct cmsghdr *)&pktsock_ctlbuf[off]), ptr, len);
666*ef8d499eSDavid van Moolenbroek 
667*ef8d499eSDavid van Moolenbroek 	return size;
668*ef8d499eSDavid van Moolenbroek }
669*ef8d499eSDavid van Moolenbroek 
670*ef8d499eSDavid van Moolenbroek /*
671*ef8d499eSDavid van Moolenbroek  * Generate and copy out control data, as part of delivering a packet from
672*ef8d499eSDavid van Moolenbroek  * socket 'pkt' to userland.  The control data buffer is given as 'ctl', with
673*ef8d499eSDavid van Moolenbroek  * a user-given length of 'ctl_len' bytes.  The packet's header information is
674*ef8d499eSDavid van Moolenbroek  * provided as 'pkthdr', and its source and destination addresses as 'pktaddr',
675*ef8d499eSDavid van Moolenbroek  * which maybe a pktaddr4 or pktaddr6 structure depending on the value of the
676*ef8d499eSDavid van Moolenbroek  * PKTHF_IPV6 flag in the 'flags' field in 'pkthdr'.  Note that we support
677*ef8d499eSDavid van Moolenbroek  * dual-stack sockets, and as such it is possible that the socket is of domain
678*ef8d499eSDavid van Moolenbroek  * AF_INET6 while the received packet is an IPv4 packet.  On success, return
679*ef8d499eSDavid van Moolenbroek  * the size of the control data copied out (possibly zero).  If more control
680*ef8d499eSDavid van Moolenbroek  * data were generated than copied out, also merge the MSG_CTRUNC flag into
681*ef8d499eSDavid van Moolenbroek  * 'rflags'.  On failure, return a negative error code.
682*ef8d499eSDavid van Moolenbroek  */
683*ef8d499eSDavid van Moolenbroek static int
pktsock_put_ctl(struct pktsock * pkt,const struct sockdriver_data * ctl,socklen_t ctl_len,struct pkthdr * pkthdr,void * pktaddr,int * rflags)684*ef8d499eSDavid van Moolenbroek pktsock_put_ctl(struct pktsock * pkt, const struct sockdriver_data * ctl,
685*ef8d499eSDavid van Moolenbroek 	socklen_t ctl_len, struct pkthdr * pkthdr, void * pktaddr,
686*ef8d499eSDavid van Moolenbroek 	int * rflags)
687*ef8d499eSDavid van Moolenbroek {
688*ef8d499eSDavid van Moolenbroek 	struct pktaddr6 *pktaddr6;
689*ef8d499eSDavid van Moolenbroek 	struct pktaddr4 *pktaddr4;
690*ef8d499eSDavid van Moolenbroek 	struct in_pktinfo ipi;
691*ef8d499eSDavid van Moolenbroek 	struct in6_pktinfo ipi6;
692*ef8d499eSDavid van Moolenbroek 	ip_addr_t ipaddr;
693*ef8d499eSDavid van Moolenbroek 	unsigned int flags;
694*ef8d499eSDavid van Moolenbroek 	uint8_t byte;
695*ef8d499eSDavid van Moolenbroek 	size_t off;
696*ef8d499eSDavid van Moolenbroek 	int r, val;
697*ef8d499eSDavid van Moolenbroek 
698*ef8d499eSDavid van Moolenbroek 	flags = ipsock_get_flags(&pkt->pkt_ipsock);
699*ef8d499eSDavid van Moolenbroek 
700*ef8d499eSDavid van Moolenbroek 	if (!(flags & (PKTF_RECVINFO | PKTF_RECVTOS | PKTF_RECVTTL)))
701*ef8d499eSDavid van Moolenbroek 		return 0;
702*ef8d499eSDavid van Moolenbroek 
703*ef8d499eSDavid van Moolenbroek 	/*
704*ef8d499eSDavid van Moolenbroek 	 * Important: all generated control chunks must fit in the global
705*ef8d499eSDavid van Moolenbroek 	 * control buffer together.  When adding more options here, ensure that
706*ef8d499eSDavid van Moolenbroek 	 * the control buffer remains large enough to receive all options at
707*ef8d499eSDavid van Moolenbroek 	 * once.  See also the panic in pktsock_add_ctl().
708*ef8d499eSDavid van Moolenbroek 	 */
709*ef8d499eSDavid van Moolenbroek 	off = 0;
710*ef8d499eSDavid van Moolenbroek 
711*ef8d499eSDavid van Moolenbroek 	/*
712*ef8d499eSDavid van Moolenbroek 	 * IPv6 sockets may receive IPv4 packets.  The ancillary data is in the
713*ef8d499eSDavid van Moolenbroek 	 * format corresponding to the socket, which means we may have to
714*ef8d499eSDavid van Moolenbroek 	 * convert any IPv4 addresses from the packet to IPv4-mapped IPv6
715*ef8d499eSDavid van Moolenbroek 	 * addresses for the ancillary data, just like the source address.
716*ef8d499eSDavid van Moolenbroek 	 */
717*ef8d499eSDavid van Moolenbroek 	if (ipsock_is_ipv6(&pkt->pkt_ipsock)) {
718*ef8d499eSDavid van Moolenbroek 		if (flags & PKTF_RECVTTL) {
719*ef8d499eSDavid van Moolenbroek 			val = pkthdr->ttl;
720*ef8d499eSDavid van Moolenbroek 
721*ef8d499eSDavid van Moolenbroek 			off += pktsock_add_ctl(IPPROTO_IPV6, IPV6_HOPLIMIT,
722*ef8d499eSDavid van Moolenbroek 			    &val, sizeof(val), off);
723*ef8d499eSDavid van Moolenbroek 		}
724*ef8d499eSDavid van Moolenbroek 
725*ef8d499eSDavid van Moolenbroek 		if (flags & PKTF_RECVTOS) {
726*ef8d499eSDavid van Moolenbroek 			val = pkthdr->tos;
727*ef8d499eSDavid van Moolenbroek 
728*ef8d499eSDavid van Moolenbroek 			off += pktsock_add_ctl(IPPROTO_IPV6, IPV6_TCLASS, &val,
729*ef8d499eSDavid van Moolenbroek 			    sizeof(val), off);
730*ef8d499eSDavid van Moolenbroek 		}
731*ef8d499eSDavid van Moolenbroek 
732*ef8d499eSDavid van Moolenbroek 		if (flags & PKTF_RECVINFO) {
733*ef8d499eSDavid van Moolenbroek 			memset(&ipi6, 0, sizeof(ipi6));
734*ef8d499eSDavid van Moolenbroek 
735*ef8d499eSDavid van Moolenbroek 			if (pkthdr->flags & PKTHF_IPV6) {
736*ef8d499eSDavid van Moolenbroek 				pktaddr6 = (struct pktaddr6 *)pktaddr;
737*ef8d499eSDavid van Moolenbroek 				memcpy(&ipi6.ipi6_addr, &pktaddr6->dstaddr,
738*ef8d499eSDavid van Moolenbroek 				    sizeof(ipi6.ipi6_addr));
739*ef8d499eSDavid van Moolenbroek 			} else {
740*ef8d499eSDavid van Moolenbroek 				pktaddr4 = (struct pktaddr4 *)pktaddr;
741*ef8d499eSDavid van Moolenbroek 
742*ef8d499eSDavid van Moolenbroek 				addr_make_v4mapped_v6(&ipaddr,
743*ef8d499eSDavid van Moolenbroek 				    &pktaddr4->dstaddr);
744*ef8d499eSDavid van Moolenbroek 
745*ef8d499eSDavid van Moolenbroek 				memcpy(&ipi6.ipi6_addr,
746*ef8d499eSDavid van Moolenbroek 				    ip_2_ip6(&ipaddr)->addr,
747*ef8d499eSDavid van Moolenbroek 				    sizeof(ipi6.ipi6_addr));
748*ef8d499eSDavid van Moolenbroek 			}
749*ef8d499eSDavid van Moolenbroek 			ipi6.ipi6_ifindex = pkthdr->dstif;
750*ef8d499eSDavid van Moolenbroek 
751*ef8d499eSDavid van Moolenbroek 			off += pktsock_add_ctl(IPPROTO_IPV6, IPV6_PKTINFO,
752*ef8d499eSDavid van Moolenbroek 			    &ipi6, sizeof(ipi6), off);
753*ef8d499eSDavid van Moolenbroek 		}
754*ef8d499eSDavid van Moolenbroek 	} else {
755*ef8d499eSDavid van Moolenbroek 		if (flags & PKTF_RECVTTL) {
756*ef8d499eSDavid van Moolenbroek 			byte = pkthdr->ttl;
757*ef8d499eSDavid van Moolenbroek 
758*ef8d499eSDavid van Moolenbroek 			off += pktsock_add_ctl(IPPROTO_IP, IP_TTL, &byte,
759*ef8d499eSDavid van Moolenbroek 			    sizeof(byte), off);
760*ef8d499eSDavid van Moolenbroek 		}
761*ef8d499eSDavid van Moolenbroek 
762*ef8d499eSDavid van Moolenbroek 		if (flags & PKTF_RECVINFO) {
763*ef8d499eSDavid van Moolenbroek 			assert(!(pkthdr->flags & PKTHF_IPV6));
764*ef8d499eSDavid van Moolenbroek 			pktaddr4 = (struct pktaddr4 *)pktaddr;
765*ef8d499eSDavid van Moolenbroek 
766*ef8d499eSDavid van Moolenbroek 			memset(&ipi, 0, sizeof(ipi));
767*ef8d499eSDavid van Moolenbroek 			memcpy(&ipi.ipi_addr, &pktaddr4->dstaddr,
768*ef8d499eSDavid van Moolenbroek 			    sizeof(ipi.ipi_addr));
769*ef8d499eSDavid van Moolenbroek 			ipi.ipi_ifindex = pkthdr->dstif;
770*ef8d499eSDavid van Moolenbroek 
771*ef8d499eSDavid van Moolenbroek 			off += pktsock_add_ctl(IPPROTO_IP, IP_PKTINFO, &ipi,
772*ef8d499eSDavid van Moolenbroek 			    sizeof(ipi), off);
773*ef8d499eSDavid van Moolenbroek 		}
774*ef8d499eSDavid van Moolenbroek 	}
775*ef8d499eSDavid van Moolenbroek 
776*ef8d499eSDavid van Moolenbroek 	assert(off > 0);
777*ef8d499eSDavid van Moolenbroek 
778*ef8d499eSDavid van Moolenbroek 	if (ctl_len >= off)
779*ef8d499eSDavid van Moolenbroek 		ctl_len = off;
780*ef8d499eSDavid van Moolenbroek 	else
781*ef8d499eSDavid van Moolenbroek 		*rflags |= MSG_CTRUNC;
782*ef8d499eSDavid van Moolenbroek 
783*ef8d499eSDavid van Moolenbroek 	if (ctl_len > 0 &&
784*ef8d499eSDavid van Moolenbroek 	    (r = sockdriver_copyout(ctl, 0, pktsock_ctlbuf, ctl_len)) != OK)
785*ef8d499eSDavid van Moolenbroek 		return r;
786*ef8d499eSDavid van Moolenbroek 
787*ef8d499eSDavid van Moolenbroek 	return ctl_len;
788*ef8d499eSDavid van Moolenbroek }
789*ef8d499eSDavid van Moolenbroek 
790*ef8d499eSDavid van Moolenbroek /*
791*ef8d499eSDavid van Moolenbroek  * Receive data on a packet socket.
792*ef8d499eSDavid van Moolenbroek  */
793*ef8d499eSDavid van Moolenbroek int
pktsock_recv(struct sock * sock,const struct sockdriver_data * data,size_t len,size_t * off,const struct sockdriver_data * ctl,socklen_t ctl_len,socklen_t * ctl_off,struct sockaddr * addr,socklen_t * addr_len,endpoint_t user_endpt __unused,int flags,size_t min __unused,int * rflags)794*ef8d499eSDavid van Moolenbroek pktsock_recv(struct sock * sock, const struct sockdriver_data * data,
795*ef8d499eSDavid van Moolenbroek 	size_t len, size_t * off, const struct sockdriver_data * ctl,
796*ef8d499eSDavid van Moolenbroek 	socklen_t ctl_len, socklen_t * ctl_off, struct sockaddr * addr,
797*ef8d499eSDavid van Moolenbroek 	socklen_t * addr_len, endpoint_t user_endpt __unused, int flags,
798*ef8d499eSDavid van Moolenbroek 	size_t min __unused, int * rflags)
799*ef8d499eSDavid van Moolenbroek {
800*ef8d499eSDavid van Moolenbroek 	struct pktsock *pkt = (struct pktsock *)sock;
801*ef8d499eSDavid van Moolenbroek 	struct pktaddr4 pktaddr4;
802*ef8d499eSDavid van Moolenbroek 	struct pktaddr6 pktaddr6;
803*ef8d499eSDavid van Moolenbroek 	struct pkthdr pkthdr;
804*ef8d499eSDavid van Moolenbroek 	void *pktaddr;
805*ef8d499eSDavid van Moolenbroek 	struct pbuf *pbuf;
806*ef8d499eSDavid van Moolenbroek 	ip_addr_t srcaddr;
807*ef8d499eSDavid van Moolenbroek 	int r;
808*ef8d499eSDavid van Moolenbroek 
809*ef8d499eSDavid van Moolenbroek 	if ((pbuf = pkt->pkt_rcvhead) == NULL)
810*ef8d499eSDavid van Moolenbroek 		return SUSPEND;
811*ef8d499eSDavid van Moolenbroek 
812*ef8d499eSDavid van Moolenbroek 	/*
813*ef8d499eSDavid van Moolenbroek 	 * Get the ancillary data for the packet.  The format of the ancillary
814*ef8d499eSDavid van Moolenbroek 	 * data depends on the received packet type, which may be different
815*ef8d499eSDavid van Moolenbroek 	 * from the socket type.
816*ef8d499eSDavid van Moolenbroek 	 */
817*ef8d499eSDavid van Moolenbroek 	util_pbuf_header(pbuf, sizeof(pkthdr));
818*ef8d499eSDavid van Moolenbroek 
819*ef8d499eSDavid van Moolenbroek 	memcpy(&pkthdr, pbuf->payload, sizeof(pkthdr));
820*ef8d499eSDavid van Moolenbroek 
821*ef8d499eSDavid van Moolenbroek 	if (pkthdr.flags & PKTHF_IPV6) {
822*ef8d499eSDavid van Moolenbroek 		util_pbuf_header(pbuf, sizeof(pktaddr6));
823*ef8d499eSDavid van Moolenbroek 
824*ef8d499eSDavid van Moolenbroek 		memcpy(&pktaddr6, pbuf->payload, sizeof(pktaddr6));
825*ef8d499eSDavid van Moolenbroek 		pktaddr = &pktaddr6;
826*ef8d499eSDavid van Moolenbroek 
827*ef8d499eSDavid van Moolenbroek 		ip_addr_copy_from_ip6_packed(srcaddr, pktaddr6.srcaddr);
828*ef8d499eSDavid van Moolenbroek 		if (ip6_addr_has_scope(ip_2_ip6(&srcaddr), IP6_UNICAST))
829*ef8d499eSDavid van Moolenbroek 			ip6_addr_set_zone(ip_2_ip6(&srcaddr), pkthdr.addrif);
830*ef8d499eSDavid van Moolenbroek 
831*ef8d499eSDavid van Moolenbroek 		util_pbuf_header(pbuf,
832*ef8d499eSDavid van Moolenbroek 		    -(int)(sizeof(pkthdr) + sizeof(pktaddr6)));
833*ef8d499eSDavid van Moolenbroek 	} else {
834*ef8d499eSDavid van Moolenbroek 		util_pbuf_header(pbuf, sizeof(pktaddr4));
835*ef8d499eSDavid van Moolenbroek 
836*ef8d499eSDavid van Moolenbroek 		memcpy(&pktaddr4, pbuf->payload, sizeof(pktaddr4));
837*ef8d499eSDavid van Moolenbroek 		pktaddr = &pktaddr4;
838*ef8d499eSDavid van Moolenbroek 
839*ef8d499eSDavid van Moolenbroek 		ip_addr_copy_from_ip4(srcaddr, pktaddr4.srcaddr);
840*ef8d499eSDavid van Moolenbroek 
841*ef8d499eSDavid van Moolenbroek 		util_pbuf_header(pbuf,
842*ef8d499eSDavid van Moolenbroek 		    -(int)(sizeof(pkthdr) + sizeof(pktaddr4)));
843*ef8d499eSDavid van Moolenbroek 	}
844*ef8d499eSDavid van Moolenbroek 
845*ef8d499eSDavid van Moolenbroek 	/* Copy out the packet data to the calling user process. */
846*ef8d499eSDavid van Moolenbroek 	if (len >= pbuf->tot_len)
847*ef8d499eSDavid van Moolenbroek 		len = pbuf->tot_len;
848*ef8d499eSDavid van Moolenbroek 	else
849*ef8d499eSDavid van Moolenbroek 		*rflags |= MSG_TRUNC;
850*ef8d499eSDavid van Moolenbroek 
851*ef8d499eSDavid van Moolenbroek 	r = util_copy_data(data, len, 0, pbuf, 0, FALSE /*copy_in*/);
852*ef8d499eSDavid van Moolenbroek 
853*ef8d499eSDavid van Moolenbroek 	if (r != OK)
854*ef8d499eSDavid van Moolenbroek 		return r;
855*ef8d499eSDavid van Moolenbroek 
856*ef8d499eSDavid van Moolenbroek 	/* Generate and copy out ancillary (control) data, if requested. */
857*ef8d499eSDavid van Moolenbroek 	if ((r = pktsock_put_ctl(pkt, ctl, ctl_len, &pkthdr, pktaddr,
858*ef8d499eSDavid van Moolenbroek 	    rflags)) < 0)
859*ef8d499eSDavid van Moolenbroek 		return r;
860*ef8d499eSDavid van Moolenbroek 
861*ef8d499eSDavid van Moolenbroek 	/* Store the source IP address. */
862*ef8d499eSDavid van Moolenbroek 	ipsock_put_addr(&pkt->pkt_ipsock, addr, addr_len, &srcaddr,
863*ef8d499eSDavid van Moolenbroek 	    pkthdr.port);
864*ef8d499eSDavid van Moolenbroek 
865*ef8d499eSDavid van Moolenbroek 	/* Set multicast or broadcast message flag, if applicable. */
866*ef8d499eSDavid van Moolenbroek 	if (pkthdr.flags & PKTHF_MCAST)
867*ef8d499eSDavid van Moolenbroek 		*rflags |= MSG_MCAST;
868*ef8d499eSDavid van Moolenbroek 	else if (pkthdr.flags & PKTHF_BCAST)
869*ef8d499eSDavid van Moolenbroek 		*rflags |= MSG_BCAST;
870*ef8d499eSDavid van Moolenbroek 
871*ef8d499eSDavid van Moolenbroek 	/* Discard the packet now, unless we were instructed to peek only. */
872*ef8d499eSDavid van Moolenbroek 	if (!(flags & MSG_PEEK))
873*ef8d499eSDavid van Moolenbroek 		pktsock_dequeue(pkt);
874*ef8d499eSDavid van Moolenbroek 
875*ef8d499eSDavid van Moolenbroek 	/* Return the received part of the packet length. */
876*ef8d499eSDavid van Moolenbroek 	*off = len;
877*ef8d499eSDavid van Moolenbroek 	*ctl_off = r;
878*ef8d499eSDavid van Moolenbroek 	return OK;
879*ef8d499eSDavid van Moolenbroek }
880*ef8d499eSDavid van Moolenbroek 
881*ef8d499eSDavid van Moolenbroek /*
882*ef8d499eSDavid van Moolenbroek  * Test whether data can be received on a packet socket, and if so, how many
883*ef8d499eSDavid van Moolenbroek  * bytes of data.
884*ef8d499eSDavid van Moolenbroek  */
885*ef8d499eSDavid van Moolenbroek int
pktsock_test_recv(struct sock * sock,size_t min __unused,size_t * size)886*ef8d499eSDavid van Moolenbroek pktsock_test_recv(struct sock * sock, size_t min __unused, size_t * size)
887*ef8d499eSDavid van Moolenbroek {
888*ef8d499eSDavid van Moolenbroek 	struct pktsock *pkt = (struct pktsock *)sock;
889*ef8d499eSDavid van Moolenbroek 
890*ef8d499eSDavid van Moolenbroek 	if (pkt->pkt_rcvhead == NULL)
891*ef8d499eSDavid van Moolenbroek 		return SUSPEND;
892*ef8d499eSDavid van Moolenbroek 
893*ef8d499eSDavid van Moolenbroek 	if (size != NULL)
894*ef8d499eSDavid van Moolenbroek 		*size = pkt->pkt_rcvhead->tot_len;
895*ef8d499eSDavid van Moolenbroek 	return OK;
896*ef8d499eSDavid van Moolenbroek }
897*ef8d499eSDavid van Moolenbroek 
898*ef8d499eSDavid van Moolenbroek /*
899*ef8d499eSDavid van Moolenbroek  * The caller has performed a multicast operation on the given socket.  Thus,
900*ef8d499eSDavid van Moolenbroek  * the caller is multicast aware.  Remember this, because that means the socket
901*ef8d499eSDavid van Moolenbroek  * may also receive traffic to multicast destinations.
902*ef8d499eSDavid van Moolenbroek  */
903*ef8d499eSDavid van Moolenbroek void
pktsock_set_mcaware(struct pktsock * pkt)904*ef8d499eSDavid van Moolenbroek pktsock_set_mcaware(struct pktsock * pkt)
905*ef8d499eSDavid van Moolenbroek {
906*ef8d499eSDavid van Moolenbroek 
907*ef8d499eSDavid van Moolenbroek 	ipsock_set_flag(&pkt->pkt_ipsock, PKTF_MCAWARE);
908*ef8d499eSDavid van Moolenbroek }
909*ef8d499eSDavid van Moolenbroek 
910*ef8d499eSDavid van Moolenbroek /*
911*ef8d499eSDavid van Moolenbroek  * Set socket options on a packet socket.
912*ef8d499eSDavid van Moolenbroek  */
913*ef8d499eSDavid van Moolenbroek int
pktsock_setsockopt(struct pktsock * pkt,int level,int name,const struct sockdriver_data * data,socklen_t len,struct ipopts * ipopts)914*ef8d499eSDavid van Moolenbroek pktsock_setsockopt(struct pktsock * pkt, int level, int name,
915*ef8d499eSDavid van Moolenbroek 	const struct sockdriver_data * data, socklen_t len,
916*ef8d499eSDavid van Moolenbroek 	struct ipopts * ipopts)
917*ef8d499eSDavid van Moolenbroek {
918*ef8d499eSDavid van Moolenbroek 	struct ip_mreq imr;
919*ef8d499eSDavid van Moolenbroek 	struct ipv6_mreq ipv6mr;
920*ef8d499eSDavid van Moolenbroek 	struct in6_pktinfo ipi6;
921*ef8d499eSDavid van Moolenbroek 	ip_addr_t ipaddr, ifaddr;
922*ef8d499eSDavid van Moolenbroek 	struct ifdev *ifdev;
923*ef8d499eSDavid van Moolenbroek 	unsigned int flag;
924*ef8d499eSDavid van Moolenbroek 	uint32_t ifindex;
925*ef8d499eSDavid van Moolenbroek 	int r, val, has_scope;
926*ef8d499eSDavid van Moolenbroek 
927*ef8d499eSDavid van Moolenbroek 	switch (level) {
928*ef8d499eSDavid van Moolenbroek 	case IPPROTO_IP:
929*ef8d499eSDavid van Moolenbroek 		if (ipsock_is_ipv6(&pkt->pkt_ipsock))
930*ef8d499eSDavid van Moolenbroek 			break;
931*ef8d499eSDavid van Moolenbroek 
932*ef8d499eSDavid van Moolenbroek 		switch (name) {
933*ef8d499eSDavid van Moolenbroek 		case IP_ADD_MEMBERSHIP:
934*ef8d499eSDavid van Moolenbroek 		case IP_DROP_MEMBERSHIP:
935*ef8d499eSDavid van Moolenbroek 			pktsock_set_mcaware(pkt);
936*ef8d499eSDavid van Moolenbroek 
937*ef8d499eSDavid van Moolenbroek 			if ((r = sockdriver_copyin_opt(data, &imr, sizeof(imr),
938*ef8d499eSDavid van Moolenbroek 			    len)) != OK)
939*ef8d499eSDavid van Moolenbroek 				return r;
940*ef8d499eSDavid van Moolenbroek 
941*ef8d499eSDavid van Moolenbroek 			ip_addr_set_ip4_u32(&ipaddr, imr.imr_multiaddr.s_addr);
942*ef8d499eSDavid van Moolenbroek 			ip_addr_set_ip4_u32(&ifaddr, imr.imr_interface.s_addr);
943*ef8d499eSDavid van Moolenbroek 
944*ef8d499eSDavid van Moolenbroek 			if (!ip_addr_isany(&ifaddr)) {
945*ef8d499eSDavid van Moolenbroek 				ifdev = ifaddr_map_by_addr(&ifaddr);
946*ef8d499eSDavid van Moolenbroek 
947*ef8d499eSDavid van Moolenbroek 				if (ifdev == NULL)
948*ef8d499eSDavid van Moolenbroek 					return EADDRNOTAVAIL;
949*ef8d499eSDavid van Moolenbroek 			} else
950*ef8d499eSDavid van Moolenbroek 				ifdev = NULL;
951*ef8d499eSDavid van Moolenbroek 
952*ef8d499eSDavid van Moolenbroek 			if (name == IP_ADD_MEMBERSHIP)
953*ef8d499eSDavid van Moolenbroek 				r = mcast_join(&pkt->pkt_mcast, &ipaddr,
954*ef8d499eSDavid van Moolenbroek 				    ifdev);
955*ef8d499eSDavid van Moolenbroek 			else
956*ef8d499eSDavid van Moolenbroek 				r = mcast_leave(&pkt->pkt_mcast, &ipaddr,
957*ef8d499eSDavid van Moolenbroek 				    ifdev);
958*ef8d499eSDavid van Moolenbroek 
959*ef8d499eSDavid van Moolenbroek 			return r;
960*ef8d499eSDavid van Moolenbroek 
961*ef8d499eSDavid van Moolenbroek 		case IP_RECVTTL:
962*ef8d499eSDavid van Moolenbroek 		case IP_RECVPKTINFO:
963*ef8d499eSDavid van Moolenbroek 			if ((r = sockdriver_copyin_opt(data, &val, sizeof(val),
964*ef8d499eSDavid van Moolenbroek 			    len)) != OK)
965*ef8d499eSDavid van Moolenbroek 				return r;
966*ef8d499eSDavid van Moolenbroek 
967*ef8d499eSDavid van Moolenbroek 			switch (name) {
968*ef8d499eSDavid van Moolenbroek 			case IP_RECVTTL:	flag = PKTF_RECVTTL; break;
969*ef8d499eSDavid van Moolenbroek 			case IP_RECVPKTINFO:	flag = PKTF_RECVINFO; break;
970*ef8d499eSDavid van Moolenbroek 			default:		flag = 0; assert(0); break;
971*ef8d499eSDavid van Moolenbroek 			}
972*ef8d499eSDavid van Moolenbroek 
973*ef8d499eSDavid van Moolenbroek 			if (val)
974*ef8d499eSDavid van Moolenbroek 				ipsock_set_flag(&pkt->pkt_ipsock, flag);
975*ef8d499eSDavid van Moolenbroek 			else
976*ef8d499eSDavid van Moolenbroek 				ipsock_clear_flag(&pkt->pkt_ipsock, flag);
977*ef8d499eSDavid van Moolenbroek 
978*ef8d499eSDavid van Moolenbroek 			return OK;
979*ef8d499eSDavid van Moolenbroek 		}
980*ef8d499eSDavid van Moolenbroek 
981*ef8d499eSDavid van Moolenbroek 		break;
982*ef8d499eSDavid van Moolenbroek 
983*ef8d499eSDavid van Moolenbroek 	case IPPROTO_IPV6:
984*ef8d499eSDavid van Moolenbroek 		if (!ipsock_is_ipv6(&pkt->pkt_ipsock))
985*ef8d499eSDavid van Moolenbroek 			break;
986*ef8d499eSDavid van Moolenbroek 
987*ef8d499eSDavid van Moolenbroek 		switch (name) {
988*ef8d499eSDavid van Moolenbroek 		case IPV6_JOIN_GROUP:
989*ef8d499eSDavid van Moolenbroek 		case IPV6_LEAVE_GROUP:
990*ef8d499eSDavid van Moolenbroek 			pktsock_set_mcaware(pkt);
991*ef8d499eSDavid van Moolenbroek 
992*ef8d499eSDavid van Moolenbroek 			if ((r = sockdriver_copyin_opt(data, &ipv6mr,
993*ef8d499eSDavid van Moolenbroek 			    sizeof(ipv6mr), len)) != OK)
994*ef8d499eSDavid van Moolenbroek 				return r;
995*ef8d499eSDavid van Moolenbroek 
996*ef8d499eSDavid van Moolenbroek 			ip_addr_set_zero_ip6(&ipaddr);
997*ef8d499eSDavid van Moolenbroek 			memcpy(ip_2_ip6(&ipaddr)->addr,
998*ef8d499eSDavid van Moolenbroek 			    &ipv6mr.ipv6mr_multiaddr,
999*ef8d499eSDavid van Moolenbroek 			    sizeof(ip_2_ip6(&ipaddr)->addr));
1000*ef8d499eSDavid van Moolenbroek 
1001*ef8d499eSDavid van Moolenbroek 			/*
1002*ef8d499eSDavid van Moolenbroek 			 * We currently do not support joining IPv4 multicast
1003*ef8d499eSDavid van Moolenbroek 			 * groups on IPv6 sockets.  The reason for this is that
1004*ef8d499eSDavid van Moolenbroek 			 * this would require decisions on what to do if the
1005*ef8d499eSDavid van Moolenbroek 			 * socket is set to V6ONLY later, as well as various
1006*ef8d499eSDavid van Moolenbroek 			 * additional exceptions for a case that hopefully
1007*ef8d499eSDavid van Moolenbroek 			 * doesn't occur in practice anyway.
1008*ef8d499eSDavid van Moolenbroek 			 */
1009*ef8d499eSDavid van Moolenbroek 			if (ip6_addr_isipv4mappedipv6(ip_2_ip6(&ipaddr)))
1010*ef8d499eSDavid van Moolenbroek 				return EADDRNOTAVAIL;
1011*ef8d499eSDavid van Moolenbroek 
1012*ef8d499eSDavid van Moolenbroek 			has_scope = ip6_addr_has_scope(ip_2_ip6(&ipaddr),
1013*ef8d499eSDavid van Moolenbroek 			    IP6_UNKNOWN);
1014*ef8d499eSDavid van Moolenbroek 
1015*ef8d499eSDavid van Moolenbroek 			if ((ifindex = ipv6mr.ipv6mr_interface) != 0) {
1016*ef8d499eSDavid van Moolenbroek 				ifdev = ifdev_get_by_index(ifindex);
1017*ef8d499eSDavid van Moolenbroek 
1018*ef8d499eSDavid van Moolenbroek 				if (ifdev == NULL)
1019*ef8d499eSDavid van Moolenbroek 					return ENXIO;
1020*ef8d499eSDavid van Moolenbroek 
1021*ef8d499eSDavid van Moolenbroek 				if (has_scope)
1022*ef8d499eSDavid van Moolenbroek 					ip6_addr_set_zone(ip_2_ip6(&ipaddr),
1023*ef8d499eSDavid van Moolenbroek 					    ifindex);
1024*ef8d499eSDavid van Moolenbroek 			} else {
1025*ef8d499eSDavid van Moolenbroek 				if (has_scope)
1026*ef8d499eSDavid van Moolenbroek 					return EADDRNOTAVAIL;
1027*ef8d499eSDavid van Moolenbroek 
1028*ef8d499eSDavid van Moolenbroek 				ifdev = NULL;
1029*ef8d499eSDavid van Moolenbroek 			}
1030*ef8d499eSDavid van Moolenbroek 
1031*ef8d499eSDavid van Moolenbroek 			if (name == IPV6_JOIN_GROUP)
1032*ef8d499eSDavid van Moolenbroek 				r = mcast_join(&pkt->pkt_mcast, &ipaddr,
1033*ef8d499eSDavid van Moolenbroek 				    ifdev);
1034*ef8d499eSDavid van Moolenbroek 			else
1035*ef8d499eSDavid van Moolenbroek 				r = mcast_leave(&pkt->pkt_mcast, &ipaddr,
1036*ef8d499eSDavid van Moolenbroek 				    ifdev);
1037*ef8d499eSDavid van Moolenbroek 
1038*ef8d499eSDavid van Moolenbroek 			return r;
1039*ef8d499eSDavid van Moolenbroek 
1040*ef8d499eSDavid van Moolenbroek 		case IPV6_USE_MIN_MTU:
1041*ef8d499eSDavid van Moolenbroek 			if ((r = sockdriver_copyin_opt(data, &val, sizeof(val),
1042*ef8d499eSDavid van Moolenbroek 			    len)) != OK)
1043*ef8d499eSDavid van Moolenbroek 				return r;
1044*ef8d499eSDavid van Moolenbroek 
1045*ef8d499eSDavid van Moolenbroek 			if (val < -1 || val > 1)
1046*ef8d499eSDavid van Moolenbroek 				return EINVAL;
1047*ef8d499eSDavid van Moolenbroek 
1048*ef8d499eSDavid van Moolenbroek 			/*
1049*ef8d499eSDavid van Moolenbroek 			 * lwIP does not support path MTU discovery, so do
1050*ef8d499eSDavid van Moolenbroek 			 * nothing.  TODO: see if this is actually good enough.
1051*ef8d499eSDavid van Moolenbroek 			 */
1052*ef8d499eSDavid van Moolenbroek 			return OK;
1053*ef8d499eSDavid van Moolenbroek 
1054*ef8d499eSDavid van Moolenbroek 		case IPV6_PKTINFO:
1055*ef8d499eSDavid van Moolenbroek 			if ((r = sockdriver_copyin_opt(data, &ipi6,
1056*ef8d499eSDavid van Moolenbroek 			    sizeof(ipi6), len)) != OK)
1057*ef8d499eSDavid van Moolenbroek 				return r;
1058*ef8d499eSDavid van Moolenbroek 
1059*ef8d499eSDavid van Moolenbroek 			/*
1060*ef8d499eSDavid van Moolenbroek 			 * Simply copy in what is given.  The values will be
1061*ef8d499eSDavid van Moolenbroek 			 * parsed only once a packet is sent, in
1062*ef8d499eSDavid van Moolenbroek 			 * pktsock_get_pktinfo().  Otherwise, if we perform
1063*ef8d499eSDavid van Moolenbroek 			 * checks here, they may be outdated by the time the
1064*ef8d499eSDavid van Moolenbroek 			 * values are actually used.
1065*ef8d499eSDavid van Moolenbroek 			 */
1066*ef8d499eSDavid van Moolenbroek 			memcpy(&pkt->pkt_srcaddr.addr, &ipi6.ipi6_addr,
1067*ef8d499eSDavid van Moolenbroek 			    sizeof(pkt->pkt_srcaddr.addr));
1068*ef8d499eSDavid van Moolenbroek 			pkt->pkt_ifindex = ipi6.ipi6_ifindex;
1069*ef8d499eSDavid van Moolenbroek 
1070*ef8d499eSDavid van Moolenbroek 			return OK;
1071*ef8d499eSDavid van Moolenbroek 
1072*ef8d499eSDavid van Moolenbroek 		case IPV6_RECVPKTINFO:
1073*ef8d499eSDavid van Moolenbroek 		case IPV6_RECVHOPLIMIT:
1074*ef8d499eSDavid van Moolenbroek 		case IPV6_RECVTCLASS:
1075*ef8d499eSDavid van Moolenbroek 			if ((r = sockdriver_copyin_opt(data, &val, sizeof(val),
1076*ef8d499eSDavid van Moolenbroek 			    len)) != OK)
1077*ef8d499eSDavid van Moolenbroek 				return r;
1078*ef8d499eSDavid van Moolenbroek 
1079*ef8d499eSDavid van Moolenbroek 			switch (name) {
1080*ef8d499eSDavid van Moolenbroek 			case IPV6_RECVPKTINFO:	flag = PKTF_RECVINFO; break;
1081*ef8d499eSDavid van Moolenbroek 			case IPV6_RECVHOPLIMIT:	flag = PKTF_RECVTTL; break;
1082*ef8d499eSDavid van Moolenbroek 			case IPV6_RECVTCLASS:	flag = PKTF_RECVTOS; break;
1083*ef8d499eSDavid van Moolenbroek 			default:		flag = 0; assert(0); break;
1084*ef8d499eSDavid van Moolenbroek 			}
1085*ef8d499eSDavid van Moolenbroek 
1086*ef8d499eSDavid van Moolenbroek 			if (val)
1087*ef8d499eSDavid van Moolenbroek 				ipsock_set_flag(&pkt->pkt_ipsock, flag);
1088*ef8d499eSDavid van Moolenbroek 			else
1089*ef8d499eSDavid van Moolenbroek 				ipsock_clear_flag(&pkt->pkt_ipsock, flag);
1090*ef8d499eSDavid van Moolenbroek 
1091*ef8d499eSDavid van Moolenbroek 			return OK;
1092*ef8d499eSDavid van Moolenbroek 		}
1093*ef8d499eSDavid van Moolenbroek 
1094*ef8d499eSDavid van Moolenbroek 		break;
1095*ef8d499eSDavid van Moolenbroek 	}
1096*ef8d499eSDavid van Moolenbroek 
1097*ef8d499eSDavid van Moolenbroek 	return ipsock_setsockopt(&pkt->pkt_ipsock, level, name, data, len,
1098*ef8d499eSDavid van Moolenbroek 	    ipopts);
1099*ef8d499eSDavid van Moolenbroek }
1100*ef8d499eSDavid van Moolenbroek 
1101*ef8d499eSDavid van Moolenbroek /*
1102*ef8d499eSDavid van Moolenbroek  * Retrieve socket options on a packet socket.
1103*ef8d499eSDavid van Moolenbroek  */
1104*ef8d499eSDavid van Moolenbroek int
pktsock_getsockopt(struct pktsock * pkt,int level,int name,const struct sockdriver_data * data,socklen_t * len,struct ipopts * ipopts)1105*ef8d499eSDavid van Moolenbroek pktsock_getsockopt(struct pktsock * pkt, int level, int name,
1106*ef8d499eSDavid van Moolenbroek 	const struct sockdriver_data * data, socklen_t * len,
1107*ef8d499eSDavid van Moolenbroek 	struct ipopts * ipopts)
1108*ef8d499eSDavid van Moolenbroek {
1109*ef8d499eSDavid van Moolenbroek 	struct in6_pktinfo ipi6;
1110*ef8d499eSDavid van Moolenbroek 	unsigned int flag;
1111*ef8d499eSDavid van Moolenbroek 	int val;
1112*ef8d499eSDavid van Moolenbroek 
1113*ef8d499eSDavid van Moolenbroek 	switch (level) {
1114*ef8d499eSDavid van Moolenbroek 	case IPPROTO_IP:
1115*ef8d499eSDavid van Moolenbroek 		if (ipsock_is_ipv6(&pkt->pkt_ipsock))
1116*ef8d499eSDavid van Moolenbroek 			break;
1117*ef8d499eSDavid van Moolenbroek 
1118*ef8d499eSDavid van Moolenbroek 		switch (name) {
1119*ef8d499eSDavid van Moolenbroek 		case IP_RECVTTL:
1120*ef8d499eSDavid van Moolenbroek 		case IP_RECVPKTINFO:
1121*ef8d499eSDavid van Moolenbroek 			switch (name) {
1122*ef8d499eSDavid van Moolenbroek 			case IP_RECVTTL:	flag = PKTF_RECVTTL; break;
1123*ef8d499eSDavid van Moolenbroek 			case IP_RECVPKTINFO:	flag = PKTF_RECVINFO; break;
1124*ef8d499eSDavid van Moolenbroek 			default:		flag = 0; assert(0); break;
1125*ef8d499eSDavid van Moolenbroek 			}
1126*ef8d499eSDavid van Moolenbroek 
1127*ef8d499eSDavid van Moolenbroek 			val = !!(ipsock_get_flag(&pkt->pkt_ipsock, flag));
1128*ef8d499eSDavid van Moolenbroek 
1129*ef8d499eSDavid van Moolenbroek 			return sockdriver_copyout_opt(data, &val, sizeof(val),
1130*ef8d499eSDavid van Moolenbroek 			    len);
1131*ef8d499eSDavid van Moolenbroek 		}
1132*ef8d499eSDavid van Moolenbroek 
1133*ef8d499eSDavid van Moolenbroek 		break;
1134*ef8d499eSDavid van Moolenbroek 
1135*ef8d499eSDavid van Moolenbroek 	case IPPROTO_IPV6:
1136*ef8d499eSDavid van Moolenbroek 		if (!ipsock_is_ipv6(&pkt->pkt_ipsock))
1137*ef8d499eSDavid van Moolenbroek 			break;
1138*ef8d499eSDavid van Moolenbroek 
1139*ef8d499eSDavid van Moolenbroek 		switch (name) {
1140*ef8d499eSDavid van Moolenbroek 		case IPV6_USE_MIN_MTU:
1141*ef8d499eSDavid van Moolenbroek 			/*
1142*ef8d499eSDavid van Moolenbroek 			 * TODO: sort out exactly what lwIP actually supports
1143*ef8d499eSDavid van Moolenbroek 			 * in the way of path MTU discovery.  Value 1 means
1144*ef8d499eSDavid van Moolenbroek 			 * that path MTU discovery is disabled and packets are
1145*ef8d499eSDavid van Moolenbroek 			 * sent at the minimum MTU (RFC 3542).
1146*ef8d499eSDavid van Moolenbroek 			 */
1147*ef8d499eSDavid van Moolenbroek 			val = 1;
1148*ef8d499eSDavid van Moolenbroek 
1149*ef8d499eSDavid van Moolenbroek 			return sockdriver_copyout_opt(data, &val, sizeof(val),
1150*ef8d499eSDavid van Moolenbroek 			    len);
1151*ef8d499eSDavid van Moolenbroek 
1152*ef8d499eSDavid van Moolenbroek 		case IPV6_PKTINFO:
1153*ef8d499eSDavid van Moolenbroek 			memset(&ipi6, 0, sizeof(ipi6));
1154*ef8d499eSDavid van Moolenbroek 
1155*ef8d499eSDavid van Moolenbroek 			/*
1156*ef8d499eSDavid van Moolenbroek 			 * Simply copy out whatever was given before.  These
1157*ef8d499eSDavid van Moolenbroek 			 * fields are initialized to zero on socket creation.
1158*ef8d499eSDavid van Moolenbroek 			 */
1159*ef8d499eSDavid van Moolenbroek 			memcpy(&ipi6.ipi6_addr, &pkt->pkt_srcaddr.addr,
1160*ef8d499eSDavid van Moolenbroek 			    sizeof(ipi6.ipi6_addr));
1161*ef8d499eSDavid van Moolenbroek 			ipi6.ipi6_ifindex = pkt->pkt_ifindex;
1162*ef8d499eSDavid van Moolenbroek 
1163*ef8d499eSDavid van Moolenbroek 			return sockdriver_copyout_opt(data, &ipi6,
1164*ef8d499eSDavid van Moolenbroek 			    sizeof(ipi6), len);
1165*ef8d499eSDavid van Moolenbroek 
1166*ef8d499eSDavid van Moolenbroek 		case IPV6_RECVPKTINFO:
1167*ef8d499eSDavid van Moolenbroek 		case IPV6_RECVHOPLIMIT:
1168*ef8d499eSDavid van Moolenbroek 		case IPV6_RECVTCLASS:
1169*ef8d499eSDavid van Moolenbroek 			switch (name) {
1170*ef8d499eSDavid van Moolenbroek 			case IPV6_RECVPKTINFO:	flag = PKTF_RECVINFO; break;
1171*ef8d499eSDavid van Moolenbroek 			case IPV6_RECVHOPLIMIT:	flag = PKTF_RECVTTL; break;
1172*ef8d499eSDavid van Moolenbroek 			case IPV6_RECVTCLASS:	flag = PKTF_RECVTOS; break;
1173*ef8d499eSDavid van Moolenbroek 			default:		flag = 0; assert(0); break;
1174*ef8d499eSDavid van Moolenbroek 			}
1175*ef8d499eSDavid van Moolenbroek 
1176*ef8d499eSDavid van Moolenbroek 			val = !!(ipsock_get_flag(&pkt->pkt_ipsock, flag));
1177*ef8d499eSDavid van Moolenbroek 
1178*ef8d499eSDavid van Moolenbroek 			return sockdriver_copyout_opt(data, &val, sizeof(val),
1179*ef8d499eSDavid van Moolenbroek 			    len);
1180*ef8d499eSDavid van Moolenbroek 		}
1181*ef8d499eSDavid van Moolenbroek 
1182*ef8d499eSDavid van Moolenbroek 		break;
1183*ef8d499eSDavid van Moolenbroek 	}
1184*ef8d499eSDavid van Moolenbroek 
1185*ef8d499eSDavid van Moolenbroek 	return ipsock_getsockopt(&pkt->pkt_ipsock, level, name, data, len,
1186*ef8d499eSDavid van Moolenbroek 	    ipopts);
1187*ef8d499eSDavid van Moolenbroek }
1188*ef8d499eSDavid van Moolenbroek 
1189*ef8d499eSDavid van Moolenbroek /*
1190*ef8d499eSDavid van Moolenbroek  * Drain the receive queue of a packet socket.
1191*ef8d499eSDavid van Moolenbroek  */
1192*ef8d499eSDavid van Moolenbroek static void
pktsock_drain(struct pktsock * pkt)1193*ef8d499eSDavid van Moolenbroek pktsock_drain(struct pktsock * pkt)
1194*ef8d499eSDavid van Moolenbroek {
1195*ef8d499eSDavid van Moolenbroek 
1196*ef8d499eSDavid van Moolenbroek 	while (pkt->pkt_rcvhead != NULL)
1197*ef8d499eSDavid van Moolenbroek 		pktsock_dequeue(pkt);
1198*ef8d499eSDavid van Moolenbroek 
1199*ef8d499eSDavid van Moolenbroek 	assert(pkt->pkt_rcvlen == 0);
1200*ef8d499eSDavid van Moolenbroek 	assert(pkt->pkt_rcvtailp == &pkt->pkt_rcvhead);
1201*ef8d499eSDavid van Moolenbroek }
1202*ef8d499eSDavid van Moolenbroek 
1203*ef8d499eSDavid van Moolenbroek /*
1204*ef8d499eSDavid van Moolenbroek  * Shut down a packet socket for reading and/or writing.
1205*ef8d499eSDavid van Moolenbroek  */
1206*ef8d499eSDavid van Moolenbroek void
pktsock_shutdown(struct pktsock * pkt,unsigned int mask)1207*ef8d499eSDavid van Moolenbroek pktsock_shutdown(struct pktsock * pkt, unsigned int mask)
1208*ef8d499eSDavid van Moolenbroek {
1209*ef8d499eSDavid van Moolenbroek 
1210*ef8d499eSDavid van Moolenbroek 	if (mask & SFL_SHUT_RD)
1211*ef8d499eSDavid van Moolenbroek 		pktsock_drain(pkt);
1212*ef8d499eSDavid van Moolenbroek }
1213*ef8d499eSDavid van Moolenbroek 
1214*ef8d499eSDavid van Moolenbroek /*
1215*ef8d499eSDavid van Moolenbroek  * Close a packet socket.
1216*ef8d499eSDavid van Moolenbroek  */
1217*ef8d499eSDavid van Moolenbroek void
pktsock_close(struct pktsock * pkt)1218*ef8d499eSDavid van Moolenbroek pktsock_close(struct pktsock * pkt)
1219*ef8d499eSDavid van Moolenbroek {
1220*ef8d499eSDavid van Moolenbroek 
1221*ef8d499eSDavid van Moolenbroek 	pktsock_drain(pkt);
1222*ef8d499eSDavid van Moolenbroek 
1223*ef8d499eSDavid van Moolenbroek 	mcast_leave_all(&pkt->pkt_mcast);
1224*ef8d499eSDavid van Moolenbroek }
1225*ef8d499eSDavid van Moolenbroek 
1226*ef8d499eSDavid van Moolenbroek /*
1227*ef8d499eSDavid van Moolenbroek  * Return the rounded-up number of bytes in the packet socket's receive queue,
1228*ef8d499eSDavid van Moolenbroek  * for sysctl(7).  NetBSD returns the used portion of each buffer, but that
1229*ef8d499eSDavid van Moolenbroek  * would be quite some extra effort for us (TODO).
1230*ef8d499eSDavid van Moolenbroek  */
1231*ef8d499eSDavid van Moolenbroek size_t
pktsock_get_recvlen(struct pktsock * pkt)1232*ef8d499eSDavid van Moolenbroek pktsock_get_recvlen(struct pktsock * pkt)
1233*ef8d499eSDavid van Moolenbroek {
1234*ef8d499eSDavid van Moolenbroek 
1235*ef8d499eSDavid van Moolenbroek 	return pkt->pkt_rcvlen;
1236*ef8d499eSDavid van Moolenbroek }
1237