xref: /minix3/minix/net/lwip/rawsock.c (revision ef8d499e2d2af900e9b2ab297171d7b088652482)
1*ef8d499eSDavid van Moolenbroek /* LWIP service - rawsock.c - RAW sockets */
2*ef8d499eSDavid van Moolenbroek /*
3*ef8d499eSDavid van Moolenbroek  * For IPv6 sockets, this module attempts to implement a part of RFC 3542, but
4*ef8d499eSDavid van Moolenbroek  * currently not more than what is supported by lwIP and/or what is expected by
5*ef8d499eSDavid van Moolenbroek  * a handful of standard utilities (dhcpcd, ping6, traceroute6..).
6*ef8d499eSDavid van Moolenbroek  *
7*ef8d499eSDavid van Moolenbroek  * For general understanding, be aware that IPv4 raw sockets always receive
8*ef8d499eSDavid van Moolenbroek  * packets including the IP header, and may be used to send packets including
9*ef8d499eSDavid van Moolenbroek  * the IP header if IP_HDRINCL is set, while IPv6 raw sockets always send and
10*ef8d499eSDavid van Moolenbroek  * receive actual payloads only, using ancillary (control) data to set and
11*ef8d499eSDavid van Moolenbroek  * retrieve per-packet IP header fields.
12*ef8d499eSDavid van Moolenbroek  *
13*ef8d499eSDavid van Moolenbroek  * For packet headers we follow general BSD semantics.  For example, some IPv4
14*ef8d499eSDavid van Moolenbroek  * header fields are swapped both when sending and when receiving.  Also, like
15*ef8d499eSDavid van Moolenbroek  * on NetBSD, IPPROTO_RAW is not a special value in any way.
16*ef8d499eSDavid van Moolenbroek  */
17*ef8d499eSDavid van Moolenbroek 
18*ef8d499eSDavid van Moolenbroek #include "lwip.h"
19*ef8d499eSDavid van Moolenbroek #include "ifaddr.h"
20*ef8d499eSDavid van Moolenbroek #include "pktsock.h"
21*ef8d499eSDavid van Moolenbroek 
22*ef8d499eSDavid van Moolenbroek #include "lwip/raw.h"
23*ef8d499eSDavid van Moolenbroek #include "lwip/inet_chksum.h"
24*ef8d499eSDavid van Moolenbroek 
25*ef8d499eSDavid van Moolenbroek #include <net/route.h>
26*ef8d499eSDavid van Moolenbroek #include <netinet/icmp6.h>
27*ef8d499eSDavid van Moolenbroek #include <netinet/ip.h>
28*ef8d499eSDavid van Moolenbroek #include <netinet/in_pcb.h>
29*ef8d499eSDavid van Moolenbroek 
30*ef8d499eSDavid van Moolenbroek /* The number of RAW sockets.  Inherited from the lwIP configuration. */
31*ef8d499eSDavid van Moolenbroek #define NR_RAWSOCK	MEMP_NUM_RAW_PCB
32*ef8d499eSDavid van Moolenbroek 
33*ef8d499eSDavid van Moolenbroek /*
34*ef8d499eSDavid van Moolenbroek  * Outgoing packets are not getting buffered, so the send buffer size simply
35*ef8d499eSDavid van Moolenbroek  * determines the maximum size for sent packets.  The send buffer maximum is
36*ef8d499eSDavid van Moolenbroek  * therefore limited to the maximum size of a single packet (64K-1 bytes),
37*ef8d499eSDavid van Moolenbroek  * which is already enforced by lwIP's 16-bit length parameter to pbuf_alloc().
38*ef8d499eSDavid van Moolenbroek  *
39*ef8d499eSDavid van Moolenbroek  * The actual transmission may enforce a lower limit, though.  The full packet
40*ef8d499eSDavid van Moolenbroek  * size must not exceed the same 64K-1 limit, and that includes any headers
41*ef8d499eSDavid van Moolenbroek  * that still have to be prepended to the given packet.  The size of those
42*ef8d499eSDavid van Moolenbroek  * headers depends on the socket type (IPv4/IPv6) and the IP_HDRINCL setting.
43*ef8d499eSDavid van Moolenbroek  *
44*ef8d499eSDavid van Moolenbroek  * The default is equal to the maximum here, because if a (by definition,
45*ef8d499eSDavid van Moolenbroek  * privileged) application wishes to send large raw packets, it probably has a
46*ef8d499eSDavid van Moolenbroek  * good reason, and we do not want to get in its way.
47*ef8d499eSDavid van Moolenbroek  */
48*ef8d499eSDavid van Moolenbroek #define RAW_MAX_PAYLOAD	(UINT16_MAX)
49*ef8d499eSDavid van Moolenbroek 
50*ef8d499eSDavid van Moolenbroek #define RAW_SNDBUF_MIN	1		/* minimum RAW send buffer size */
51*ef8d499eSDavid van Moolenbroek #define RAW_SNDBUF_DEF	RAW_MAX_PAYLOAD	/* default RAW send buffer size */
52*ef8d499eSDavid van Moolenbroek #define RAW_SNDBUF_MAX	RAW_MAX_PAYLOAD	/* maximum RAW send buffer size */
53*ef8d499eSDavid van Moolenbroek #define RAW_RCVBUF_MIN	MEMPOOL_BUFSIZE	/* minimum RAW receive buffer size */
54*ef8d499eSDavid van Moolenbroek #define RAW_RCVBUF_DEF	32768		/* default RAW receive buffer size */
55*ef8d499eSDavid van Moolenbroek #define RAW_RCVBUF_MAX	65536		/* maximum RAW receive buffer size */
56*ef8d499eSDavid van Moolenbroek 
57*ef8d499eSDavid van Moolenbroek static struct rawsock {
58*ef8d499eSDavid van Moolenbroek 	struct pktsock raw_pktsock;		/* packet socket object */
59*ef8d499eSDavid van Moolenbroek 	struct raw_pcb *raw_pcb;		/* lwIP RAW control block */
60*ef8d499eSDavid van Moolenbroek 	TAILQ_ENTRY(rawsock) raw_next;		/* next in active/free list */
61*ef8d499eSDavid van Moolenbroek 	struct icmp6_filter raw_icmp6filter;	/* ICMPv6 type filter */
62*ef8d499eSDavid van Moolenbroek } raw_array[NR_RAWSOCK];
63*ef8d499eSDavid van Moolenbroek 
64*ef8d499eSDavid van Moolenbroek static TAILQ_HEAD(, rawsock) raw_freelist;	/* list of free RAW sockets */
65*ef8d499eSDavid van Moolenbroek static TAILQ_HEAD(, rawsock) raw_activelist;	/* list, in-use RAW sockets */
66*ef8d499eSDavid van Moolenbroek 
67*ef8d499eSDavid van Moolenbroek static const struct sockevent_ops rawsock_ops;
68*ef8d499eSDavid van Moolenbroek 
69*ef8d499eSDavid van Moolenbroek #define rawsock_get_sock(raw)	(ipsock_get_sock(rawsock_get_ipsock(raw)))
70*ef8d499eSDavid van Moolenbroek #define rawsock_get_ipsock(raw)	(pktsock_get_ipsock(&(raw)->raw_pktsock))
71*ef8d499eSDavid van Moolenbroek #define rawsock_is_ipv6(raw)	(ipsock_is_ipv6(rawsock_get_ipsock(raw)))
72*ef8d499eSDavid van Moolenbroek #define rawsock_is_v6only(raw)	(ipsock_is_v6only(rawsock_get_ipsock(raw)))
73*ef8d499eSDavid van Moolenbroek #define rawsock_is_conn(raw)	\
74*ef8d499eSDavid van Moolenbroek 	(raw_flags((raw)->raw_pcb) & RAW_FLAGS_CONNECTED)
75*ef8d499eSDavid van Moolenbroek #define rawsock_is_hdrincl(raw)	\
76*ef8d499eSDavid van Moolenbroek 	(raw_flags((raw)->raw_pcb) & RAW_FLAGS_HDRINCL)
77*ef8d499eSDavid van Moolenbroek 
78*ef8d499eSDavid van Moolenbroek static ssize_t rawsock_pcblist(struct rmib_call *, struct rmib_node *,
79*ef8d499eSDavid van Moolenbroek 	struct rmib_oldp *, struct rmib_newp *);
80*ef8d499eSDavid van Moolenbroek 
81*ef8d499eSDavid van Moolenbroek /* The CTL_NET {PF_INET,PF_INET6} IPPROTO_RAW subtree. */
82*ef8d499eSDavid van Moolenbroek /* All dynamically numbered; the sendspace/recvspace entries are ours. */
83*ef8d499eSDavid van Moolenbroek static struct rmib_node net_inet_raw_table[] = {
84*ef8d499eSDavid van Moolenbroek 	RMIB_INT(RMIB_RO, RAW_SNDBUF_DEF, "sendspace",
85*ef8d499eSDavid van Moolenbroek 	    "Default RAW send buffer size"),
86*ef8d499eSDavid van Moolenbroek 	RMIB_INT(RMIB_RO, RAW_RCVBUF_DEF, "recvspace",
87*ef8d499eSDavid van Moolenbroek 	    "Default RAW receive buffer size"),
88*ef8d499eSDavid van Moolenbroek 	RMIB_FUNC(RMIB_RO | CTLTYPE_NODE, 0, rawsock_pcblist, "pcblist",
89*ef8d499eSDavid van Moolenbroek 	    "RAW IP protocol control block list"),
90*ef8d499eSDavid van Moolenbroek };
91*ef8d499eSDavid van Moolenbroek 
92*ef8d499eSDavid van Moolenbroek static struct rmib_node net_inet_raw_node =
93*ef8d499eSDavid van Moolenbroek     RMIB_NODE(RMIB_RO, net_inet_raw_table, "raw", "RAW IPv4 settings");
94*ef8d499eSDavid van Moolenbroek static struct rmib_node net_inet6_raw6_node =
95*ef8d499eSDavid van Moolenbroek     RMIB_NODE(RMIB_RO, net_inet_raw_table, "raw6", "RAW IPv6 settings");
96*ef8d499eSDavid van Moolenbroek 
97*ef8d499eSDavid van Moolenbroek /*
98*ef8d499eSDavid van Moolenbroek  * Initialize the raw sockets module.
99*ef8d499eSDavid van Moolenbroek  */
100*ef8d499eSDavid van Moolenbroek void
rawsock_init(void)101*ef8d499eSDavid van Moolenbroek rawsock_init(void)
102*ef8d499eSDavid van Moolenbroek {
103*ef8d499eSDavid van Moolenbroek 	unsigned int slot;
104*ef8d499eSDavid van Moolenbroek 
105*ef8d499eSDavid van Moolenbroek 	/* Initialize the list of free RAW sockets. */
106*ef8d499eSDavid van Moolenbroek 	TAILQ_INIT(&raw_freelist);
107*ef8d499eSDavid van Moolenbroek 
108*ef8d499eSDavid van Moolenbroek 	for (slot = 0; slot < __arraycount(raw_array); slot++)
109*ef8d499eSDavid van Moolenbroek 		TAILQ_INSERT_TAIL(&raw_freelist, &raw_array[slot], raw_next);
110*ef8d499eSDavid van Moolenbroek 
111*ef8d499eSDavid van Moolenbroek 	/* Initialize the list of active RAW sockets. */
112*ef8d499eSDavid van Moolenbroek 	TAILQ_INIT(&raw_activelist);
113*ef8d499eSDavid van Moolenbroek 
114*ef8d499eSDavid van Moolenbroek 	/* Register the net.inet.raw and net.inet6.raw6 RMIB subtrees. */
115*ef8d499eSDavid van Moolenbroek 	mibtree_register_inet(PF_INET, IPPROTO_RAW, &net_inet_raw_node);
116*ef8d499eSDavid van Moolenbroek 	mibtree_register_inet(PF_INET6, IPPROTO_RAW, &net_inet6_raw6_node);
117*ef8d499eSDavid van Moolenbroek }
118*ef8d499eSDavid van Moolenbroek 
119*ef8d499eSDavid van Moolenbroek /*
120*ef8d499eSDavid van Moolenbroek  * Check whether the given arrived IPv6 packet is fit to be received on the
121*ef8d499eSDavid van Moolenbroek  * given raw socket.
122*ef8d499eSDavid van Moolenbroek  */
123*ef8d499eSDavid van Moolenbroek static int
rawsock_check_v6(struct rawsock * raw,struct pbuf * pbuf)124*ef8d499eSDavid van Moolenbroek rawsock_check_v6(struct rawsock * raw, struct pbuf * pbuf)
125*ef8d499eSDavid van Moolenbroek {
126*ef8d499eSDavid van Moolenbroek 	uint8_t type;
127*ef8d499eSDavid van Moolenbroek 
128*ef8d499eSDavid van Moolenbroek 	assert(rawsock_is_ipv6(raw));
129*ef8d499eSDavid van Moolenbroek 
130*ef8d499eSDavid van Moolenbroek 	/*
131*ef8d499eSDavid van Moolenbroek 	 * For ICMPv6 packets, test against the configured type filter.
132*ef8d499eSDavid van Moolenbroek 	 */
133*ef8d499eSDavid van Moolenbroek 	if (raw->raw_pcb->protocol == IPPROTO_ICMPV6) {
134*ef8d499eSDavid van Moolenbroek 		if (pbuf->len < offsetof(struct icmp6_hdr, icmp6_dataun))
135*ef8d499eSDavid van Moolenbroek 			return FALSE;
136*ef8d499eSDavid van Moolenbroek 
137*ef8d499eSDavid van Moolenbroek 		memcpy(&type, &((struct icmp6_hdr *)pbuf->payload)->icmp6_type,
138*ef8d499eSDavid van Moolenbroek 		    sizeof(type));
139*ef8d499eSDavid van Moolenbroek 
140*ef8d499eSDavid van Moolenbroek 		if (!ICMP6_FILTER_WILLPASS((int)type, &raw->raw_icmp6filter))
141*ef8d499eSDavid van Moolenbroek 			return FALSE;
142*ef8d499eSDavid van Moolenbroek 	}
143*ef8d499eSDavid van Moolenbroek 
144*ef8d499eSDavid van Moolenbroek 	/*
145*ef8d499eSDavid van Moolenbroek 	 * For ICMPv6 packets, or if IPV6_CHECKSUM is enabled, we have to
146*ef8d499eSDavid van Moolenbroek 	 * verify the checksum of the packet before passing it to the user.
147*ef8d499eSDavid van Moolenbroek 	 * This is costly, but it needs to be done and lwIP is not doing it for
148*ef8d499eSDavid van Moolenbroek 	 * us (as of writing, anyway), even though it maintains the offset..
149*ef8d499eSDavid van Moolenbroek 	 */
150*ef8d499eSDavid van Moolenbroek 	if (raw->raw_pcb->chksum_reqd &&
151*ef8d499eSDavid van Moolenbroek 	    (pbuf->tot_len < raw->raw_pcb->chksum_offset + sizeof(uint16_t) ||
152*ef8d499eSDavid van Moolenbroek 	    ip6_chksum_pseudo(pbuf, raw->raw_pcb->protocol, pbuf->tot_len,
153*ef8d499eSDavid van Moolenbroek 	    ip6_current_src_addr(), ip6_current_dest_addr()) != 0)) {
154*ef8d499eSDavid van Moolenbroek 		return FALSE;
155*ef8d499eSDavid van Moolenbroek 	}
156*ef8d499eSDavid van Moolenbroek 
157*ef8d499eSDavid van Moolenbroek 	/* No reason to filter out this packet. */
158*ef8d499eSDavid van Moolenbroek 	return TRUE;
159*ef8d499eSDavid van Moolenbroek }
160*ef8d499eSDavid van Moolenbroek 
161*ef8d499eSDavid van Moolenbroek /*
162*ef8d499eSDavid van Moolenbroek  * Adjust the given arrived IPv4 packet by changing the length and offset
163*ef8d499eSDavid van Moolenbroek  * fields to host-byte order, as is done by the BSDs.  This effectively mirrors
164*ef8d499eSDavid van Moolenbroek  * the swapping part of the preparation done on IPv4 packets being sent if the
165*ef8d499eSDavid van Moolenbroek  * IP_HDRINCL socket option is enabled.
166*ef8d499eSDavid van Moolenbroek  */
167*ef8d499eSDavid van Moolenbroek static void
rawsock_adjust_v4(struct pbuf * pbuf)168*ef8d499eSDavid van Moolenbroek rawsock_adjust_v4(struct pbuf * pbuf)
169*ef8d499eSDavid van Moolenbroek {
170*ef8d499eSDavid van Moolenbroek 	struct ip_hdr *iphdr;
171*ef8d499eSDavid van Moolenbroek 
172*ef8d499eSDavid van Moolenbroek 	if (pbuf->len < sizeof(struct ip_hdr))
173*ef8d499eSDavid van Moolenbroek 		return;
174*ef8d499eSDavid van Moolenbroek 
175*ef8d499eSDavid van Moolenbroek 	iphdr = (struct ip_hdr *)pbuf->payload;
176*ef8d499eSDavid van Moolenbroek 
177*ef8d499eSDavid van Moolenbroek 	/*
178*ef8d499eSDavid van Moolenbroek 	 * W. Richard Stevens also mentions ip_id, but at least on NetBSD that
179*ef8d499eSDavid van Moolenbroek 	 * field seems to be swapped neither when sending nor when receiving..
180*ef8d499eSDavid van Moolenbroek 	 */
181*ef8d499eSDavid van Moolenbroek 	IPH_LEN(iphdr) = htons(IPH_LEN(iphdr));
182*ef8d499eSDavid van Moolenbroek 	IPH_OFFSET(iphdr) = htons(IPH_OFFSET(iphdr));
183*ef8d499eSDavid van Moolenbroek }
184*ef8d499eSDavid van Moolenbroek 
185*ef8d499eSDavid van Moolenbroek /*
186*ef8d499eSDavid van Moolenbroek  * A packet has arrived on a raw socket.  Since the same packet may have to be
187*ef8d499eSDavid van Moolenbroek  * delivered to multiple raw sockets, we always return 0 (= not consumed) from
188*ef8d499eSDavid van Moolenbroek  * this function.  As such, we must make a copy of the given packet if we want
189*ef8d499eSDavid van Moolenbroek  * to keep it, and never free it.
190*ef8d499eSDavid van Moolenbroek  */
191*ef8d499eSDavid van Moolenbroek static uint8_t
rawsock_input(void * arg,struct raw_pcb * pcb __unused,struct pbuf * psrc,const ip_addr_t * srcaddr)192*ef8d499eSDavid van Moolenbroek rawsock_input(void * arg, struct raw_pcb * pcb __unused, struct pbuf * psrc,
193*ef8d499eSDavid van Moolenbroek 	const ip_addr_t * srcaddr)
194*ef8d499eSDavid van Moolenbroek {
195*ef8d499eSDavid van Moolenbroek 	struct rawsock *raw = (struct rawsock *)arg;
196*ef8d499eSDavid van Moolenbroek 	struct pbuf *pbuf;
197*ef8d499eSDavid van Moolenbroek 	int off, hdrlen;
198*ef8d499eSDavid van Moolenbroek 
199*ef8d499eSDavid van Moolenbroek 	assert(raw->raw_pcb == pcb);
200*ef8d499eSDavid van Moolenbroek 
201*ef8d499eSDavid van Moolenbroek 	/*
202*ef8d499eSDavid van Moolenbroek 	 * If adding this packet would cause the receive buffer to go beyond
203*ef8d499eSDavid van Moolenbroek 	 * the current limit, drop the new packet.  This is just an estimation,
204*ef8d499eSDavid van Moolenbroek 	 * because the copy we are about to make may not take the exact same
205*ef8d499eSDavid van Moolenbroek 	 * amount of memory, due to the fact that 1) the pbuf we're given has
206*ef8d499eSDavid van Moolenbroek 	 * an unknown set of headers in front of it, and 2) we need to store
207*ef8d499eSDavid van Moolenbroek 	 * extra information in our copy.  The return value of this call, if
208*ef8d499eSDavid van Moolenbroek 	 * not -1, is the number of bytes we need to reserve to store that
209*ef8d499eSDavid van Moolenbroek 	 * extra information.
210*ef8d499eSDavid van Moolenbroek 	 */
211*ef8d499eSDavid van Moolenbroek 	if ((hdrlen = pktsock_test_input(&raw->raw_pktsock, psrc)) < 0)
212*ef8d499eSDavid van Moolenbroek 		return 0;
213*ef8d499eSDavid van Moolenbroek 
214*ef8d499eSDavid van Moolenbroek 	/*
215*ef8d499eSDavid van Moolenbroek 	 * Raw IPv6 sockets receive only the actual packet data, whereas raw
216*ef8d499eSDavid van Moolenbroek 	 * IPv4 sockets receive the IP header as well.
217*ef8d499eSDavid van Moolenbroek 	 */
218*ef8d499eSDavid van Moolenbroek 	if (ip_current_is_v6()) {
219*ef8d499eSDavid van Moolenbroek 		off = ip_current_header_tot_len();
220*ef8d499eSDavid van Moolenbroek 
221*ef8d499eSDavid van Moolenbroek 		util_pbuf_header(psrc, -off);
222*ef8d499eSDavid van Moolenbroek 
223*ef8d499eSDavid van Moolenbroek 		if (!rawsock_check_v6(raw, psrc)) {
224*ef8d499eSDavid van Moolenbroek 			util_pbuf_header(psrc, off);
225*ef8d499eSDavid van Moolenbroek 
226*ef8d499eSDavid van Moolenbroek 			return 0;
227*ef8d499eSDavid van Moolenbroek 		}
228*ef8d499eSDavid van Moolenbroek 	} else {
229*ef8d499eSDavid van Moolenbroek 		/*
230*ef8d499eSDavid van Moolenbroek 		 * For IPv6 sockets, drop the packet if it was sent as an IPv4
231*ef8d499eSDavid van Moolenbroek 		 * packet and checksumming is enabled (this includes ICMPv6).
232*ef8d499eSDavid van Moolenbroek 		 * Otherwise, the packet would bypass the above checks that we
233*ef8d499eSDavid van Moolenbroek 		 * perform on IPv6 packets.  Applications that want to use a
234*ef8d499eSDavid van Moolenbroek 		 * dual-stack protocol with checksumming will have to do the
235*ef8d499eSDavid van Moolenbroek 		 * checksum verification part themselves.  Presumably the two
236*ef8d499eSDavid van Moolenbroek 		 * different pseudoheaders would result in different checksums
237*ef8d499eSDavid van Moolenbroek 		 * anyhow, so it would be useless to try to support that.
238*ef8d499eSDavid van Moolenbroek 		 *
239*ef8d499eSDavid van Moolenbroek 		 * Beyond that, for IPv4 packets on IPv6 sockets, hide the IPv4
240*ef8d499eSDavid van Moolenbroek 		 * header.
241*ef8d499eSDavid van Moolenbroek 		 */
242*ef8d499eSDavid van Moolenbroek 		if (rawsock_is_ipv6(raw)) {
243*ef8d499eSDavid van Moolenbroek 			if (raw->raw_pcb->chksum_reqd)
244*ef8d499eSDavid van Moolenbroek 				return 0;
245*ef8d499eSDavid van Moolenbroek 
246*ef8d499eSDavid van Moolenbroek 			off = IP_HLEN;
247*ef8d499eSDavid van Moolenbroek 
248*ef8d499eSDavid van Moolenbroek 			util_pbuf_header(psrc, -off);
249*ef8d499eSDavid van Moolenbroek 		} else
250*ef8d499eSDavid van Moolenbroek 			off = 0;
251*ef8d499eSDavid van Moolenbroek 	}
252*ef8d499eSDavid van Moolenbroek 
253*ef8d499eSDavid van Moolenbroek 	/*
254*ef8d499eSDavid van Moolenbroek 	 * We need to make a copy of the incoming packet.  If we eat the one
255*ef8d499eSDavid van Moolenbroek 	 * given to us, this will 1) stop any other raw sockets from getting
256*ef8d499eSDavid van Moolenbroek 	 * the same packet, 2) allow a single raw socket to discard all TCP/UDP
257*ef8d499eSDavid van Moolenbroek 	 * traffic, and 3) present us with a problem on how to store ancillary
258*ef8d499eSDavid van Moolenbroek 	 * data.  Raw sockets are not that performance critical so the extra
259*ef8d499eSDavid van Moolenbroek 	 * copy -even when not always necessary- is not that big of a deal.
260*ef8d499eSDavid van Moolenbroek 	 */
261*ef8d499eSDavid van Moolenbroek 	if ((pbuf = pchain_alloc(PBUF_RAW, hdrlen + psrc->tot_len)) == NULL) {
262*ef8d499eSDavid van Moolenbroek 		if (off > 0)
263*ef8d499eSDavid van Moolenbroek 			util_pbuf_header(psrc, off);
264*ef8d499eSDavid van Moolenbroek 
265*ef8d499eSDavid van Moolenbroek 		return 0;
266*ef8d499eSDavid van Moolenbroek 	}
267*ef8d499eSDavid van Moolenbroek 
268*ef8d499eSDavid van Moolenbroek 	util_pbuf_header(pbuf, -hdrlen);
269*ef8d499eSDavid van Moolenbroek 
270*ef8d499eSDavid van Moolenbroek 	if (pbuf_copy(pbuf, psrc) != ERR_OK)
271*ef8d499eSDavid van Moolenbroek 		panic("unexpected pbuf copy failure");
272*ef8d499eSDavid van Moolenbroek 
273*ef8d499eSDavid van Moolenbroek 	pbuf->flags |= psrc->flags & (PBUF_FLAG_LLMCAST | PBUF_FLAG_LLBCAST);
274*ef8d499eSDavid van Moolenbroek 
275*ef8d499eSDavid van Moolenbroek 	if (off > 0)
276*ef8d499eSDavid van Moolenbroek 		util_pbuf_header(psrc, off);
277*ef8d499eSDavid van Moolenbroek 
278*ef8d499eSDavid van Moolenbroek 	if (!rawsock_is_ipv6(raw))
279*ef8d499eSDavid van Moolenbroek 		rawsock_adjust_v4(pbuf);
280*ef8d499eSDavid van Moolenbroek 
281*ef8d499eSDavid van Moolenbroek 	pktsock_input(&raw->raw_pktsock, pbuf, srcaddr, 0);
282*ef8d499eSDavid van Moolenbroek 
283*ef8d499eSDavid van Moolenbroek 	return 0;
284*ef8d499eSDavid van Moolenbroek }
285*ef8d499eSDavid van Moolenbroek 
286*ef8d499eSDavid van Moolenbroek /*
287*ef8d499eSDavid van Moolenbroek  * Create a raw socket.
288*ef8d499eSDavid van Moolenbroek  */
289*ef8d499eSDavid van Moolenbroek sockid_t
rawsock_socket(int domain,int protocol,struct sock ** sockp,const struct sockevent_ops ** ops)290*ef8d499eSDavid van Moolenbroek rawsock_socket(int domain, int protocol, struct sock ** sockp,
291*ef8d499eSDavid van Moolenbroek 	const struct sockevent_ops ** ops)
292*ef8d499eSDavid van Moolenbroek {
293*ef8d499eSDavid van Moolenbroek 	struct rawsock *raw;
294*ef8d499eSDavid van Moolenbroek 	unsigned int flags;
295*ef8d499eSDavid van Moolenbroek 	uint8_t ip_type;
296*ef8d499eSDavid van Moolenbroek 
297*ef8d499eSDavid van Moolenbroek 	if (protocol < 0 || protocol > UINT8_MAX)
298*ef8d499eSDavid van Moolenbroek 		return EPROTONOSUPPORT;
299*ef8d499eSDavid van Moolenbroek 
300*ef8d499eSDavid van Moolenbroek 	if (TAILQ_EMPTY(&raw_freelist))
301*ef8d499eSDavid van Moolenbroek 		return ENOBUFS;
302*ef8d499eSDavid van Moolenbroek 
303*ef8d499eSDavid van Moolenbroek 	raw = TAILQ_FIRST(&raw_freelist);
304*ef8d499eSDavid van Moolenbroek 
305*ef8d499eSDavid van Moolenbroek 	/*
306*ef8d499eSDavid van Moolenbroek 	 * Initialize the structure.  Do not memset it to zero, as it is still
307*ef8d499eSDavid van Moolenbroek 	 * part of the linked free list.  Initialization may still fail.
308*ef8d499eSDavid van Moolenbroek 	 */
309*ef8d499eSDavid van Moolenbroek 
310*ef8d499eSDavid van Moolenbroek 	ip_type = pktsock_socket(&raw->raw_pktsock, domain, RAW_SNDBUF_DEF,
311*ef8d499eSDavid van Moolenbroek 	    RAW_RCVBUF_DEF, sockp);
312*ef8d499eSDavid van Moolenbroek 
313*ef8d499eSDavid van Moolenbroek 	/* We should have enough PCBs so this call should not fail.. */
314*ef8d499eSDavid van Moolenbroek 	if ((raw->raw_pcb = raw_new_ip_type(ip_type, protocol)) == NULL)
315*ef8d499eSDavid van Moolenbroek 		return ENOBUFS;
316*ef8d499eSDavid van Moolenbroek 	raw_recv(raw->raw_pcb, rawsock_input, (void *)raw);
317*ef8d499eSDavid van Moolenbroek 
318*ef8d499eSDavid van Moolenbroek 	/* By default, the multicast TTL is 1 and looping is enabled. */
319*ef8d499eSDavid van Moolenbroek 	raw_set_multicast_ttl(raw->raw_pcb, 1);
320*ef8d499eSDavid van Moolenbroek 
321*ef8d499eSDavid van Moolenbroek 	flags = raw_flags(raw->raw_pcb);
322*ef8d499eSDavid van Moolenbroek 	raw_setflags(raw->raw_pcb, flags | RAW_FLAGS_MULTICAST_LOOP);
323*ef8d499eSDavid van Moolenbroek 
324*ef8d499eSDavid van Moolenbroek 	/*
325*ef8d499eSDavid van Moolenbroek 	 * For ICMPv6, checksum generation and verification is mandatory and
326*ef8d499eSDavid van Moolenbroek 	 * type filtering of incoming packets is supported (RFC 3542).  For all
327*ef8d499eSDavid van Moolenbroek 	 * other IPv6 protocols, checksumming may be turned on by the user.
328*ef8d499eSDavid van Moolenbroek 	 */
329*ef8d499eSDavid van Moolenbroek 	if (rawsock_is_ipv6(raw) && protocol == IPPROTO_ICMPV6) {
330*ef8d499eSDavid van Moolenbroek 		raw->raw_pcb->chksum_reqd = 1;
331*ef8d499eSDavid van Moolenbroek 		raw->raw_pcb->chksum_offset =
332*ef8d499eSDavid van Moolenbroek 		    offsetof(struct icmp6_hdr, icmp6_cksum);
333*ef8d499eSDavid van Moolenbroek 
334*ef8d499eSDavid van Moolenbroek 		ICMP6_FILTER_SETPASSALL(&raw->raw_icmp6filter);
335*ef8d499eSDavid van Moolenbroek 	} else
336*ef8d499eSDavid van Moolenbroek 		raw->raw_pcb->chksum_reqd = 0;
337*ef8d499eSDavid van Moolenbroek 
338*ef8d499eSDavid van Moolenbroek 	TAILQ_REMOVE(&raw_freelist, raw, raw_next);
339*ef8d499eSDavid van Moolenbroek 
340*ef8d499eSDavid van Moolenbroek 	TAILQ_INSERT_TAIL(&raw_activelist, raw, raw_next);
341*ef8d499eSDavid van Moolenbroek 
342*ef8d499eSDavid van Moolenbroek 	*ops = &rawsock_ops;
343*ef8d499eSDavid van Moolenbroek 	return SOCKID_RAW | (sockid_t)(raw - raw_array);
344*ef8d499eSDavid van Moolenbroek }
345*ef8d499eSDavid van Moolenbroek 
346*ef8d499eSDavid van Moolenbroek /*
347*ef8d499eSDavid van Moolenbroek  * Bind a raw socket to a local address.
348*ef8d499eSDavid van Moolenbroek  */
349*ef8d499eSDavid van Moolenbroek static int
rawsock_bind(struct sock * sock,const struct sockaddr * addr,socklen_t addr_len,endpoint_t user_endpt)350*ef8d499eSDavid van Moolenbroek rawsock_bind(struct sock * sock, const struct sockaddr * addr,
351*ef8d499eSDavid van Moolenbroek 	socklen_t addr_len, endpoint_t user_endpt)
352*ef8d499eSDavid van Moolenbroek {
353*ef8d499eSDavid van Moolenbroek 	struct rawsock *raw = (struct rawsock *)sock;
354*ef8d499eSDavid van Moolenbroek 	ip_addr_t ipaddr;
355*ef8d499eSDavid van Moolenbroek 	err_t err;
356*ef8d499eSDavid van Moolenbroek 	int r;
357*ef8d499eSDavid van Moolenbroek 
358*ef8d499eSDavid van Moolenbroek 	/*
359*ef8d499eSDavid van Moolenbroek 	 * Raw sockets may be rebound even if that is not too useful.  However,
360*ef8d499eSDavid van Moolenbroek 	 * we do not allow (re)binding when the socket is connected, so as to
361*ef8d499eSDavid van Moolenbroek 	 * eliminate any problems with source and destination type mismatches:
362*ef8d499eSDavid van Moolenbroek 	 * such mismatches are detected at connect time, and rebinding would
363*ef8d499eSDavid van Moolenbroek 	 * avoid those, possibly triggering lwIP asserts as a result.
364*ef8d499eSDavid van Moolenbroek 	 */
365*ef8d499eSDavid van Moolenbroek 	if (rawsock_is_conn(raw))
366*ef8d499eSDavid van Moolenbroek 		return EINVAL;
367*ef8d499eSDavid van Moolenbroek 
368*ef8d499eSDavid van Moolenbroek 	if ((r = ipsock_get_src_addr(rawsock_get_ipsock(raw), addr, addr_len,
369*ef8d499eSDavid van Moolenbroek 	    user_endpt, &raw->raw_pcb->local_ip, 0 /*local_port*/,
370*ef8d499eSDavid van Moolenbroek 	    TRUE /*allow_mcast*/, &ipaddr, NULL /*portp*/)) != OK)
371*ef8d499eSDavid van Moolenbroek 		return r;
372*ef8d499eSDavid van Moolenbroek 
373*ef8d499eSDavid van Moolenbroek 	err = raw_bind(raw->raw_pcb, &ipaddr);
374*ef8d499eSDavid van Moolenbroek 
375*ef8d499eSDavid van Moolenbroek 	return util_convert_err(err);
376*ef8d499eSDavid van Moolenbroek }
377*ef8d499eSDavid van Moolenbroek 
378*ef8d499eSDavid van Moolenbroek /*
379*ef8d499eSDavid van Moolenbroek  * Connect a raw socket to a remote address.
380*ef8d499eSDavid van Moolenbroek  */
381*ef8d499eSDavid van Moolenbroek static int
rawsock_connect(struct sock * sock,const struct sockaddr * addr,socklen_t addr_len,endpoint_t user_endpt __unused)382*ef8d499eSDavid van Moolenbroek rawsock_connect(struct sock * sock, const struct sockaddr * addr,
383*ef8d499eSDavid van Moolenbroek 	socklen_t addr_len, endpoint_t user_endpt __unused)
384*ef8d499eSDavid van Moolenbroek {
385*ef8d499eSDavid van Moolenbroek 	struct rawsock *raw = (struct rawsock *)sock;
386*ef8d499eSDavid van Moolenbroek 	const ip_addr_t *src_addr;
387*ef8d499eSDavid van Moolenbroek 	ip_addr_t dst_addr;
388*ef8d499eSDavid van Moolenbroek 	struct ifdev *ifdev;
389*ef8d499eSDavid van Moolenbroek 	uint32_t ifindex, ifindex2;
390*ef8d499eSDavid van Moolenbroek 	err_t err;
391*ef8d499eSDavid van Moolenbroek 	int r;
392*ef8d499eSDavid van Moolenbroek 
393*ef8d499eSDavid van Moolenbroek 	/*
394*ef8d499eSDavid van Moolenbroek 	 * One may "unconnect" socket by providing an address with family
395*ef8d499eSDavid van Moolenbroek 	 * AF_UNSPEC.
396*ef8d499eSDavid van Moolenbroek 	 */
397*ef8d499eSDavid van Moolenbroek 	if (addr_is_unspec(addr, addr_len)) {
398*ef8d499eSDavid van Moolenbroek 		raw_disconnect(raw->raw_pcb);
399*ef8d499eSDavid van Moolenbroek 
400*ef8d499eSDavid van Moolenbroek 		return OK;
401*ef8d499eSDavid van Moolenbroek 	}
402*ef8d499eSDavid van Moolenbroek 
403*ef8d499eSDavid van Moolenbroek 	if ((r = ipsock_get_dst_addr(rawsock_get_ipsock(raw), addr, addr_len,
404*ef8d499eSDavid van Moolenbroek 	    &raw->raw_pcb->local_ip, &dst_addr, NULL /*dst_port*/)) != OK)
405*ef8d499eSDavid van Moolenbroek 		return r;
406*ef8d499eSDavid van Moolenbroek 
407*ef8d499eSDavid van Moolenbroek 	/*
408*ef8d499eSDavid van Moolenbroek 	 * Bind explicitly to a source address if the PCB is not bound to one
409*ef8d499eSDavid van Moolenbroek 	 * yet.  This is expected in the BSD socket API, but lwIP does not do
410*ef8d499eSDavid van Moolenbroek 	 * it for us.
411*ef8d499eSDavid van Moolenbroek 	 */
412*ef8d499eSDavid van Moolenbroek 	if (ip_addr_isany(&raw->raw_pcb->local_ip)) {
413*ef8d499eSDavid van Moolenbroek 		/* Help the multicast case a bit, if possible. */
414*ef8d499eSDavid van Moolenbroek 		ifdev = NULL;
415*ef8d499eSDavid van Moolenbroek 		if (ip_addr_ismulticast(&dst_addr)) {
416*ef8d499eSDavid van Moolenbroek 			ifindex = pktsock_get_ifindex(&raw->raw_pktsock);
417*ef8d499eSDavid van Moolenbroek 			ifindex2 = raw_get_multicast_netif_index(raw->raw_pcb);
418*ef8d499eSDavid van Moolenbroek 			if (ifindex == 0)
419*ef8d499eSDavid van Moolenbroek 				ifindex = ifindex2;
420*ef8d499eSDavid van Moolenbroek 
421*ef8d499eSDavid van Moolenbroek 			if (ifindex != 0) {
422*ef8d499eSDavid van Moolenbroek 				ifdev = ifdev_get_by_index(ifindex);
423*ef8d499eSDavid van Moolenbroek 
424*ef8d499eSDavid van Moolenbroek 				if (ifdev == NULL)
425*ef8d499eSDavid van Moolenbroek 					return ENXIO;
426*ef8d499eSDavid van Moolenbroek 			}
427*ef8d499eSDavid van Moolenbroek 		}
428*ef8d499eSDavid van Moolenbroek 
429*ef8d499eSDavid van Moolenbroek 		src_addr = ifaddr_select(&dst_addr, ifdev, NULL /*ifdevp*/);
430*ef8d499eSDavid van Moolenbroek 
431*ef8d499eSDavid van Moolenbroek 		if (src_addr == NULL)
432*ef8d499eSDavid van Moolenbroek 			return EHOSTUNREACH;
433*ef8d499eSDavid van Moolenbroek 
434*ef8d499eSDavid van Moolenbroek 		err = raw_bind(raw->raw_pcb, src_addr);
435*ef8d499eSDavid van Moolenbroek 
436*ef8d499eSDavid van Moolenbroek 		if (err != ERR_OK)
437*ef8d499eSDavid van Moolenbroek 			return util_convert_err(err);
438*ef8d499eSDavid van Moolenbroek 	}
439*ef8d499eSDavid van Moolenbroek 
440*ef8d499eSDavid van Moolenbroek 	/*
441*ef8d499eSDavid van Moolenbroek 	 * Connecting a raw socket serves two main purposes: 1) the socket uses
442*ef8d499eSDavid van Moolenbroek 	 * the address as destination when sending, and 2) the socket receives
443*ef8d499eSDavid van Moolenbroek 	 * packets from only the connected address.
444*ef8d499eSDavid van Moolenbroek 	 */
445*ef8d499eSDavid van Moolenbroek 	err = raw_connect(raw->raw_pcb, &dst_addr);
446*ef8d499eSDavid van Moolenbroek 
447*ef8d499eSDavid van Moolenbroek 	if (err != ERR_OK)
448*ef8d499eSDavid van Moolenbroek 		return util_convert_err(err);
449*ef8d499eSDavid van Moolenbroek 
450*ef8d499eSDavid van Moolenbroek 	return OK;
451*ef8d499eSDavid van Moolenbroek }
452*ef8d499eSDavid van Moolenbroek 
453*ef8d499eSDavid van Moolenbroek /*
454*ef8d499eSDavid van Moolenbroek  * Perform preliminary checks on a send request.
455*ef8d499eSDavid van Moolenbroek  */
456*ef8d499eSDavid van Moolenbroek static int
rawsock_pre_send(struct sock * sock,size_t len,socklen_t ctl_len __unused,const struct sockaddr * addr,socklen_t addr_len __unused,endpoint_t user_endpt __unused,int flags)457*ef8d499eSDavid van Moolenbroek rawsock_pre_send(struct sock * sock, size_t len, socklen_t ctl_len __unused,
458*ef8d499eSDavid van Moolenbroek 	const struct sockaddr * addr, socklen_t addr_len __unused,
459*ef8d499eSDavid van Moolenbroek 	endpoint_t user_endpt __unused, int flags)
460*ef8d499eSDavid van Moolenbroek {
461*ef8d499eSDavid van Moolenbroek 	struct rawsock *raw = (struct rawsock *)sock;
462*ef8d499eSDavid van Moolenbroek 
463*ef8d499eSDavid van Moolenbroek 	if ((flags & ~MSG_DONTROUTE) != 0)
464*ef8d499eSDavid van Moolenbroek 		return EOPNOTSUPP;
465*ef8d499eSDavid van Moolenbroek 
466*ef8d499eSDavid van Moolenbroek 	if (!rawsock_is_conn(raw) && addr == NULL)
467*ef8d499eSDavid van Moolenbroek 		return EDESTADDRREQ;
468*ef8d499eSDavid van Moolenbroek 
469*ef8d499eSDavid van Moolenbroek 	/*
470*ef8d499eSDavid van Moolenbroek 	 * This is only one part of the length check.  The rest is done from
471*ef8d499eSDavid van Moolenbroek 	 * rawsock_send(), once we have more information.
472*ef8d499eSDavid van Moolenbroek 	 */
473*ef8d499eSDavid van Moolenbroek 	if (len > ipsock_get_sndbuf(rawsock_get_ipsock(raw)))
474*ef8d499eSDavid van Moolenbroek 		return EMSGSIZE;
475*ef8d499eSDavid van Moolenbroek 
476*ef8d499eSDavid van Moolenbroek 	return OK;
477*ef8d499eSDavid van Moolenbroek }
478*ef8d499eSDavid van Moolenbroek 
479*ef8d499eSDavid van Moolenbroek /*
480*ef8d499eSDavid van Moolenbroek  * Swap IP-level options between the RAW PCB and the packet options structure,
481*ef8d499eSDavid van Moolenbroek  * for all options that have their flag set in the packet options structure.
482*ef8d499eSDavid van Moolenbroek  * This function is called twice when sending a packet.  The result is that the
483*ef8d499eSDavid van Moolenbroek  * flagged options are overridden for only the packet being sent.
484*ef8d499eSDavid van Moolenbroek  */
485*ef8d499eSDavid van Moolenbroek static void
rawsock_swap_opt(struct rawsock * raw,struct pktopt * pkto)486*ef8d499eSDavid van Moolenbroek rawsock_swap_opt(struct rawsock * raw, struct pktopt * pkto)
487*ef8d499eSDavid van Moolenbroek {
488*ef8d499eSDavid van Moolenbroek 	uint8_t tos, ttl, mcast_ttl;
489*ef8d499eSDavid van Moolenbroek 
490*ef8d499eSDavid van Moolenbroek 	if (pkto->pkto_flags & PKTOF_TOS) {
491*ef8d499eSDavid van Moolenbroek 		tos = raw->raw_pcb->tos;
492*ef8d499eSDavid van Moolenbroek 		raw->raw_pcb->tos = pkto->pkto_tos;
493*ef8d499eSDavid van Moolenbroek 		pkto->pkto_tos = tos;
494*ef8d499eSDavid van Moolenbroek 	}
495*ef8d499eSDavid van Moolenbroek 
496*ef8d499eSDavid van Moolenbroek 	if (pkto->pkto_flags & PKTOF_TTL) {
497*ef8d499eSDavid van Moolenbroek 		ttl = raw->raw_pcb->ttl;
498*ef8d499eSDavid van Moolenbroek 		mcast_ttl = raw_get_multicast_ttl(raw->raw_pcb);
499*ef8d499eSDavid van Moolenbroek 		raw->raw_pcb->ttl = pkto->pkto_ttl;
500*ef8d499eSDavid van Moolenbroek 		raw_set_multicast_ttl(raw->raw_pcb, pkto->pkto_ttl);
501*ef8d499eSDavid van Moolenbroek 		pkto->pkto_ttl = ttl;
502*ef8d499eSDavid van Moolenbroek 		pkto->pkto_mcast_ttl = mcast_ttl;
503*ef8d499eSDavid van Moolenbroek 	}
504*ef8d499eSDavid van Moolenbroek }
505*ef8d499eSDavid van Moolenbroek 
506*ef8d499eSDavid van Moolenbroek /*
507*ef8d499eSDavid van Moolenbroek  * We are about to send the given packet that already includes an IPv4 header,
508*ef8d499eSDavid van Moolenbroek  * because the IP_HDRINCL option is enabled on a raw IPv4 socket.  Prepare the
509*ef8d499eSDavid van Moolenbroek  * IPv4 header for sending, by modifying a few fields in it, as expected by
510*ef8d499eSDavid van Moolenbroek  * userland.
511*ef8d499eSDavid van Moolenbroek  */
512*ef8d499eSDavid van Moolenbroek static int
rawsock_prepare_hdrincl(struct rawsock * raw,struct pbuf * pbuf,const ip_addr_t * src_addr)513*ef8d499eSDavid van Moolenbroek rawsock_prepare_hdrincl(struct rawsock * raw, struct pbuf * pbuf,
514*ef8d499eSDavid van Moolenbroek 	const ip_addr_t * src_addr)
515*ef8d499eSDavid van Moolenbroek {
516*ef8d499eSDavid van Moolenbroek 	struct ip_hdr *iphdr;
517*ef8d499eSDavid van Moolenbroek 	size_t hlen;
518*ef8d499eSDavid van Moolenbroek 
519*ef8d499eSDavid van Moolenbroek 	/*
520*ef8d499eSDavid van Moolenbroek 	 * lwIP obtains the destination address from the IP packet header in
521*ef8d499eSDavid van Moolenbroek 	 * this case, so make sure the packet has a full-sized header.
522*ef8d499eSDavid van Moolenbroek 	 */
523*ef8d499eSDavid van Moolenbroek 	if (pbuf->len < sizeof(struct ip_hdr))
524*ef8d499eSDavid van Moolenbroek 		return EINVAL;
525*ef8d499eSDavid van Moolenbroek 
526*ef8d499eSDavid van Moolenbroek 	iphdr = (struct ip_hdr *)pbuf->payload;
527*ef8d499eSDavid van Moolenbroek 
528*ef8d499eSDavid van Moolenbroek 	/*
529*ef8d499eSDavid van Moolenbroek 	 * Fill in the source address if it is not set, and do the byte
530*ef8d499eSDavid van Moolenbroek 	 * swapping and checksum computation common for the BSDs, without which
531*ef8d499eSDavid van Moolenbroek 	 * ping(8) and traceroute(8) do not work properly.  We consider this a
532*ef8d499eSDavid van Moolenbroek 	 * convenience feature, so malformed packets are simply sent as is.
533*ef8d499eSDavid van Moolenbroek 	 * TODO: deal with type punning..
534*ef8d499eSDavid van Moolenbroek 	 */
535*ef8d499eSDavid van Moolenbroek 	hlen = (size_t)IPH_HL(iphdr) << 2;
536*ef8d499eSDavid van Moolenbroek 
537*ef8d499eSDavid van Moolenbroek 	if (pbuf->len >= hlen) {
538*ef8d499eSDavid van Moolenbroek 		/* Fill in the source address if it is blank. */
539*ef8d499eSDavid van Moolenbroek 		if (iphdr->src.addr == PP_HTONL(INADDR_ANY)) {
540*ef8d499eSDavid van Moolenbroek 			assert(IP_IS_V4(src_addr));
541*ef8d499eSDavid van Moolenbroek 
542*ef8d499eSDavid van Moolenbroek 			iphdr->src.addr = ip_addr_get_ip4_u32(src_addr);
543*ef8d499eSDavid van Moolenbroek 		}
544*ef8d499eSDavid van Moolenbroek 
545*ef8d499eSDavid van Moolenbroek 		IPH_LEN(iphdr) = htons(IPH_LEN(iphdr));
546*ef8d499eSDavid van Moolenbroek 		IPH_OFFSET(iphdr) = htons(IPH_OFFSET(iphdr));
547*ef8d499eSDavid van Moolenbroek 		IPH_CHKSUM(iphdr) = 0;
548*ef8d499eSDavid van Moolenbroek 
549*ef8d499eSDavid van Moolenbroek 		IPH_CHKSUM(iphdr) = inet_chksum(iphdr, hlen);
550*ef8d499eSDavid van Moolenbroek 	}
551*ef8d499eSDavid van Moolenbroek 
552*ef8d499eSDavid van Moolenbroek 	return OK;
553*ef8d499eSDavid van Moolenbroek }
554*ef8d499eSDavid van Moolenbroek 
555*ef8d499eSDavid van Moolenbroek /*
556*ef8d499eSDavid van Moolenbroek  * Send a packet on a raw socket.
557*ef8d499eSDavid van Moolenbroek  */
558*ef8d499eSDavid van Moolenbroek static int
rawsock_send(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,const struct sockaddr * addr,socklen_t addr_len,endpoint_t user_endpt __unused,int flags,size_t min __unused)559*ef8d499eSDavid van Moolenbroek rawsock_send(struct sock * sock, const struct sockdriver_data * data,
560*ef8d499eSDavid van Moolenbroek 	size_t len, size_t * off, const struct sockdriver_data * ctl __unused,
561*ef8d499eSDavid van Moolenbroek 	socklen_t ctl_len __unused, socklen_t * ctl_off __unused,
562*ef8d499eSDavid van Moolenbroek 	const struct sockaddr * addr, socklen_t addr_len,
563*ef8d499eSDavid van Moolenbroek 	endpoint_t user_endpt __unused, int flags, size_t min __unused)
564*ef8d499eSDavid van Moolenbroek {
565*ef8d499eSDavid van Moolenbroek 	struct rawsock *raw = (struct rawsock *)sock;
566*ef8d499eSDavid van Moolenbroek 	struct pktopt pktopt;
567*ef8d499eSDavid van Moolenbroek 	struct pbuf *pbuf;
568*ef8d499eSDavid van Moolenbroek 	struct ifdev *ifdev;
569*ef8d499eSDavid van Moolenbroek 	struct netif *netif;
570*ef8d499eSDavid van Moolenbroek 	const ip_addr_t *dst_addrp, *src_addrp;
571*ef8d499eSDavid van Moolenbroek 	ip_addr_t src_addr, dst_addr; /* for storage only; not always used! */
572*ef8d499eSDavid van Moolenbroek 	size_t hdrlen;
573*ef8d499eSDavid van Moolenbroek 	uint32_t ifindex;
574*ef8d499eSDavid van Moolenbroek 	err_t err;
575*ef8d499eSDavid van Moolenbroek 	int r;
576*ef8d499eSDavid van Moolenbroek 
577*ef8d499eSDavid van Moolenbroek 	/* Copy in and parse any packet options. */
578*ef8d499eSDavid van Moolenbroek 	pktopt.pkto_flags = 0;
579*ef8d499eSDavid van Moolenbroek 
580*ef8d499eSDavid van Moolenbroek 	if ((r = pktsock_get_ctl(&raw->raw_pktsock, ctl, ctl_len,
581*ef8d499eSDavid van Moolenbroek 	    &pktopt)) != OK)
582*ef8d499eSDavid van Moolenbroek 		return r;
583*ef8d499eSDavid van Moolenbroek 
584*ef8d499eSDavid van Moolenbroek 	/*
585*ef8d499eSDavid van Moolenbroek 	 * For a more in-depth explanation of what is going on here, see the
586*ef8d499eSDavid van Moolenbroek 	 * udpsock module, which has largely the same code but with more
587*ef8d499eSDavid van Moolenbroek 	 * elaborate comments.
588*ef8d499eSDavid van Moolenbroek 	 */
589*ef8d499eSDavid van Moolenbroek 
590*ef8d499eSDavid van Moolenbroek 	/*
591*ef8d499eSDavid van Moolenbroek 	 * Start by checking whether the source address and/or the outgoing
592*ef8d499eSDavid van Moolenbroek 	 * interface are overridden using sticky and/or ancillary options.
593*ef8d499eSDavid van Moolenbroek 	 */
594*ef8d499eSDavid van Moolenbroek 	if ((r = pktsock_get_pktinfo(&raw->raw_pktsock, &pktopt, &ifdev,
595*ef8d499eSDavid van Moolenbroek 	    &src_addr)) != OK)
596*ef8d499eSDavid van Moolenbroek 		return r;
597*ef8d499eSDavid van Moolenbroek 
598*ef8d499eSDavid van Moolenbroek 	if (ifdev != NULL && !ip_addr_isany(&src_addr)) {
599*ef8d499eSDavid van Moolenbroek 		/* This is guaranteed to be a proper local unicast address. */
600*ef8d499eSDavid van Moolenbroek 		src_addrp = &src_addr;
601*ef8d499eSDavid van Moolenbroek 	} else {
602*ef8d499eSDavid van Moolenbroek 		src_addrp = &raw->raw_pcb->local_ip;
603*ef8d499eSDavid van Moolenbroek 
604*ef8d499eSDavid van Moolenbroek 		/*
605*ef8d499eSDavid van Moolenbroek 		 * If the socket is bound to a multicast address, use the
606*ef8d499eSDavid van Moolenbroek 		 * unspecified ('any') address as source address instead.  A
607*ef8d499eSDavid van Moolenbroek 		 * real source address will then be selected further below.
608*ef8d499eSDavid van Moolenbroek 		 */
609*ef8d499eSDavid van Moolenbroek 		if (ip_addr_ismulticast(src_addrp))
610*ef8d499eSDavid van Moolenbroek 			src_addrp = IP46_ADDR_ANY(IP_GET_TYPE(src_addrp));
611*ef8d499eSDavid van Moolenbroek 	}
612*ef8d499eSDavid van Moolenbroek 
613*ef8d499eSDavid van Moolenbroek 	/*
614*ef8d499eSDavid van Moolenbroek 	 * Determine the destination address to use.  If the socket is
615*ef8d499eSDavid van Moolenbroek 	 * connected, always ignore any address provided in the send call.
616*ef8d499eSDavid van Moolenbroek 	 */
617*ef8d499eSDavid van Moolenbroek 	if (!rawsock_is_conn(raw)) {
618*ef8d499eSDavid van Moolenbroek 		assert(addr != NULL); /* already checked in pre_send */
619*ef8d499eSDavid van Moolenbroek 
620*ef8d499eSDavid van Moolenbroek 		if ((r = ipsock_get_dst_addr(rawsock_get_ipsock(raw), addr,
621*ef8d499eSDavid van Moolenbroek 		    addr_len, src_addrp, &dst_addr, NULL /*dst_port*/)) != OK)
622*ef8d499eSDavid van Moolenbroek 			return r;
623*ef8d499eSDavid van Moolenbroek 
624*ef8d499eSDavid van Moolenbroek 		dst_addrp = &dst_addr;
625*ef8d499eSDavid van Moolenbroek 	} else
626*ef8d499eSDavid van Moolenbroek 		dst_addrp = &raw->raw_pcb->remote_ip;
627*ef8d499eSDavid van Moolenbroek 
628*ef8d499eSDavid van Moolenbroek 	/*
629*ef8d499eSDavid van Moolenbroek 	 * If the destination is a multicast address, select the outgoing
630*ef8d499eSDavid van Moolenbroek 	 * interface based on the multicast interface index, if one is set.
631*ef8d499eSDavid van Moolenbroek 	 * This must however *not* override an interface index already
632*ef8d499eSDavid van Moolenbroek 	 * specified using IPV6_PKTINFO, as per RFC 3542 Sec. 6.7.
633*ef8d499eSDavid van Moolenbroek 	 */
634*ef8d499eSDavid van Moolenbroek 	if (ifdev == NULL && ip_addr_ismulticast(dst_addrp)) {
635*ef8d499eSDavid van Moolenbroek 		ifindex = raw_get_multicast_netif_index(raw->raw_pcb);
636*ef8d499eSDavid van Moolenbroek 
637*ef8d499eSDavid van Moolenbroek 		if (ifindex != NETIF_NO_INDEX)
638*ef8d499eSDavid van Moolenbroek 			ifdev = ifdev_get_by_index(ifindex); /* (may fail) */
639*ef8d499eSDavid van Moolenbroek 	}
640*ef8d499eSDavid van Moolenbroek 
641*ef8d499eSDavid van Moolenbroek 	/*
642*ef8d499eSDavid van Moolenbroek 	 * If an interface has been determined already now, the send operation
643*ef8d499eSDavid van Moolenbroek 	 * will bypass routing.  In that case, we must perform our own checks
644*ef8d499eSDavid van Moolenbroek 	 * on address zone violations, because those will not be made anywhere
645*ef8d499eSDavid van Moolenbroek 	 * else.  Subsequent steps below will never introduce violations.
646*ef8d499eSDavid van Moolenbroek 	 */
647*ef8d499eSDavid van Moolenbroek 	if (ifdev != NULL && IP_IS_V6(dst_addrp)) {
648*ef8d499eSDavid van Moolenbroek 		if (ifaddr_is_zone_mismatch(ip_2_ip6(dst_addrp), ifdev))
649*ef8d499eSDavid van Moolenbroek 			return EHOSTUNREACH;
650*ef8d499eSDavid van Moolenbroek 
651*ef8d499eSDavid van Moolenbroek 		if (IP_IS_V6(src_addrp) &&
652*ef8d499eSDavid van Moolenbroek 		    ifaddr_is_zone_mismatch(ip_2_ip6(src_addrp), ifdev))
653*ef8d499eSDavid van Moolenbroek 			return EHOSTUNREACH;
654*ef8d499eSDavid van Moolenbroek 	}
655*ef8d499eSDavid van Moolenbroek 
656*ef8d499eSDavid van Moolenbroek 	/*
657*ef8d499eSDavid van Moolenbroek 	 * If we do not yet have an interface at this point, perform a route
658*ef8d499eSDavid van Moolenbroek 	 * lookup to determine the outgoing interface, unless MSG_DONTROUTE is
659*ef8d499eSDavid van Moolenbroek 	 * set.
660*ef8d499eSDavid van Moolenbroek 	 */
661*ef8d499eSDavid van Moolenbroek 	if (ifdev == NULL) {
662*ef8d499eSDavid van Moolenbroek 		if (!(flags & MSG_DONTROUTE)) {
663*ef8d499eSDavid van Moolenbroek 			/*
664*ef8d499eSDavid van Moolenbroek 			 * ip_route() should never be called with an
665*ef8d499eSDavid van Moolenbroek 			 * IPADDR_TYPE_ANY type address.  This is a lwIP-
666*ef8d499eSDavid van Moolenbroek 			 * internal requirement; while we override both routing
667*ef8d499eSDavid van Moolenbroek 			 * functions, we do not deviate from it.
668*ef8d499eSDavid van Moolenbroek 			 */
669*ef8d499eSDavid van Moolenbroek 			if (IP_IS_ANY_TYPE_VAL(*src_addrp))
670*ef8d499eSDavid van Moolenbroek 				src_addrp =
671*ef8d499eSDavid van Moolenbroek 				    IP46_ADDR_ANY(IP_GET_TYPE(dst_addrp));
672*ef8d499eSDavid van Moolenbroek 
673*ef8d499eSDavid van Moolenbroek 			/* Perform the route lookup. */
674*ef8d499eSDavid van Moolenbroek 			if ((netif = ip_route(src_addrp, dst_addrp)) == NULL)
675*ef8d499eSDavid van Moolenbroek 				return EHOSTUNREACH;
676*ef8d499eSDavid van Moolenbroek 
677*ef8d499eSDavid van Moolenbroek 			ifdev = netif_get_ifdev(netif);
678*ef8d499eSDavid van Moolenbroek 		} else {
679*ef8d499eSDavid van Moolenbroek 			if ((ifdev = ifaddr_map_by_subnet(dst_addrp)) == NULL)
680*ef8d499eSDavid van Moolenbroek 				return EHOSTUNREACH;
681*ef8d499eSDavid van Moolenbroek 		}
682*ef8d499eSDavid van Moolenbroek 	}
683*ef8d499eSDavid van Moolenbroek 
684*ef8d499eSDavid van Moolenbroek 	/*
685*ef8d499eSDavid van Moolenbroek 	 * At this point we have an outgoing interface.  If we do not have a
686*ef8d499eSDavid van Moolenbroek 	 * source address yet, pick one now.  As a sidenote, if the destination
687*ef8d499eSDavid van Moolenbroek 	 * address is scoped but has no zone, we could also fill in the zone
688*ef8d499eSDavid van Moolenbroek 	 * now.  We let lwIP handle that instead, though.
689*ef8d499eSDavid van Moolenbroek 	 */
690*ef8d499eSDavid van Moolenbroek 	assert(ifdev != NULL);
691*ef8d499eSDavid van Moolenbroek 
692*ef8d499eSDavid van Moolenbroek 	if (ip_addr_isany(src_addrp)) {
693*ef8d499eSDavid van Moolenbroek 		src_addrp = ifaddr_select(dst_addrp, ifdev, NULL /*ifdevp*/);
694*ef8d499eSDavid van Moolenbroek 
695*ef8d499eSDavid van Moolenbroek 		if (src_addrp == NULL)
696*ef8d499eSDavid van Moolenbroek 			return EHOSTUNREACH;
697*ef8d499eSDavid van Moolenbroek 	}
698*ef8d499eSDavid van Moolenbroek 
699*ef8d499eSDavid van Moolenbroek 	/*
700*ef8d499eSDavid van Moolenbroek 	 * Now that we know the full conditions of what we are about to send,
701*ef8d499eSDavid van Moolenbroek 	 * check whether the packet size leaves enough room for lwIP to prepend
702*ef8d499eSDavid van Moolenbroek 	 * headers.  If so, allocate a chain of pbufs for the packet.
703*ef8d499eSDavid van Moolenbroek 	 */
704*ef8d499eSDavid van Moolenbroek 	assert(len <= RAW_MAX_PAYLOAD);
705*ef8d499eSDavid van Moolenbroek 
706*ef8d499eSDavid van Moolenbroek 	if (rawsock_is_hdrincl(raw))
707*ef8d499eSDavid van Moolenbroek 		hdrlen = 0;
708*ef8d499eSDavid van Moolenbroek 	else if (IP_IS_V6(dst_addrp))
709*ef8d499eSDavid van Moolenbroek 		hdrlen = IP6_HLEN;
710*ef8d499eSDavid van Moolenbroek 	else
711*ef8d499eSDavid van Moolenbroek 		hdrlen = IP_HLEN;
712*ef8d499eSDavid van Moolenbroek 
713*ef8d499eSDavid van Moolenbroek 	if (hdrlen + len > RAW_MAX_PAYLOAD)
714*ef8d499eSDavid van Moolenbroek 		return EMSGSIZE;
715*ef8d499eSDavid van Moolenbroek 
716*ef8d499eSDavid van Moolenbroek 	if ((pbuf = pchain_alloc(PBUF_IP, len)) == NULL)
717*ef8d499eSDavid van Moolenbroek 		return ENOBUFS;
718*ef8d499eSDavid van Moolenbroek 
719*ef8d499eSDavid van Moolenbroek 	/* Copy in the packet data. */
720*ef8d499eSDavid van Moolenbroek 	if ((r = pktsock_get_data(&raw->raw_pktsock, data, len, pbuf)) != OK) {
721*ef8d499eSDavid van Moolenbroek 		pbuf_free(pbuf);
722*ef8d499eSDavid van Moolenbroek 
723*ef8d499eSDavid van Moolenbroek 		return r;
724*ef8d499eSDavid van Moolenbroek 	}
725*ef8d499eSDavid van Moolenbroek 
726*ef8d499eSDavid van Moolenbroek 	/*
727*ef8d499eSDavid van Moolenbroek 	 * If the user has turned on IPV6_CHECKSUM, ensure that the packet is
728*ef8d499eSDavid van Moolenbroek 	 * not only large enough to have the checksum stored at the configured
729*ef8d499eSDavid van Moolenbroek 	 * place, but also that the checksum fits within the first pbuf: if we
730*ef8d499eSDavid van Moolenbroek 	 * do not test this here, an assert will trigger in lwIP later.  Also
731*ef8d499eSDavid van Moolenbroek 	 * zero out the checksum field first, because lwIP does not do that.
732*ef8d499eSDavid van Moolenbroek 	 */
733*ef8d499eSDavid van Moolenbroek 	if (raw->raw_pcb->chksum_reqd) {
734*ef8d499eSDavid van Moolenbroek 		if (pbuf->len < raw->raw_pcb->chksum_offset +
735*ef8d499eSDavid van Moolenbroek 		    sizeof(uint16_t)) {
736*ef8d499eSDavid van Moolenbroek 			pbuf_free(pbuf);
737*ef8d499eSDavid van Moolenbroek 
738*ef8d499eSDavid van Moolenbroek 			return EINVAL;
739*ef8d499eSDavid van Moolenbroek 		}
740*ef8d499eSDavid van Moolenbroek 
741*ef8d499eSDavid van Moolenbroek 		memset((char *)pbuf->payload + raw->raw_pcb->chksum_offset, 0,
742*ef8d499eSDavid van Moolenbroek 		    sizeof(uint16_t));
743*ef8d499eSDavid van Moolenbroek 	}
744*ef8d499eSDavid van Moolenbroek 
745*ef8d499eSDavid van Moolenbroek 	/*
746*ef8d499eSDavid van Moolenbroek 	 * For sockets where an IPv4 header is already included in the packet,
747*ef8d499eSDavid van Moolenbroek 	 * we need to alter a few header fields to be compatible with BSD.
748*ef8d499eSDavid van Moolenbroek 	 */
749*ef8d499eSDavid van Moolenbroek 	if (rawsock_is_hdrincl(raw) &&
750*ef8d499eSDavid van Moolenbroek 	    (r = rawsock_prepare_hdrincl(raw, pbuf, src_addrp)) != OK) {
751*ef8d499eSDavid van Moolenbroek 		pbuf_free(pbuf);
752*ef8d499eSDavid van Moolenbroek 
753*ef8d499eSDavid van Moolenbroek 		return r;
754*ef8d499eSDavid van Moolenbroek 	}
755*ef8d499eSDavid van Moolenbroek 
756*ef8d499eSDavid van Moolenbroek 	/* Set broadcast/multicast flags for accounting purposes. */
757*ef8d499eSDavid van Moolenbroek 	if (ip_addr_ismulticast(dst_addrp))
758*ef8d499eSDavid van Moolenbroek 		pbuf->flags |= PBUF_FLAG_LLMCAST;
759*ef8d499eSDavid van Moolenbroek 	else if (ip_addr_isbroadcast(dst_addrp, ifdev_get_netif(ifdev)))
760*ef8d499eSDavid van Moolenbroek 		pbuf->flags |= PBUF_FLAG_LLBCAST;
761*ef8d499eSDavid van Moolenbroek 
762*ef8d499eSDavid van Moolenbroek 	/* Send the packet. */
763*ef8d499eSDavid van Moolenbroek 	rawsock_swap_opt(raw, &pktopt);
764*ef8d499eSDavid van Moolenbroek 
765*ef8d499eSDavid van Moolenbroek 	assert(!ip_addr_isany(src_addrp));
766*ef8d499eSDavid van Moolenbroek 	assert(!ip_addr_ismulticast(src_addrp));
767*ef8d499eSDavid van Moolenbroek 
768*ef8d499eSDavid van Moolenbroek 	err = raw_sendto_if_src(raw->raw_pcb, pbuf, dst_addrp,
769*ef8d499eSDavid van Moolenbroek 	    ifdev_get_netif(ifdev), src_addrp);
770*ef8d499eSDavid van Moolenbroek 
771*ef8d499eSDavid van Moolenbroek 	rawsock_swap_opt(raw, &pktopt);
772*ef8d499eSDavid van Moolenbroek 
773*ef8d499eSDavid van Moolenbroek 	/* Free the pbuf again. */
774*ef8d499eSDavid van Moolenbroek 	pbuf_free(pbuf);
775*ef8d499eSDavid van Moolenbroek 
776*ef8d499eSDavid van Moolenbroek 	/*
777*ef8d499eSDavid van Moolenbroek 	 * On success, make sure to return the size of the sent packet as well.
778*ef8d499eSDavid van Moolenbroek 	 * As an aside: ctl_off need not be updated, as it is not returned.
779*ef8d499eSDavid van Moolenbroek 	 */
780*ef8d499eSDavid van Moolenbroek 	if ((r = util_convert_err(err)) == OK)
781*ef8d499eSDavid van Moolenbroek 		*off = len;
782*ef8d499eSDavid van Moolenbroek 	return r;
783*ef8d499eSDavid van Moolenbroek }
784*ef8d499eSDavid van Moolenbroek 
785*ef8d499eSDavid van Moolenbroek /*
786*ef8d499eSDavid van Moolenbroek  * Update the set of flag-type socket options on a raw socket.
787*ef8d499eSDavid van Moolenbroek  */
788*ef8d499eSDavid van Moolenbroek static void
rawsock_setsockmask(struct sock * sock,unsigned int mask)789*ef8d499eSDavid van Moolenbroek rawsock_setsockmask(struct sock * sock, unsigned int mask)
790*ef8d499eSDavid van Moolenbroek {
791*ef8d499eSDavid van Moolenbroek 	struct rawsock *raw = (struct rawsock *)sock;
792*ef8d499eSDavid van Moolenbroek 
793*ef8d499eSDavid van Moolenbroek 	/*
794*ef8d499eSDavid van Moolenbroek 	 * FIXME: raw sockets are not supposed to have a broardcast check, so
795*ef8d499eSDavid van Moolenbroek 	 * perhaps just remove this and instead always set SOF_BROADCAST?
796*ef8d499eSDavid van Moolenbroek 	 */
797*ef8d499eSDavid van Moolenbroek 	if (mask & SO_BROADCAST)
798*ef8d499eSDavid van Moolenbroek 		ip_set_option(raw->raw_pcb, SOF_BROADCAST);
799*ef8d499eSDavid van Moolenbroek 	else
800*ef8d499eSDavid van Moolenbroek 		ip_reset_option(raw->raw_pcb, SOF_BROADCAST);
801*ef8d499eSDavid van Moolenbroek }
802*ef8d499eSDavid van Moolenbroek 
803*ef8d499eSDavid van Moolenbroek /*
804*ef8d499eSDavid van Moolenbroek  * Prepare a helper structure for IP-level option processing.
805*ef8d499eSDavid van Moolenbroek  */
806*ef8d499eSDavid van Moolenbroek static void
rawsock_get_ipopts(struct rawsock * raw,struct ipopts * ipopts)807*ef8d499eSDavid van Moolenbroek rawsock_get_ipopts(struct rawsock * raw, struct ipopts * ipopts)
808*ef8d499eSDavid van Moolenbroek {
809*ef8d499eSDavid van Moolenbroek 
810*ef8d499eSDavid van Moolenbroek 	ipopts->local_ip = &raw->raw_pcb->local_ip;
811*ef8d499eSDavid van Moolenbroek 	ipopts->remote_ip = &raw->raw_pcb->remote_ip;
812*ef8d499eSDavid van Moolenbroek 	ipopts->tos = &raw->raw_pcb->tos;
813*ef8d499eSDavid van Moolenbroek 	ipopts->ttl = &raw->raw_pcb->ttl;
814*ef8d499eSDavid van Moolenbroek 	ipopts->sndmin = RAW_SNDBUF_MIN;
815*ef8d499eSDavid van Moolenbroek 	ipopts->sndmax = RAW_SNDBUF_MAX;
816*ef8d499eSDavid van Moolenbroek 	ipopts->rcvmin = RAW_RCVBUF_MIN;
817*ef8d499eSDavid van Moolenbroek 	ipopts->rcvmax = RAW_RCVBUF_MAX;
818*ef8d499eSDavid van Moolenbroek }
819*ef8d499eSDavid van Moolenbroek 
820*ef8d499eSDavid van Moolenbroek /*
821*ef8d499eSDavid van Moolenbroek  * Set socket options on a raw socket.
822*ef8d499eSDavid van Moolenbroek  */
823*ef8d499eSDavid van Moolenbroek static int
rawsock_setsockopt(struct sock * sock,int level,int name,const struct sockdriver_data * data,socklen_t len)824*ef8d499eSDavid van Moolenbroek rawsock_setsockopt(struct sock * sock, int level, int name,
825*ef8d499eSDavid van Moolenbroek 	const struct sockdriver_data * data, socklen_t len)
826*ef8d499eSDavid van Moolenbroek {
827*ef8d499eSDavid van Moolenbroek 	struct rawsock *raw = (struct rawsock *)sock;
828*ef8d499eSDavid van Moolenbroek 	struct ipopts ipopts;
829*ef8d499eSDavid van Moolenbroek 	struct icmp6_filter filter;
830*ef8d499eSDavid van Moolenbroek 	ip_addr_t ipaddr;
831*ef8d499eSDavid van Moolenbroek 	struct in_addr in_addr;
832*ef8d499eSDavid van Moolenbroek 	struct ifdev *ifdev;
833*ef8d499eSDavid van Moolenbroek 	unsigned int flags;
834*ef8d499eSDavid van Moolenbroek 	uint32_t ifindex;
835*ef8d499eSDavid van Moolenbroek 	uint8_t byte;
836*ef8d499eSDavid van Moolenbroek 	int r, val;
837*ef8d499eSDavid van Moolenbroek 
838*ef8d499eSDavid van Moolenbroek 	/*
839*ef8d499eSDavid van Moolenbroek 	 * Unfortunately, we have to duplicate most of the multicast options
840*ef8d499eSDavid van Moolenbroek 	 * rather than sharing them with udpsock at the pktsock level.  The
841*ef8d499eSDavid van Moolenbroek 	 * reason is that each of the PCBs have their own multicast abstraction
842*ef8d499eSDavid van Moolenbroek 	 * functions and so we cannot merge the rest.  Same for getsockopt.
843*ef8d499eSDavid van Moolenbroek 	 */
844*ef8d499eSDavid van Moolenbroek 
845*ef8d499eSDavid van Moolenbroek 	switch (level) {
846*ef8d499eSDavid van Moolenbroek 	case IPPROTO_IP:
847*ef8d499eSDavid van Moolenbroek 		if (rawsock_is_ipv6(raw))
848*ef8d499eSDavid van Moolenbroek 			break;
849*ef8d499eSDavid van Moolenbroek 
850*ef8d499eSDavid van Moolenbroek 		switch (name) {
851*ef8d499eSDavid van Moolenbroek 		case IP_HDRINCL:
852*ef8d499eSDavid van Moolenbroek 			if ((r = sockdriver_copyin_opt(data, &val, sizeof(val),
853*ef8d499eSDavid van Moolenbroek 			    len)) != OK)
854*ef8d499eSDavid van Moolenbroek 				return r;
855*ef8d499eSDavid van Moolenbroek 
856*ef8d499eSDavid van Moolenbroek 			if (val) {
857*ef8d499eSDavid van Moolenbroek 				raw_setflags(raw->raw_pcb,
858*ef8d499eSDavid van Moolenbroek 				    raw_flags(raw->raw_pcb) |
859*ef8d499eSDavid van Moolenbroek 				    RAW_FLAGS_HDRINCL);
860*ef8d499eSDavid van Moolenbroek 			} else {
861*ef8d499eSDavid van Moolenbroek 				raw_setflags(raw->raw_pcb,
862*ef8d499eSDavid van Moolenbroek 				    raw_flags(raw->raw_pcb) &
863*ef8d499eSDavid van Moolenbroek 				    ~RAW_FLAGS_HDRINCL);
864*ef8d499eSDavid van Moolenbroek 			}
865*ef8d499eSDavid van Moolenbroek 
866*ef8d499eSDavid van Moolenbroek 			return OK;
867*ef8d499eSDavid van Moolenbroek 
868*ef8d499eSDavid van Moolenbroek 		case IP_MULTICAST_IF:
869*ef8d499eSDavid van Moolenbroek 			pktsock_set_mcaware(&raw->raw_pktsock);
870*ef8d499eSDavid van Moolenbroek 
871*ef8d499eSDavid van Moolenbroek 			if ((r = sockdriver_copyin_opt(data, &in_addr,
872*ef8d499eSDavid van Moolenbroek 			    sizeof(in_addr), len)) != OK)
873*ef8d499eSDavid van Moolenbroek 				return r;
874*ef8d499eSDavid van Moolenbroek 
875*ef8d499eSDavid van Moolenbroek 			ip_addr_set_ip4_u32(&ipaddr, in_addr.s_addr);
876*ef8d499eSDavid van Moolenbroek 
877*ef8d499eSDavid van Moolenbroek 			if ((ifdev = ifaddr_map_by_addr(&ipaddr)) == NULL)
878*ef8d499eSDavid van Moolenbroek 				return EADDRNOTAVAIL;
879*ef8d499eSDavid van Moolenbroek 
880*ef8d499eSDavid van Moolenbroek 			raw_set_multicast_netif_index(raw->raw_pcb,
881*ef8d499eSDavid van Moolenbroek 			    ifdev_get_index(ifdev));
882*ef8d499eSDavid van Moolenbroek 
883*ef8d499eSDavid van Moolenbroek 			return OK;
884*ef8d499eSDavid van Moolenbroek 
885*ef8d499eSDavid van Moolenbroek 		case IP_MULTICAST_LOOP:
886*ef8d499eSDavid van Moolenbroek 			pktsock_set_mcaware(&raw->raw_pktsock);
887*ef8d499eSDavid van Moolenbroek 
888*ef8d499eSDavid van Moolenbroek 			if ((r = sockdriver_copyin_opt(data, &byte,
889*ef8d499eSDavid van Moolenbroek 			    sizeof(byte), len)) != OK)
890*ef8d499eSDavid van Moolenbroek 				return r;
891*ef8d499eSDavid van Moolenbroek 
892*ef8d499eSDavid van Moolenbroek 			flags = raw_flags(raw->raw_pcb);
893*ef8d499eSDavid van Moolenbroek 
894*ef8d499eSDavid van Moolenbroek 			if (byte)
895*ef8d499eSDavid van Moolenbroek 				flags |= RAW_FLAGS_MULTICAST_LOOP;
896*ef8d499eSDavid van Moolenbroek 			else
897*ef8d499eSDavid van Moolenbroek 				flags &= ~RAW_FLAGS_MULTICAST_LOOP;
898*ef8d499eSDavid van Moolenbroek 
899*ef8d499eSDavid van Moolenbroek 			raw_setflags(raw->raw_pcb, flags);
900*ef8d499eSDavid van Moolenbroek 
901*ef8d499eSDavid van Moolenbroek 			return OK;
902*ef8d499eSDavid van Moolenbroek 
903*ef8d499eSDavid van Moolenbroek 		case IP_MULTICAST_TTL:
904*ef8d499eSDavid van Moolenbroek 			pktsock_set_mcaware(&raw->raw_pktsock);
905*ef8d499eSDavid van Moolenbroek 
906*ef8d499eSDavid van Moolenbroek 			if ((r = sockdriver_copyin_opt(data, &byte,
907*ef8d499eSDavid van Moolenbroek 			    sizeof(byte), len)) != OK)
908*ef8d499eSDavid van Moolenbroek 				return r;
909*ef8d499eSDavid van Moolenbroek 
910*ef8d499eSDavid van Moolenbroek 			raw_set_multicast_ttl(raw->raw_pcb, byte);
911*ef8d499eSDavid van Moolenbroek 
912*ef8d499eSDavid van Moolenbroek 			return OK;
913*ef8d499eSDavid van Moolenbroek 		}
914*ef8d499eSDavid van Moolenbroek 
915*ef8d499eSDavid van Moolenbroek 		break;
916*ef8d499eSDavid van Moolenbroek 
917*ef8d499eSDavid van Moolenbroek 	case IPPROTO_IPV6:
918*ef8d499eSDavid van Moolenbroek 		if (!rawsock_is_ipv6(raw))
919*ef8d499eSDavid van Moolenbroek 			break;
920*ef8d499eSDavid van Moolenbroek 
921*ef8d499eSDavid van Moolenbroek 		switch (name) {
922*ef8d499eSDavid van Moolenbroek 		case IPV6_CHECKSUM:
923*ef8d499eSDavid van Moolenbroek 			/* ICMPv6 checksums are always computed. */
924*ef8d499eSDavid van Moolenbroek 			if (raw->raw_pcb->protocol == IPPROTO_ICMPV6)
925*ef8d499eSDavid van Moolenbroek 				return EINVAL;
926*ef8d499eSDavid van Moolenbroek 
927*ef8d499eSDavid van Moolenbroek 			if ((r = sockdriver_copyin_opt(data, &val, sizeof(val),
928*ef8d499eSDavid van Moolenbroek 			    len)) != OK)
929*ef8d499eSDavid van Moolenbroek 				return r;
930*ef8d499eSDavid van Moolenbroek 
931*ef8d499eSDavid van Moolenbroek 			if (val == -1) {
932*ef8d499eSDavid van Moolenbroek 				raw->raw_pcb->chksum_reqd = 0;
933*ef8d499eSDavid van Moolenbroek 
934*ef8d499eSDavid van Moolenbroek 				return OK;
935*ef8d499eSDavid van Moolenbroek 			} else if (val >= 0 && !(val & 1)) {
936*ef8d499eSDavid van Moolenbroek 				raw->raw_pcb->chksum_reqd = 1;
937*ef8d499eSDavid van Moolenbroek 				raw->raw_pcb->chksum_offset = val;
938*ef8d499eSDavid van Moolenbroek 
939*ef8d499eSDavid van Moolenbroek 				return OK;
940*ef8d499eSDavid van Moolenbroek 			} else
941*ef8d499eSDavid van Moolenbroek 				return EINVAL;
942*ef8d499eSDavid van Moolenbroek 
943*ef8d499eSDavid van Moolenbroek 		case IPV6_MULTICAST_IF:
944*ef8d499eSDavid van Moolenbroek 			pktsock_set_mcaware(&raw->raw_pktsock);
945*ef8d499eSDavid van Moolenbroek 
946*ef8d499eSDavid van Moolenbroek 			if ((r = sockdriver_copyin_opt(data, &val, sizeof(val),
947*ef8d499eSDavid van Moolenbroek 			    len)) != OK)
948*ef8d499eSDavid van Moolenbroek 				return r;
949*ef8d499eSDavid van Moolenbroek 
950*ef8d499eSDavid van Moolenbroek 			if (val != 0) {
951*ef8d499eSDavid van Moolenbroek 				ifindex = (uint32_t)val;
952*ef8d499eSDavid van Moolenbroek 
953*ef8d499eSDavid van Moolenbroek 				ifdev = ifdev_get_by_index(ifindex);
954*ef8d499eSDavid van Moolenbroek 
955*ef8d499eSDavid van Moolenbroek 				if (ifdev == NULL)
956*ef8d499eSDavid van Moolenbroek 					return ENXIO;
957*ef8d499eSDavid van Moolenbroek 			} else
958*ef8d499eSDavid van Moolenbroek 				ifindex = NETIF_NO_INDEX;
959*ef8d499eSDavid van Moolenbroek 
960*ef8d499eSDavid van Moolenbroek 			raw_set_multicast_netif_index(raw->raw_pcb, ifindex);
961*ef8d499eSDavid van Moolenbroek 
962*ef8d499eSDavid van Moolenbroek 			return OK;
963*ef8d499eSDavid van Moolenbroek 
964*ef8d499eSDavid van Moolenbroek 		case IPV6_MULTICAST_LOOP:
965*ef8d499eSDavid van Moolenbroek 			pktsock_set_mcaware(&raw->raw_pktsock);
966*ef8d499eSDavid van Moolenbroek 
967*ef8d499eSDavid van Moolenbroek 			if ((r = sockdriver_copyin_opt(data, &val, sizeof(val),
968*ef8d499eSDavid van Moolenbroek 			    len)) != OK)
969*ef8d499eSDavid van Moolenbroek 				return r;
970*ef8d499eSDavid van Moolenbroek 
971*ef8d499eSDavid van Moolenbroek 			if (val < 0 || val > 1)
972*ef8d499eSDavid van Moolenbroek 				return EINVAL;
973*ef8d499eSDavid van Moolenbroek 
974*ef8d499eSDavid van Moolenbroek 			flags = raw_flags(raw->raw_pcb);
975*ef8d499eSDavid van Moolenbroek 
976*ef8d499eSDavid van Moolenbroek 			if (val)
977*ef8d499eSDavid van Moolenbroek 				flags |= RAW_FLAGS_MULTICAST_LOOP;
978*ef8d499eSDavid van Moolenbroek 			else
979*ef8d499eSDavid van Moolenbroek 				flags &= ~RAW_FLAGS_MULTICAST_LOOP;
980*ef8d499eSDavid van Moolenbroek 
981*ef8d499eSDavid van Moolenbroek 			/*
982*ef8d499eSDavid van Moolenbroek 			 * lwIP's IPv6 functionality does not actually check
983*ef8d499eSDavid van Moolenbroek 			 * this flag at all yet.  We set it in the hope that
984*ef8d499eSDavid van Moolenbroek 			 * one day this will magically start working.
985*ef8d499eSDavid van Moolenbroek 			 */
986*ef8d499eSDavid van Moolenbroek 			raw_setflags(raw->raw_pcb, flags);
987*ef8d499eSDavid van Moolenbroek 
988*ef8d499eSDavid van Moolenbroek 			return OK;
989*ef8d499eSDavid van Moolenbroek 
990*ef8d499eSDavid van Moolenbroek 		case IPV6_MULTICAST_HOPS:
991*ef8d499eSDavid van Moolenbroek 			pktsock_set_mcaware(&raw->raw_pktsock);
992*ef8d499eSDavid van Moolenbroek 
993*ef8d499eSDavid van Moolenbroek 			if ((r = sockdriver_copyin_opt(data, &val, sizeof(val),
994*ef8d499eSDavid van Moolenbroek 			    len)) != OK)
995*ef8d499eSDavid van Moolenbroek 				return r;
996*ef8d499eSDavid van Moolenbroek 
997*ef8d499eSDavid van Moolenbroek 			if (val < -1 || val > UINT8_MAX)
998*ef8d499eSDavid van Moolenbroek 				return EINVAL;
999*ef8d499eSDavid van Moolenbroek 
1000*ef8d499eSDavid van Moolenbroek 			if (val == -1)
1001*ef8d499eSDavid van Moolenbroek 				val = 1;
1002*ef8d499eSDavid van Moolenbroek 
1003*ef8d499eSDavid van Moolenbroek 			raw_set_multicast_ttl(raw->raw_pcb, val);
1004*ef8d499eSDavid van Moolenbroek 
1005*ef8d499eSDavid van Moolenbroek 			return OK;
1006*ef8d499eSDavid van Moolenbroek 		}
1007*ef8d499eSDavid van Moolenbroek 
1008*ef8d499eSDavid van Moolenbroek 		break;
1009*ef8d499eSDavid van Moolenbroek 
1010*ef8d499eSDavid van Moolenbroek 	case IPPROTO_ICMPV6:
1011*ef8d499eSDavid van Moolenbroek 		if (!rawsock_is_ipv6(raw) ||
1012*ef8d499eSDavid van Moolenbroek 		    raw->raw_pcb->protocol != IPPROTO_ICMPV6)
1013*ef8d499eSDavid van Moolenbroek 			break;
1014*ef8d499eSDavid van Moolenbroek 
1015*ef8d499eSDavid van Moolenbroek 		switch (name) {
1016*ef8d499eSDavid van Moolenbroek 		case ICMP6_FILTER:
1017*ef8d499eSDavid van Moolenbroek 			/* Who comes up with these stupid exceptions? */
1018*ef8d499eSDavid van Moolenbroek 			if (len == 0) {
1019*ef8d499eSDavid van Moolenbroek 				ICMP6_FILTER_SETPASSALL(&raw->raw_icmp6filter);
1020*ef8d499eSDavid van Moolenbroek 
1021*ef8d499eSDavid van Moolenbroek 				return OK;
1022*ef8d499eSDavid van Moolenbroek 			}
1023*ef8d499eSDavid van Moolenbroek 
1024*ef8d499eSDavid van Moolenbroek 			if ((r = sockdriver_copyin_opt(data, &filter,
1025*ef8d499eSDavid van Moolenbroek 			    sizeof(filter), len)) != OK)
1026*ef8d499eSDavid van Moolenbroek 				return r;
1027*ef8d499eSDavid van Moolenbroek 
1028*ef8d499eSDavid van Moolenbroek 			/*
1029*ef8d499eSDavid van Moolenbroek 			 * As always, never copy in the data into the actual
1030*ef8d499eSDavid van Moolenbroek 			 * destination, as any copy may run into a copy fault
1031*ef8d499eSDavid van Moolenbroek 			 * halfway through, potentially leaving the destination
1032*ef8d499eSDavid van Moolenbroek 			 * in a half-updated and thus corrupted state.
1033*ef8d499eSDavid van Moolenbroek 			 */
1034*ef8d499eSDavid van Moolenbroek 			memcpy(&raw->raw_icmp6filter, &filter, sizeof(filter));
1035*ef8d499eSDavid van Moolenbroek 
1036*ef8d499eSDavid van Moolenbroek 			return OK;
1037*ef8d499eSDavid van Moolenbroek 		}
1038*ef8d499eSDavid van Moolenbroek 	}
1039*ef8d499eSDavid van Moolenbroek 
1040*ef8d499eSDavid van Moolenbroek 	rawsock_get_ipopts(raw, &ipopts);
1041*ef8d499eSDavid van Moolenbroek 
1042*ef8d499eSDavid van Moolenbroek 	return pktsock_setsockopt(&raw->raw_pktsock, level, name, data, len,
1043*ef8d499eSDavid van Moolenbroek 	    &ipopts);
1044*ef8d499eSDavid van Moolenbroek }
1045*ef8d499eSDavid van Moolenbroek 
1046*ef8d499eSDavid van Moolenbroek /*
1047*ef8d499eSDavid van Moolenbroek  * Retrieve socket options on a raw socket.
1048*ef8d499eSDavid van Moolenbroek  */
1049*ef8d499eSDavid van Moolenbroek static int
rawsock_getsockopt(struct sock * sock,int level,int name,const struct sockdriver_data * data,socklen_t * len)1050*ef8d499eSDavid van Moolenbroek rawsock_getsockopt(struct sock * sock, int level, int name,
1051*ef8d499eSDavid van Moolenbroek 	const struct sockdriver_data * data, socklen_t * len)
1052*ef8d499eSDavid van Moolenbroek {
1053*ef8d499eSDavid van Moolenbroek 	struct rawsock *raw = (struct rawsock *)sock;
1054*ef8d499eSDavid van Moolenbroek 	struct ipopts ipopts;
1055*ef8d499eSDavid van Moolenbroek 	const ip4_addr_t *ip4addr;
1056*ef8d499eSDavid van Moolenbroek 	struct in_addr in_addr;
1057*ef8d499eSDavid van Moolenbroek 	struct ifdev *ifdev;
1058*ef8d499eSDavid van Moolenbroek 	unsigned int flags;
1059*ef8d499eSDavid van Moolenbroek 	uint32_t ifindex;
1060*ef8d499eSDavid van Moolenbroek 	uint8_t byte;
1061*ef8d499eSDavid van Moolenbroek 	int val;
1062*ef8d499eSDavid van Moolenbroek 
1063*ef8d499eSDavid van Moolenbroek 	switch (level) {
1064*ef8d499eSDavid van Moolenbroek 	case IPPROTO_IP:
1065*ef8d499eSDavid van Moolenbroek 		if (rawsock_is_ipv6(raw))
1066*ef8d499eSDavid van Moolenbroek 			break;
1067*ef8d499eSDavid van Moolenbroek 
1068*ef8d499eSDavid van Moolenbroek 		switch (name) {
1069*ef8d499eSDavid van Moolenbroek 		case IP_HDRINCL:
1070*ef8d499eSDavid van Moolenbroek 			val = !!rawsock_is_hdrincl(raw);
1071*ef8d499eSDavid van Moolenbroek 
1072*ef8d499eSDavid van Moolenbroek 			return sockdriver_copyout_opt(data, &val, sizeof(val),
1073*ef8d499eSDavid van Moolenbroek 			    len);
1074*ef8d499eSDavid van Moolenbroek 
1075*ef8d499eSDavid van Moolenbroek 		case IP_MULTICAST_IF:
1076*ef8d499eSDavid van Moolenbroek 			ifindex = raw_get_multicast_netif_index(raw->raw_pcb);
1077*ef8d499eSDavid van Moolenbroek 
1078*ef8d499eSDavid van Moolenbroek 			/*
1079*ef8d499eSDavid van Moolenbroek 			 * Map back from the interface index to the IPv4
1080*ef8d499eSDavid van Moolenbroek 			 * address assigned to the corresponding interface.
1081*ef8d499eSDavid van Moolenbroek 			 * Should this not work out, return the 'any' address.
1082*ef8d499eSDavid van Moolenbroek 			 */
1083*ef8d499eSDavid van Moolenbroek 			if (ifindex != NETIF_NO_INDEX &&
1084*ef8d499eSDavid van Moolenbroek 			   (ifdev = ifdev_get_by_index(ifindex)) != NULL) {
1085*ef8d499eSDavid van Moolenbroek 				ip4addr =
1086*ef8d499eSDavid van Moolenbroek 				    netif_ip4_addr(ifdev_get_netif(ifdev));
1087*ef8d499eSDavid van Moolenbroek 
1088*ef8d499eSDavid van Moolenbroek 				in_addr.s_addr = ip4_addr_get_u32(ip4addr);
1089*ef8d499eSDavid van Moolenbroek 			} else
1090*ef8d499eSDavid van Moolenbroek 				in_addr.s_addr = PP_HTONL(INADDR_ANY);
1091*ef8d499eSDavid van Moolenbroek 
1092*ef8d499eSDavid van Moolenbroek 			return sockdriver_copyout_opt(data, &in_addr,
1093*ef8d499eSDavid van Moolenbroek 			    sizeof(in_addr), len);
1094*ef8d499eSDavid van Moolenbroek 
1095*ef8d499eSDavid van Moolenbroek 		case IP_MULTICAST_LOOP:
1096*ef8d499eSDavid van Moolenbroek 			flags = raw_flags(raw->raw_pcb);
1097*ef8d499eSDavid van Moolenbroek 
1098*ef8d499eSDavid van Moolenbroek 			byte = !!(flags & RAW_FLAGS_MULTICAST_LOOP);
1099*ef8d499eSDavid van Moolenbroek 
1100*ef8d499eSDavid van Moolenbroek 			return sockdriver_copyout_opt(data, &byte,
1101*ef8d499eSDavid van Moolenbroek 			    sizeof(byte), len);
1102*ef8d499eSDavid van Moolenbroek 
1103*ef8d499eSDavid van Moolenbroek 		case IP_MULTICAST_TTL:
1104*ef8d499eSDavid van Moolenbroek 			byte = raw_get_multicast_ttl(raw->raw_pcb);
1105*ef8d499eSDavid van Moolenbroek 
1106*ef8d499eSDavid van Moolenbroek 			return sockdriver_copyout_opt(data, &byte,
1107*ef8d499eSDavid van Moolenbroek 			    sizeof(byte), len);
1108*ef8d499eSDavid van Moolenbroek 		}
1109*ef8d499eSDavid van Moolenbroek 
1110*ef8d499eSDavid van Moolenbroek 		break;
1111*ef8d499eSDavid van Moolenbroek 
1112*ef8d499eSDavid van Moolenbroek 	case IPPROTO_IPV6:
1113*ef8d499eSDavid van Moolenbroek 		if (!rawsock_is_ipv6(raw))
1114*ef8d499eSDavid van Moolenbroek 			break;
1115*ef8d499eSDavid van Moolenbroek 
1116*ef8d499eSDavid van Moolenbroek 		switch (name) {
1117*ef8d499eSDavid van Moolenbroek 		case IPV6_CHECKSUM:
1118*ef8d499eSDavid van Moolenbroek 			if (raw->raw_pcb->chksum_reqd)
1119*ef8d499eSDavid van Moolenbroek 				val = raw->raw_pcb->chksum_offset;
1120*ef8d499eSDavid van Moolenbroek 			else
1121*ef8d499eSDavid van Moolenbroek 				val = -1;
1122*ef8d499eSDavid van Moolenbroek 
1123*ef8d499eSDavid van Moolenbroek 			return sockdriver_copyout_opt(data, &val, sizeof(val),
1124*ef8d499eSDavid van Moolenbroek 			    len);
1125*ef8d499eSDavid van Moolenbroek 
1126*ef8d499eSDavid van Moolenbroek 		case IPV6_MULTICAST_IF:
1127*ef8d499eSDavid van Moolenbroek 			ifindex = raw_get_multicast_netif_index(raw->raw_pcb);
1128*ef8d499eSDavid van Moolenbroek 
1129*ef8d499eSDavid van Moolenbroek 			val = (int)ifindex;
1130*ef8d499eSDavid van Moolenbroek 
1131*ef8d499eSDavid van Moolenbroek 			return sockdriver_copyout_opt(data, &val, sizeof(val),
1132*ef8d499eSDavid van Moolenbroek 			    len);
1133*ef8d499eSDavid van Moolenbroek 
1134*ef8d499eSDavid van Moolenbroek 		case IPV6_MULTICAST_LOOP:
1135*ef8d499eSDavid van Moolenbroek 			flags = raw_flags(raw->raw_pcb);
1136*ef8d499eSDavid van Moolenbroek 
1137*ef8d499eSDavid van Moolenbroek 			val = !!(flags & RAW_FLAGS_MULTICAST_LOOP);
1138*ef8d499eSDavid van Moolenbroek 
1139*ef8d499eSDavid van Moolenbroek 			return sockdriver_copyout_opt(data, &val, sizeof(val),
1140*ef8d499eSDavid van Moolenbroek 			    len);
1141*ef8d499eSDavid van Moolenbroek 
1142*ef8d499eSDavid van Moolenbroek 		case IPV6_MULTICAST_HOPS:
1143*ef8d499eSDavid van Moolenbroek 			val = raw_get_multicast_ttl(raw->raw_pcb);
1144*ef8d499eSDavid van Moolenbroek 
1145*ef8d499eSDavid van Moolenbroek 			return sockdriver_copyout_opt(data, &val, sizeof(val),
1146*ef8d499eSDavid van Moolenbroek 			    len);
1147*ef8d499eSDavid van Moolenbroek 		}
1148*ef8d499eSDavid van Moolenbroek 
1149*ef8d499eSDavid van Moolenbroek 		break;
1150*ef8d499eSDavid van Moolenbroek 
1151*ef8d499eSDavid van Moolenbroek 	case IPPROTO_ICMPV6:
1152*ef8d499eSDavid van Moolenbroek 		if (!rawsock_is_ipv6(raw) ||
1153*ef8d499eSDavid van Moolenbroek 		    raw->raw_pcb->protocol != IPPROTO_ICMPV6)
1154*ef8d499eSDavid van Moolenbroek 			break;
1155*ef8d499eSDavid van Moolenbroek 
1156*ef8d499eSDavid van Moolenbroek 		switch (name) {
1157*ef8d499eSDavid van Moolenbroek 		case ICMP6_FILTER:
1158*ef8d499eSDavid van Moolenbroek 			return sockdriver_copyout_opt(data,
1159*ef8d499eSDavid van Moolenbroek 			    &raw->raw_icmp6filter,
1160*ef8d499eSDavid van Moolenbroek 			    sizeof(raw->raw_icmp6filter), len);
1161*ef8d499eSDavid van Moolenbroek 		}
1162*ef8d499eSDavid van Moolenbroek 
1163*ef8d499eSDavid van Moolenbroek 		break;
1164*ef8d499eSDavid van Moolenbroek 	}
1165*ef8d499eSDavid van Moolenbroek 
1166*ef8d499eSDavid van Moolenbroek 	rawsock_get_ipopts(raw, &ipopts);
1167*ef8d499eSDavid van Moolenbroek 
1168*ef8d499eSDavid van Moolenbroek 	return pktsock_getsockopt(&raw->raw_pktsock, level, name, data, len,
1169*ef8d499eSDavid van Moolenbroek 	    &ipopts);
1170*ef8d499eSDavid van Moolenbroek }
1171*ef8d499eSDavid van Moolenbroek 
1172*ef8d499eSDavid van Moolenbroek /*
1173*ef8d499eSDavid van Moolenbroek  * Retrieve the local socket address of a raw socket.
1174*ef8d499eSDavid van Moolenbroek  */
1175*ef8d499eSDavid van Moolenbroek static int
rawsock_getsockname(struct sock * sock,struct sockaddr * addr,socklen_t * addr_len)1176*ef8d499eSDavid van Moolenbroek rawsock_getsockname(struct sock * sock, struct sockaddr * addr,
1177*ef8d499eSDavid van Moolenbroek 	socklen_t * addr_len)
1178*ef8d499eSDavid van Moolenbroek {
1179*ef8d499eSDavid van Moolenbroek 	struct rawsock *raw = (struct rawsock *)sock;
1180*ef8d499eSDavid van Moolenbroek 
1181*ef8d499eSDavid van Moolenbroek 	ipsock_put_addr(rawsock_get_ipsock(raw), addr, addr_len,
1182*ef8d499eSDavid van Moolenbroek 	    &raw->raw_pcb->local_ip, 0 /*port*/);
1183*ef8d499eSDavid van Moolenbroek 
1184*ef8d499eSDavid van Moolenbroek 	return OK;
1185*ef8d499eSDavid van Moolenbroek }
1186*ef8d499eSDavid van Moolenbroek 
1187*ef8d499eSDavid van Moolenbroek /*
1188*ef8d499eSDavid van Moolenbroek  * Retrieve the remote socket address of a raw socket.
1189*ef8d499eSDavid van Moolenbroek  */
1190*ef8d499eSDavid van Moolenbroek static int
rawsock_getpeername(struct sock * sock,struct sockaddr * addr,socklen_t * addr_len)1191*ef8d499eSDavid van Moolenbroek rawsock_getpeername(struct sock * sock, struct sockaddr * addr,
1192*ef8d499eSDavid van Moolenbroek 	socklen_t * addr_len)
1193*ef8d499eSDavid van Moolenbroek {
1194*ef8d499eSDavid van Moolenbroek 	struct rawsock *raw = (struct rawsock *)sock;
1195*ef8d499eSDavid van Moolenbroek 
1196*ef8d499eSDavid van Moolenbroek 	if (!rawsock_is_conn(raw))
1197*ef8d499eSDavid van Moolenbroek 		return ENOTCONN;
1198*ef8d499eSDavid van Moolenbroek 
1199*ef8d499eSDavid van Moolenbroek 	ipsock_put_addr(rawsock_get_ipsock(raw), addr, addr_len,
1200*ef8d499eSDavid van Moolenbroek 	    &raw->raw_pcb->remote_ip, 0 /*port*/);
1201*ef8d499eSDavid van Moolenbroek 
1202*ef8d499eSDavid van Moolenbroek 	return OK;
1203*ef8d499eSDavid van Moolenbroek }
1204*ef8d499eSDavid van Moolenbroek 
1205*ef8d499eSDavid van Moolenbroek /*
1206*ef8d499eSDavid van Moolenbroek  * Shut down a raw socket for reading and/or writing.
1207*ef8d499eSDavid van Moolenbroek  */
1208*ef8d499eSDavid van Moolenbroek static int
rawsock_shutdown(struct sock * sock,unsigned int mask)1209*ef8d499eSDavid van Moolenbroek rawsock_shutdown(struct sock * sock, unsigned int mask)
1210*ef8d499eSDavid van Moolenbroek {
1211*ef8d499eSDavid van Moolenbroek 	struct rawsock *raw = (struct rawsock *)sock;
1212*ef8d499eSDavid van Moolenbroek 
1213*ef8d499eSDavid van Moolenbroek 	if (mask & SFL_SHUT_RD)
1214*ef8d499eSDavid van Moolenbroek 		raw_recv(raw->raw_pcb, NULL, NULL);
1215*ef8d499eSDavid van Moolenbroek 
1216*ef8d499eSDavid van Moolenbroek 	pktsock_shutdown(&raw->raw_pktsock, mask);
1217*ef8d499eSDavid van Moolenbroek 
1218*ef8d499eSDavid van Moolenbroek 	return OK;
1219*ef8d499eSDavid van Moolenbroek }
1220*ef8d499eSDavid van Moolenbroek 
1221*ef8d499eSDavid van Moolenbroek /*
1222*ef8d499eSDavid van Moolenbroek  * Close a raw socket.
1223*ef8d499eSDavid van Moolenbroek  */
1224*ef8d499eSDavid van Moolenbroek static int
rawsock_close(struct sock * sock,int force __unused)1225*ef8d499eSDavid van Moolenbroek rawsock_close(struct sock * sock, int force __unused)
1226*ef8d499eSDavid van Moolenbroek {
1227*ef8d499eSDavid van Moolenbroek 	struct rawsock *raw = (struct rawsock *)sock;
1228*ef8d499eSDavid van Moolenbroek 
1229*ef8d499eSDavid van Moolenbroek 	raw_recv(raw->raw_pcb, NULL, NULL);
1230*ef8d499eSDavid van Moolenbroek 
1231*ef8d499eSDavid van Moolenbroek 	raw_remove(raw->raw_pcb);
1232*ef8d499eSDavid van Moolenbroek 	raw->raw_pcb = NULL;
1233*ef8d499eSDavid van Moolenbroek 
1234*ef8d499eSDavid van Moolenbroek 	pktsock_close(&raw->raw_pktsock);
1235*ef8d499eSDavid van Moolenbroek 
1236*ef8d499eSDavid van Moolenbroek 	return OK;
1237*ef8d499eSDavid van Moolenbroek }
1238*ef8d499eSDavid van Moolenbroek 
1239*ef8d499eSDavid van Moolenbroek /*
1240*ef8d499eSDavid van Moolenbroek  * Free up a closed raw socket.
1241*ef8d499eSDavid van Moolenbroek  */
1242*ef8d499eSDavid van Moolenbroek static void
rawsock_free(struct sock * sock)1243*ef8d499eSDavid van Moolenbroek rawsock_free(struct sock * sock)
1244*ef8d499eSDavid van Moolenbroek {
1245*ef8d499eSDavid van Moolenbroek 	struct rawsock *raw = (struct rawsock *)sock;
1246*ef8d499eSDavid van Moolenbroek 
1247*ef8d499eSDavid van Moolenbroek 	assert(raw->raw_pcb == NULL);
1248*ef8d499eSDavid van Moolenbroek 
1249*ef8d499eSDavid van Moolenbroek 	TAILQ_REMOVE(&raw_activelist, raw, raw_next);
1250*ef8d499eSDavid van Moolenbroek 
1251*ef8d499eSDavid van Moolenbroek 	TAILQ_INSERT_HEAD(&raw_freelist, raw, raw_next);
1252*ef8d499eSDavid van Moolenbroek }
1253*ef8d499eSDavid van Moolenbroek 
1254*ef8d499eSDavid van Moolenbroek /*
1255*ef8d499eSDavid van Moolenbroek  * Fill the given kinfo_pcb sysctl(7) structure with information about the RAW
1256*ef8d499eSDavid van Moolenbroek  * PCB identified by the given pointer.
1257*ef8d499eSDavid van Moolenbroek  */
1258*ef8d499eSDavid van Moolenbroek static void
rawsock_get_info(struct kinfo_pcb * ki,const void * ptr)1259*ef8d499eSDavid van Moolenbroek rawsock_get_info(struct kinfo_pcb * ki, const void * ptr)
1260*ef8d499eSDavid van Moolenbroek {
1261*ef8d499eSDavid van Moolenbroek 	const struct raw_pcb *pcb = (const struct raw_pcb *)ptr;
1262*ef8d499eSDavid van Moolenbroek 	struct rawsock *raw;
1263*ef8d499eSDavid van Moolenbroek 
1264*ef8d499eSDavid van Moolenbroek 	/* We iterate our own list so we can't find "strange" PCBs. */
1265*ef8d499eSDavid van Moolenbroek 	raw = (struct rawsock *)pcb->recv_arg;
1266*ef8d499eSDavid van Moolenbroek 	assert(raw >= raw_array &&
1267*ef8d499eSDavid van Moolenbroek 	    raw < &raw_array[__arraycount(raw_array)]);
1268*ef8d499eSDavid van Moolenbroek 
1269*ef8d499eSDavid van Moolenbroek 	ki->ki_type = SOCK_RAW;
1270*ef8d499eSDavid van Moolenbroek 	ki->ki_protocol = pcb->protocol;
1271*ef8d499eSDavid van Moolenbroek 
1272*ef8d499eSDavid van Moolenbroek 	ipsock_get_info(ki, &pcb->local_ip, 0 /*local_port*/,
1273*ef8d499eSDavid van Moolenbroek 	    &raw->raw_pcb->remote_ip, 0 /*remote_port*/);
1274*ef8d499eSDavid van Moolenbroek 
1275*ef8d499eSDavid van Moolenbroek 	/* TODO: change this so that sockstat(1) may work one day. */
1276*ef8d499eSDavid van Moolenbroek 	ki->ki_sockaddr = (uint64_t)(uintptr_t)rawsock_get_sock(raw);
1277*ef8d499eSDavid van Moolenbroek 
1278*ef8d499eSDavid van Moolenbroek 	ki->ki_rcvq = pktsock_get_recvlen(&raw->raw_pktsock);
1279*ef8d499eSDavid van Moolenbroek 
1280*ef8d499eSDavid van Moolenbroek 	if (rawsock_is_hdrincl(raw))
1281*ef8d499eSDavid van Moolenbroek 		ki->ki_pflags |= INP_HDRINCL;
1282*ef8d499eSDavid van Moolenbroek }
1283*ef8d499eSDavid van Moolenbroek 
1284*ef8d499eSDavid van Moolenbroek /*
1285*ef8d499eSDavid van Moolenbroek  * Given either NULL or a previously returned RAW PCB pointer, return the first
1286*ef8d499eSDavid van Moolenbroek  * or next RAW PCB pointer, or NULL if there are no more.  lwIP does not expose
1287*ef8d499eSDavid van Moolenbroek  * 'raw_pcbs', but other modules in this service may also use RAW PCBs (which
1288*ef8d499eSDavid van Moolenbroek  * should then stay hidden), so we iterate through our own list instead.
1289*ef8d499eSDavid van Moolenbroek  */
1290*ef8d499eSDavid van Moolenbroek static const void *
rawsock_enum(const void * last)1291*ef8d499eSDavid van Moolenbroek rawsock_enum(const void * last)
1292*ef8d499eSDavid van Moolenbroek {
1293*ef8d499eSDavid van Moolenbroek 	const struct raw_pcb *pcb;
1294*ef8d499eSDavid van Moolenbroek 	struct rawsock *raw;
1295*ef8d499eSDavid van Moolenbroek 
1296*ef8d499eSDavid van Moolenbroek 	if (last != NULL) {
1297*ef8d499eSDavid van Moolenbroek 		pcb = (const struct raw_pcb *)last;
1298*ef8d499eSDavid van Moolenbroek 
1299*ef8d499eSDavid van Moolenbroek 		raw = (struct rawsock *)pcb->recv_arg;
1300*ef8d499eSDavid van Moolenbroek 		assert(raw >= raw_array &&
1301*ef8d499eSDavid van Moolenbroek 		    raw < &raw_array[__arraycount(raw_array)]);
1302*ef8d499eSDavid van Moolenbroek 
1303*ef8d499eSDavid van Moolenbroek 		raw = TAILQ_NEXT(raw, raw_next);
1304*ef8d499eSDavid van Moolenbroek 	} else
1305*ef8d499eSDavid van Moolenbroek 		raw = TAILQ_FIRST(&raw_activelist);
1306*ef8d499eSDavid van Moolenbroek 
1307*ef8d499eSDavid van Moolenbroek 	if (raw != NULL)
1308*ef8d499eSDavid van Moolenbroek 		return raw->raw_pcb;
1309*ef8d499eSDavid van Moolenbroek 	else
1310*ef8d499eSDavid van Moolenbroek 		return NULL;
1311*ef8d499eSDavid van Moolenbroek }
1312*ef8d499eSDavid van Moolenbroek 
1313*ef8d499eSDavid van Moolenbroek /*
1314*ef8d499eSDavid van Moolenbroek  * Obtain the list of RAW protocol control blocks, for sysctl(7).
1315*ef8d499eSDavid van Moolenbroek  */
1316*ef8d499eSDavid van Moolenbroek static ssize_t
rawsock_pcblist(struct rmib_call * call,struct rmib_node * node,struct rmib_oldp * oldp,struct rmib_newp * newp __unused)1317*ef8d499eSDavid van Moolenbroek rawsock_pcblist(struct rmib_call * call, struct rmib_node * node,
1318*ef8d499eSDavid van Moolenbroek 	struct rmib_oldp * oldp, struct rmib_newp * newp __unused)
1319*ef8d499eSDavid van Moolenbroek {
1320*ef8d499eSDavid van Moolenbroek 
1321*ef8d499eSDavid van Moolenbroek 	return util_pcblist(call, oldp, rawsock_enum, rawsock_get_info);
1322*ef8d499eSDavid van Moolenbroek }
1323*ef8d499eSDavid van Moolenbroek 
1324*ef8d499eSDavid van Moolenbroek static const struct sockevent_ops rawsock_ops = {
1325*ef8d499eSDavid van Moolenbroek 	.sop_bind		= rawsock_bind,
1326*ef8d499eSDavid van Moolenbroek 	.sop_connect		= rawsock_connect,
1327*ef8d499eSDavid van Moolenbroek 	.sop_pre_send		= rawsock_pre_send,
1328*ef8d499eSDavid van Moolenbroek 	.sop_send		= rawsock_send,
1329*ef8d499eSDavid van Moolenbroek 	.sop_pre_recv		= pktsock_pre_recv,
1330*ef8d499eSDavid van Moolenbroek 	.sop_recv		= pktsock_recv,
1331*ef8d499eSDavid van Moolenbroek 	.sop_test_recv		= pktsock_test_recv,
1332*ef8d499eSDavid van Moolenbroek 	.sop_ioctl		= ifconf_ioctl,
1333*ef8d499eSDavid van Moolenbroek 	.sop_setsockmask	= rawsock_setsockmask,
1334*ef8d499eSDavid van Moolenbroek 	.sop_setsockopt		= rawsock_setsockopt,
1335*ef8d499eSDavid van Moolenbroek 	.sop_getsockopt		= rawsock_getsockopt,
1336*ef8d499eSDavid van Moolenbroek 	.sop_getsockname	= rawsock_getsockname,
1337*ef8d499eSDavid van Moolenbroek 	.sop_getpeername	= rawsock_getpeername,
1338*ef8d499eSDavid van Moolenbroek 	.sop_shutdown		= rawsock_shutdown,
1339*ef8d499eSDavid van Moolenbroek 	.sop_close		= rawsock_close,
1340*ef8d499eSDavid van Moolenbroek 	.sop_free		= rawsock_free
1341*ef8d499eSDavid van Moolenbroek };
1342