xref: /minix3/minix/net/lwip/udpsock.c (revision ef8d499e2d2af900e9b2ab297171d7b088652482)
1*ef8d499eSDavid van Moolenbroek /* LWIP service - udpsock.c - UDP sockets */
2*ef8d499eSDavid van Moolenbroek 
3*ef8d499eSDavid van Moolenbroek #include "lwip.h"
4*ef8d499eSDavid van Moolenbroek #include "ifaddr.h"
5*ef8d499eSDavid van Moolenbroek #include "pktsock.h"
6*ef8d499eSDavid van Moolenbroek 
7*ef8d499eSDavid van Moolenbroek #include "lwip/udp.h"
8*ef8d499eSDavid van Moolenbroek 
9*ef8d499eSDavid van Moolenbroek #include <netinet/udp.h>
10*ef8d499eSDavid van Moolenbroek #include <netinet/ip_var.h>
11*ef8d499eSDavid van Moolenbroek #include <netinet/udp_var.h>
12*ef8d499eSDavid van Moolenbroek 
13*ef8d499eSDavid van Moolenbroek /* The number of UDP sockets.  Inherited from the lwIP configuration. */
14*ef8d499eSDavid van Moolenbroek #define NR_UDPSOCK	MEMP_NUM_UDP_PCB
15*ef8d499eSDavid van Moolenbroek 
16*ef8d499eSDavid van Moolenbroek /*
17*ef8d499eSDavid van Moolenbroek  * Outgoing packets are not getting buffered, so the send buffer size simply
18*ef8d499eSDavid van Moolenbroek  * determines the maximum size for sent packets.  The send buffer maximum is
19*ef8d499eSDavid van Moolenbroek  * therefore limited to the maximum size of a single packet (64K-1 bytes),
20*ef8d499eSDavid van Moolenbroek  * which is already enforced by lwIP's 16-bit length parameter to pbuf_alloc().
21*ef8d499eSDavid van Moolenbroek  *
22*ef8d499eSDavid van Moolenbroek  * The actual transmission may enforce a lower limit, though.  The full packet
23*ef8d499eSDavid van Moolenbroek  * size must not exceed the same 64K-1 limit, and that includes any headers
24*ef8d499eSDavid van Moolenbroek  * that still have to be prepended to the given packet.  The size of those
25*ef8d499eSDavid van Moolenbroek  * headers depends on the socket type (IPv4/IPv6) and the IP_HDRINCL setting.
26*ef8d499eSDavid van Moolenbroek  */
27*ef8d499eSDavid van Moolenbroek #define UDP_MAX_PAYLOAD	(UINT16_MAX)
28*ef8d499eSDavid van Moolenbroek 
29*ef8d499eSDavid van Moolenbroek #define UDP_SNDBUF_MIN	1		/* minimum UDP send buffer size */
30*ef8d499eSDavid van Moolenbroek #define UDP_SNDBUF_DEF	8192		/* default UDP send buffer size */
31*ef8d499eSDavid van Moolenbroek #define UDP_SNDBUF_MAX	UDP_MAX_PAYLOAD	/* maximum UDP send buffer size */
32*ef8d499eSDavid van Moolenbroek #define UDP_RCVBUF_MIN	MEMPOOL_BUFSIZE	/* minimum UDP receive buffer size */
33*ef8d499eSDavid van Moolenbroek #define UDP_RCVBUF_DEF	32768		/* default UDP receive buffer size */
34*ef8d499eSDavid van Moolenbroek #define UDP_RCVBUF_MAX	65536		/* maximum UDP receive buffer size */
35*ef8d499eSDavid van Moolenbroek 
36*ef8d499eSDavid van Moolenbroek static struct udpsock {
37*ef8d499eSDavid van Moolenbroek 	struct pktsock udp_pktsock;		/* pkt socket, MUST be first */
38*ef8d499eSDavid van Moolenbroek 	struct udp_pcb *udp_pcb;		/* lwIP UDP control block */
39*ef8d499eSDavid van Moolenbroek 	SIMPLEQ_ENTRY(udpsock) udp_next;	/* next in free list */
40*ef8d499eSDavid van Moolenbroek } udp_array[NR_UDPSOCK];
41*ef8d499eSDavid van Moolenbroek 
42*ef8d499eSDavid van Moolenbroek static SIMPLEQ_HEAD(, udpsock) udp_freelist;	/* list of free UDP sockets */
43*ef8d499eSDavid van Moolenbroek 
44*ef8d499eSDavid van Moolenbroek static const struct sockevent_ops udpsock_ops;
45*ef8d499eSDavid van Moolenbroek 
46*ef8d499eSDavid van Moolenbroek #define udpsock_get_sock(udp)	(ipsock_get_sock(udpsock_get_ipsock(udp)))
47*ef8d499eSDavid van Moolenbroek #define udpsock_get_ipsock(udp)	(pktsock_get_ipsock(&(udp)->udp_pktsock))
48*ef8d499eSDavid van Moolenbroek #define udpsock_is_ipv6(udp)	(ipsock_is_ipv6(udpsock_get_ipsock(udp)))
49*ef8d499eSDavid van Moolenbroek #define udpsock_is_conn(udp)	\
50*ef8d499eSDavid van Moolenbroek 	(udp_flags((udp)->udp_pcb) & UDP_FLAGS_CONNECTED)
51*ef8d499eSDavid van Moolenbroek 
52*ef8d499eSDavid van Moolenbroek static ssize_t udpsock_pcblist(struct rmib_call *, struct rmib_node *,
53*ef8d499eSDavid van Moolenbroek 	struct rmib_oldp *, struct rmib_newp *);
54*ef8d499eSDavid van Moolenbroek 
55*ef8d499eSDavid van Moolenbroek /* The CTL_NET {PF_INET,PF_INET6} IPPROTO_UDP subtree. */
56*ef8d499eSDavid van Moolenbroek /* TODO: add many more and make some of them writable.. */
57*ef8d499eSDavid van Moolenbroek static struct rmib_node net_inet_udp_table[] = {
58*ef8d499eSDavid van Moolenbroek /* 1*/	[UDPCTL_CHECKSUM]	= RMIB_INT(RMIB_RO, 1, "checksum",
59*ef8d499eSDavid van Moolenbroek 				    "Compute UDP checksums"),
60*ef8d499eSDavid van Moolenbroek /* 2*/	[UDPCTL_SENDSPACE]	= RMIB_INT(RMIB_RO, UDP_SNDBUF_DEF,
61*ef8d499eSDavid van Moolenbroek 				    "sendspace",
62*ef8d499eSDavid van Moolenbroek 				    "Default UDP send buffer size"),
63*ef8d499eSDavid van Moolenbroek /* 3*/	[UDPCTL_RECVSPACE]	= RMIB_INT(RMIB_RO, UDP_RCVBUF_DEF,
64*ef8d499eSDavid van Moolenbroek 				    "recvspace",
65*ef8d499eSDavid van Moolenbroek 				    "Default UDP receive buffer size"),
66*ef8d499eSDavid van Moolenbroek /* 4*/	[UDPCTL_LOOPBACKCKSUM]	= RMIB_FUNC(RMIB_RW | CTLTYPE_INT, sizeof(int),
67*ef8d499eSDavid van Moolenbroek 				    loopif_cksum, "do_loopback_cksum",
68*ef8d499eSDavid van Moolenbroek 				    "Perform UDP checksum on loopback"),
69*ef8d499eSDavid van Moolenbroek /*+0*/	[UDPCTL_MAXID]		= RMIB_FUNC(RMIB_RO | CTLTYPE_NODE, 0,
70*ef8d499eSDavid van Moolenbroek 				    udpsock_pcblist, "pcblist",
71*ef8d499eSDavid van Moolenbroek 				    "UDP protocol control block list"),
72*ef8d499eSDavid van Moolenbroek };
73*ef8d499eSDavid van Moolenbroek 
74*ef8d499eSDavid van Moolenbroek static struct rmib_node net_inet_udp_node =
75*ef8d499eSDavid van Moolenbroek     RMIB_NODE(RMIB_RO, net_inet_udp_table, "udp", "UDPv4 related settings");
76*ef8d499eSDavid van Moolenbroek static struct rmib_node net_inet6_udp6_node =
77*ef8d499eSDavid van Moolenbroek     RMIB_NODE(RMIB_RO, net_inet_udp_table, "udp6", "UDPv6 related settings");
78*ef8d499eSDavid van Moolenbroek 
79*ef8d499eSDavid van Moolenbroek /*
80*ef8d499eSDavid van Moolenbroek  * Initialize the UDP sockets module.
81*ef8d499eSDavid van Moolenbroek  */
82*ef8d499eSDavid van Moolenbroek void
udpsock_init(void)83*ef8d499eSDavid van Moolenbroek udpsock_init(void)
84*ef8d499eSDavid van Moolenbroek {
85*ef8d499eSDavid van Moolenbroek 	unsigned int slot;
86*ef8d499eSDavid van Moolenbroek 
87*ef8d499eSDavid van Moolenbroek 	/* Initialize the list of free UDP sockets. */
88*ef8d499eSDavid van Moolenbroek 	SIMPLEQ_INIT(&udp_freelist);
89*ef8d499eSDavid van Moolenbroek 
90*ef8d499eSDavid van Moolenbroek 	for (slot = 0; slot < __arraycount(udp_array); slot++)
91*ef8d499eSDavid van Moolenbroek 		SIMPLEQ_INSERT_TAIL(&udp_freelist, &udp_array[slot], udp_next);
92*ef8d499eSDavid van Moolenbroek 
93*ef8d499eSDavid van Moolenbroek 	/* Register the net.inet.udp and net.inet6.udp6 RMIB subtrees. */
94*ef8d499eSDavid van Moolenbroek 	mibtree_register_inet(PF_INET, IPPROTO_UDP, &net_inet_udp_node);
95*ef8d499eSDavid van Moolenbroek 	mibtree_register_inet(PF_INET6, IPPROTO_UDP, &net_inet6_udp6_node);
96*ef8d499eSDavid van Moolenbroek }
97*ef8d499eSDavid van Moolenbroek 
98*ef8d499eSDavid van Moolenbroek /*
99*ef8d499eSDavid van Moolenbroek  * A packet has arrived on a UDP socket.  We own the given packet buffer, and
100*ef8d499eSDavid van Moolenbroek  * so we must free it if we do not want to keep it.
101*ef8d499eSDavid van Moolenbroek  */
102*ef8d499eSDavid van Moolenbroek static void
udpsock_input(void * arg,struct udp_pcb * pcb __unused,struct pbuf * pbuf,const ip_addr_t * ipaddr,uint16_t port)103*ef8d499eSDavid van Moolenbroek udpsock_input(void * arg, struct udp_pcb * pcb __unused, struct pbuf * pbuf,
104*ef8d499eSDavid van Moolenbroek 	const ip_addr_t * ipaddr, uint16_t port)
105*ef8d499eSDavid van Moolenbroek {
106*ef8d499eSDavid van Moolenbroek 	struct udpsock *udp = (struct udpsock *)arg;
107*ef8d499eSDavid van Moolenbroek 
108*ef8d499eSDavid van Moolenbroek 	/* All UDP input processing is handled by pktsock. */
109*ef8d499eSDavid van Moolenbroek 	pktsock_input(&udp->udp_pktsock, pbuf, ipaddr, port);
110*ef8d499eSDavid van Moolenbroek }
111*ef8d499eSDavid van Moolenbroek 
112*ef8d499eSDavid van Moolenbroek /*
113*ef8d499eSDavid van Moolenbroek  * Create a UDP socket.
114*ef8d499eSDavid van Moolenbroek  */
115*ef8d499eSDavid van Moolenbroek sockid_t
udpsock_socket(int domain,int protocol,struct sock ** sockp,const struct sockevent_ops ** ops)116*ef8d499eSDavid van Moolenbroek udpsock_socket(int domain, int protocol, struct sock ** sockp,
117*ef8d499eSDavid van Moolenbroek 	const struct sockevent_ops ** ops)
118*ef8d499eSDavid van Moolenbroek {
119*ef8d499eSDavid van Moolenbroek 	struct udpsock *udp;
120*ef8d499eSDavid van Moolenbroek 	unsigned int flags;
121*ef8d499eSDavid van Moolenbroek 	uint8_t ip_type;
122*ef8d499eSDavid van Moolenbroek 
123*ef8d499eSDavid van Moolenbroek 	switch (protocol) {
124*ef8d499eSDavid van Moolenbroek 	case 0:
125*ef8d499eSDavid van Moolenbroek 	case IPPROTO_UDP:
126*ef8d499eSDavid van Moolenbroek 		break;
127*ef8d499eSDavid van Moolenbroek 
128*ef8d499eSDavid van Moolenbroek 	/* NetBSD does not support IPPROTO_UDPLITE, even though lwIP does. */
129*ef8d499eSDavid van Moolenbroek 	default:
130*ef8d499eSDavid van Moolenbroek 		return EPROTONOSUPPORT;
131*ef8d499eSDavid van Moolenbroek 	}
132*ef8d499eSDavid van Moolenbroek 
133*ef8d499eSDavid van Moolenbroek 	if (SIMPLEQ_EMPTY(&udp_freelist))
134*ef8d499eSDavid van Moolenbroek 		return ENOBUFS;
135*ef8d499eSDavid van Moolenbroek 
136*ef8d499eSDavid van Moolenbroek 	udp = SIMPLEQ_FIRST(&udp_freelist);
137*ef8d499eSDavid van Moolenbroek 
138*ef8d499eSDavid van Moolenbroek 	ip_type = pktsock_socket(&udp->udp_pktsock, domain, UDP_SNDBUF_DEF,
139*ef8d499eSDavid van Moolenbroek 	    UDP_RCVBUF_DEF, sockp);
140*ef8d499eSDavid van Moolenbroek 
141*ef8d499eSDavid van Moolenbroek 	/* We should have enough PCBs so this call should not fail.. */
142*ef8d499eSDavid van Moolenbroek 	if ((udp->udp_pcb = udp_new_ip_type(ip_type)) == NULL)
143*ef8d499eSDavid van Moolenbroek 		return ENOBUFS;
144*ef8d499eSDavid van Moolenbroek 	udp_recv(udp->udp_pcb, udpsock_input, (void *)udp);
145*ef8d499eSDavid van Moolenbroek 
146*ef8d499eSDavid van Moolenbroek 	/* By default, the multicast TTL is 1 and looping is enabled. */
147*ef8d499eSDavid van Moolenbroek 	udp_set_multicast_ttl(udp->udp_pcb, 1);
148*ef8d499eSDavid van Moolenbroek 
149*ef8d499eSDavid van Moolenbroek 	flags = udp_flags(udp->udp_pcb);
150*ef8d499eSDavid van Moolenbroek 	udp_setflags(udp->udp_pcb, flags | UDP_FLAGS_MULTICAST_LOOP);
151*ef8d499eSDavid van Moolenbroek 
152*ef8d499eSDavid van Moolenbroek 	SIMPLEQ_REMOVE_HEAD(&udp_freelist, udp_next);
153*ef8d499eSDavid van Moolenbroek 
154*ef8d499eSDavid van Moolenbroek 	*ops = &udpsock_ops;
155*ef8d499eSDavid van Moolenbroek 	return SOCKID_UDP | (sockid_t)(udp - udp_array);
156*ef8d499eSDavid van Moolenbroek }
157*ef8d499eSDavid van Moolenbroek 
158*ef8d499eSDavid van Moolenbroek /*
159*ef8d499eSDavid van Moolenbroek  * Bind a UDP socket to a local address.
160*ef8d499eSDavid van Moolenbroek  */
161*ef8d499eSDavid van Moolenbroek static int
udpsock_bind(struct sock * sock,const struct sockaddr * addr,socklen_t addr_len,endpoint_t user_endpt)162*ef8d499eSDavid van Moolenbroek udpsock_bind(struct sock * sock, const struct sockaddr * addr,
163*ef8d499eSDavid van Moolenbroek 	socklen_t addr_len, endpoint_t user_endpt)
164*ef8d499eSDavid van Moolenbroek {
165*ef8d499eSDavid van Moolenbroek 	struct udpsock *udp = (struct udpsock *)sock;
166*ef8d499eSDavid van Moolenbroek 	ip_addr_t ipaddr;
167*ef8d499eSDavid van Moolenbroek 	uint16_t port;
168*ef8d499eSDavid van Moolenbroek 	err_t err;
169*ef8d499eSDavid van Moolenbroek 	int r;
170*ef8d499eSDavid van Moolenbroek 
171*ef8d499eSDavid van Moolenbroek 	if ((r = ipsock_get_src_addr(udpsock_get_ipsock(udp), addr, addr_len,
172*ef8d499eSDavid van Moolenbroek 	    user_endpt, &udp->udp_pcb->local_ip, udp->udp_pcb->local_port,
173*ef8d499eSDavid van Moolenbroek 	    TRUE /*allow_mcast*/, &ipaddr, &port)) != OK)
174*ef8d499eSDavid van Moolenbroek 		return r;
175*ef8d499eSDavid van Moolenbroek 
176*ef8d499eSDavid van Moolenbroek 	err = udp_bind(udp->udp_pcb, &ipaddr, port);
177*ef8d499eSDavid van Moolenbroek 
178*ef8d499eSDavid van Moolenbroek 	return util_convert_err(err);
179*ef8d499eSDavid van Moolenbroek }
180*ef8d499eSDavid van Moolenbroek 
181*ef8d499eSDavid van Moolenbroek /*
182*ef8d499eSDavid van Moolenbroek  * Connect a UDP socket to a remote address.
183*ef8d499eSDavid van Moolenbroek  */
184*ef8d499eSDavid van Moolenbroek static int
udpsock_connect(struct sock * sock,const struct sockaddr * addr,socklen_t addr_len,endpoint_t user_endpt __unused)185*ef8d499eSDavid van Moolenbroek udpsock_connect(struct sock * sock, const struct sockaddr * addr,
186*ef8d499eSDavid van Moolenbroek 	socklen_t addr_len, endpoint_t user_endpt __unused)
187*ef8d499eSDavid van Moolenbroek {
188*ef8d499eSDavid van Moolenbroek 	struct udpsock *udp = (struct udpsock *)sock;
189*ef8d499eSDavid van Moolenbroek 	struct ifdev *ifdev;
190*ef8d499eSDavid van Moolenbroek 	const ip_addr_t *src_addr;
191*ef8d499eSDavid van Moolenbroek 	ip_addr_t dst_addr;
192*ef8d499eSDavid van Moolenbroek 	uint16_t dst_port;
193*ef8d499eSDavid van Moolenbroek 	uint32_t ifindex, ifindex2;
194*ef8d499eSDavid van Moolenbroek 	err_t err;
195*ef8d499eSDavid van Moolenbroek 	int r;
196*ef8d499eSDavid van Moolenbroek 
197*ef8d499eSDavid van Moolenbroek 	/*
198*ef8d499eSDavid van Moolenbroek 	 * One may "unconnect" socket by providing an address with family
199*ef8d499eSDavid van Moolenbroek 	 * AF_UNSPEC.  Providing an <any>:0 address does not achieve the same.
200*ef8d499eSDavid van Moolenbroek 	 */
201*ef8d499eSDavid van Moolenbroek 	if (addr_is_unspec(addr, addr_len)) {
202*ef8d499eSDavid van Moolenbroek 		udp_disconnect(udp->udp_pcb);
203*ef8d499eSDavid van Moolenbroek 
204*ef8d499eSDavid van Moolenbroek 		return OK;
205*ef8d499eSDavid van Moolenbroek 	}
206*ef8d499eSDavid van Moolenbroek 
207*ef8d499eSDavid van Moolenbroek 	if ((r = ipsock_get_dst_addr(udpsock_get_ipsock(udp), addr,
208*ef8d499eSDavid van Moolenbroek 	    addr_len, &udp->udp_pcb->local_ip, &dst_addr, &dst_port)) != OK)
209*ef8d499eSDavid van Moolenbroek 		return r;
210*ef8d499eSDavid van Moolenbroek 
211*ef8d499eSDavid van Moolenbroek 	/*
212*ef8d499eSDavid van Moolenbroek 	 * Bind explicitly to a source address if the PCB is not bound to one
213*ef8d499eSDavid van Moolenbroek 	 * yet.  This is expected in the BSD socket API, but lwIP does not do
214*ef8d499eSDavid van Moolenbroek 	 * it for us.
215*ef8d499eSDavid van Moolenbroek 	 */
216*ef8d499eSDavid van Moolenbroek 	if (ip_addr_isany(&udp->udp_pcb->local_ip)) {
217*ef8d499eSDavid van Moolenbroek 		/* Help the multicast case a bit, if possible. */
218*ef8d499eSDavid van Moolenbroek 		ifdev = NULL;
219*ef8d499eSDavid van Moolenbroek 
220*ef8d499eSDavid van Moolenbroek 		if (ip_addr_ismulticast(&dst_addr)) {
221*ef8d499eSDavid van Moolenbroek 			ifindex = pktsock_get_ifindex(&udp->udp_pktsock);
222*ef8d499eSDavid van Moolenbroek 			ifindex2 = udp_get_multicast_netif_index(udp->udp_pcb);
223*ef8d499eSDavid van Moolenbroek 			if (ifindex == 0)
224*ef8d499eSDavid van Moolenbroek 				ifindex = ifindex2;
225*ef8d499eSDavid van Moolenbroek 
226*ef8d499eSDavid van Moolenbroek 			if (ifindex != 0) {
227*ef8d499eSDavid van Moolenbroek 				ifdev = ifdev_get_by_index(ifindex);
228*ef8d499eSDavid van Moolenbroek 
229*ef8d499eSDavid van Moolenbroek 				if (ifdev == NULL)
230*ef8d499eSDavid van Moolenbroek 					return ENXIO;
231*ef8d499eSDavid van Moolenbroek 			}
232*ef8d499eSDavid van Moolenbroek 		}
233*ef8d499eSDavid van Moolenbroek 
234*ef8d499eSDavid van Moolenbroek 		src_addr = ifaddr_select(&dst_addr, ifdev, NULL /*ifdevp*/);
235*ef8d499eSDavid van Moolenbroek 
236*ef8d499eSDavid van Moolenbroek 		if (src_addr == NULL)
237*ef8d499eSDavid van Moolenbroek 			return EHOSTUNREACH;
238*ef8d499eSDavid van Moolenbroek 
239*ef8d499eSDavid van Moolenbroek 		err = udp_bind(udp->udp_pcb, src_addr,
240*ef8d499eSDavid van Moolenbroek 		    udp->udp_pcb->local_port);
241*ef8d499eSDavid van Moolenbroek 
242*ef8d499eSDavid van Moolenbroek 		if (err != ERR_OK)
243*ef8d499eSDavid van Moolenbroek 			return util_convert_err(err);
244*ef8d499eSDavid van Moolenbroek 	}
245*ef8d499eSDavid van Moolenbroek 
246*ef8d499eSDavid van Moolenbroek 	/*
247*ef8d499eSDavid van Moolenbroek 	 * Connecting a UDP socket serves two main purposes: 1) the socket uses
248*ef8d499eSDavid van Moolenbroek 	 * the address as destination when sending, and 2) the socket receives
249*ef8d499eSDavid van Moolenbroek 	 * packets from only the connected address.
250*ef8d499eSDavid van Moolenbroek 	 */
251*ef8d499eSDavid van Moolenbroek 	err = udp_connect(udp->udp_pcb, &dst_addr, dst_port);
252*ef8d499eSDavid van Moolenbroek 
253*ef8d499eSDavid van Moolenbroek 	if (err != ERR_OK)
254*ef8d499eSDavid van Moolenbroek 		return util_convert_err(err);
255*ef8d499eSDavid van Moolenbroek 
256*ef8d499eSDavid van Moolenbroek 	return OK;
257*ef8d499eSDavid van Moolenbroek }
258*ef8d499eSDavid van Moolenbroek 
259*ef8d499eSDavid van Moolenbroek /*
260*ef8d499eSDavid van Moolenbroek  * Perform preliminary checks on a send request.
261*ef8d499eSDavid van Moolenbroek  */
262*ef8d499eSDavid van Moolenbroek static int
udpsock_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)263*ef8d499eSDavid van Moolenbroek udpsock_pre_send(struct sock * sock, size_t len, socklen_t ctl_len __unused,
264*ef8d499eSDavid van Moolenbroek 	const struct sockaddr * addr, socklen_t addr_len __unused,
265*ef8d499eSDavid van Moolenbroek 	endpoint_t user_endpt __unused, int flags)
266*ef8d499eSDavid van Moolenbroek {
267*ef8d499eSDavid van Moolenbroek 	struct udpsock *udp = (struct udpsock *)sock;
268*ef8d499eSDavid van Moolenbroek 
269*ef8d499eSDavid van Moolenbroek 	if ((flags & ~MSG_DONTROUTE) != 0)
270*ef8d499eSDavid van Moolenbroek 		return EOPNOTSUPP;
271*ef8d499eSDavid van Moolenbroek 
272*ef8d499eSDavid van Moolenbroek 	if (!udpsock_is_conn(udp) && addr == NULL)
273*ef8d499eSDavid van Moolenbroek 		return EDESTADDRREQ;
274*ef8d499eSDavid van Moolenbroek 
275*ef8d499eSDavid van Moolenbroek 	/*
276*ef8d499eSDavid van Moolenbroek 	 * This is only one part of the length check.  The rest is done from
277*ef8d499eSDavid van Moolenbroek 	 * udpsock_send(), once we have more information.
278*ef8d499eSDavid van Moolenbroek 	 */
279*ef8d499eSDavid van Moolenbroek 	if (len > ipsock_get_sndbuf(udpsock_get_ipsock(udp)))
280*ef8d499eSDavid van Moolenbroek 		return EMSGSIZE;
281*ef8d499eSDavid van Moolenbroek 
282*ef8d499eSDavid van Moolenbroek 	return OK;
283*ef8d499eSDavid van Moolenbroek }
284*ef8d499eSDavid van Moolenbroek 
285*ef8d499eSDavid van Moolenbroek /*
286*ef8d499eSDavid van Moolenbroek  * Swap IP-level options between the UDP PCB and the packet options structure,
287*ef8d499eSDavid van Moolenbroek  * for all options that have their flag set in the packet options structure.
288*ef8d499eSDavid van Moolenbroek  * This function is called twice when sending a packet.  The result is that the
289*ef8d499eSDavid van Moolenbroek  * flagged options are overridden for only the packet being sent.
290*ef8d499eSDavid van Moolenbroek  */
291*ef8d499eSDavid van Moolenbroek static void
udpsock_swap_opt(struct udpsock * udp,struct pktopt * pkto)292*ef8d499eSDavid van Moolenbroek udpsock_swap_opt(struct udpsock * udp, struct pktopt * pkto)
293*ef8d499eSDavid van Moolenbroek {
294*ef8d499eSDavid van Moolenbroek 	uint8_t tos, ttl, mcast_ttl;
295*ef8d499eSDavid van Moolenbroek 
296*ef8d499eSDavid van Moolenbroek 	if (pkto->pkto_flags & PKTOF_TOS) {
297*ef8d499eSDavid van Moolenbroek 		tos = udp->udp_pcb->tos;
298*ef8d499eSDavid van Moolenbroek 		udp->udp_pcb->tos = pkto->pkto_tos;
299*ef8d499eSDavid van Moolenbroek 		pkto->pkto_tos = tos;
300*ef8d499eSDavid van Moolenbroek 	}
301*ef8d499eSDavid van Moolenbroek 
302*ef8d499eSDavid van Moolenbroek 	if (pkto->pkto_flags & PKTOF_TTL) {
303*ef8d499eSDavid van Moolenbroek 		ttl = udp->udp_pcb->ttl;
304*ef8d499eSDavid van Moolenbroek 		mcast_ttl = udp_get_multicast_ttl(udp->udp_pcb);
305*ef8d499eSDavid van Moolenbroek 		udp->udp_pcb->ttl = pkto->pkto_ttl;
306*ef8d499eSDavid van Moolenbroek 		udp_set_multicast_ttl(udp->udp_pcb, pkto->pkto_mcast_ttl);
307*ef8d499eSDavid van Moolenbroek 		pkto->pkto_ttl = ttl;
308*ef8d499eSDavid van Moolenbroek 		pkto->pkto_mcast_ttl = mcast_ttl;
309*ef8d499eSDavid van Moolenbroek 	}
310*ef8d499eSDavid van Moolenbroek }
311*ef8d499eSDavid van Moolenbroek 
312*ef8d499eSDavid van Moolenbroek /*
313*ef8d499eSDavid van Moolenbroek  * Send a packet on a UDP socket.
314*ef8d499eSDavid van Moolenbroek  */
315*ef8d499eSDavid van Moolenbroek static int
udpsock_send(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 __unused,const struct sockaddr * addr,socklen_t addr_len,endpoint_t user_endpt __unused,int flags,size_t min __unused)316*ef8d499eSDavid van Moolenbroek udpsock_send(struct sock * sock, const struct sockdriver_data * data,
317*ef8d499eSDavid van Moolenbroek 	size_t len, size_t * off, const struct sockdriver_data * ctl,
318*ef8d499eSDavid van Moolenbroek 	socklen_t ctl_len, socklen_t * ctl_off __unused,
319*ef8d499eSDavid van Moolenbroek 	const struct sockaddr * addr, socklen_t addr_len,
320*ef8d499eSDavid van Moolenbroek 	endpoint_t user_endpt __unused, int flags, size_t min __unused)
321*ef8d499eSDavid van Moolenbroek {
322*ef8d499eSDavid van Moolenbroek 	struct udpsock *udp = (struct udpsock *)sock;
323*ef8d499eSDavid van Moolenbroek 	struct pktopt pktopt;
324*ef8d499eSDavid van Moolenbroek 	struct pbuf *pbuf;
325*ef8d499eSDavid van Moolenbroek 	struct ifdev *ifdev;
326*ef8d499eSDavid van Moolenbroek 	struct netif *netif;
327*ef8d499eSDavid van Moolenbroek 	const ip_addr_t *src_addrp, *dst_addrp;
328*ef8d499eSDavid van Moolenbroek 	ip_addr_t src_addr, dst_addr; /* for storage only; not always used! */
329*ef8d499eSDavid van Moolenbroek 	uint16_t dst_port;
330*ef8d499eSDavid van Moolenbroek 	uint32_t ifindex;
331*ef8d499eSDavid van Moolenbroek 	size_t hdrlen;
332*ef8d499eSDavid van Moolenbroek 	err_t err;
333*ef8d499eSDavid van Moolenbroek 	int r;
334*ef8d499eSDavid van Moolenbroek 
335*ef8d499eSDavid van Moolenbroek 	/* Copy in and parse any packet options. */
336*ef8d499eSDavid van Moolenbroek 	pktopt.pkto_flags = 0;
337*ef8d499eSDavid van Moolenbroek 
338*ef8d499eSDavid van Moolenbroek 	if ((r = pktsock_get_ctl(&udp->udp_pktsock, ctl, ctl_len,
339*ef8d499eSDavid van Moolenbroek 	    &pktopt)) != OK)
340*ef8d499eSDavid van Moolenbroek 		return r;
341*ef8d499eSDavid van Moolenbroek 
342*ef8d499eSDavid van Moolenbroek 	/*
343*ef8d499eSDavid van Moolenbroek 	 * The code below will both determine an outgoing interface and a
344*ef8d499eSDavid van Moolenbroek 	 * source address for the packet.  Even though lwIP could do this for
345*ef8d499eSDavid van Moolenbroek 	 * us in some cases, there are other cases where we must do so
346*ef8d499eSDavid van Moolenbroek 	 * ourselves, with as main reasons 1) the possibility that either or
347*ef8d499eSDavid van Moolenbroek 	 * both have been provided through IPV6_PKTINFO, and 2) our intent to
348*ef8d499eSDavid van Moolenbroek 	 * detect and stop zone violations for (combinations of) scoped IPv6
349*ef8d499eSDavid van Moolenbroek 	 * addresses.  As a result, it is easier to simply take over the
350*ef8d499eSDavid van Moolenbroek 	 * selection tasks lwIP in their entirety.
351*ef8d499eSDavid van Moolenbroek 	 *
352*ef8d499eSDavid van Moolenbroek 	 * Much of the same applies to rawsock_send() as well.  Functional
353*ef8d499eSDavid van Moolenbroek 	 * differences (e.g. IP_HDRINCL support) as well as the PCB accesses in
354*ef8d499eSDavid van Moolenbroek 	 * the code make it hard to merge the two into a single pktsock copy.
355*ef8d499eSDavid van Moolenbroek 	 * Please do keep the two in sync as much as possible.
356*ef8d499eSDavid van Moolenbroek 	 */
357*ef8d499eSDavid van Moolenbroek 
358*ef8d499eSDavid van Moolenbroek 	/*
359*ef8d499eSDavid van Moolenbroek 	 * Start by checking whether the source address and/or the outgoing
360*ef8d499eSDavid van Moolenbroek 	 * interface are overridden using sticky and/or ancillary options.  The
361*ef8d499eSDavid van Moolenbroek 	 * call to pktsock_get_pktinfo(), if successful, will either set
362*ef8d499eSDavid van Moolenbroek 	 * 'ifdev' to NULL, in which case there is no override, or it will set
363*ef8d499eSDavid van Moolenbroek 	 * 'ifdev' to the outgoing interface to use, and (only) in that case
364*ef8d499eSDavid van Moolenbroek 	 * also fill 'src_addr', with an address that may either be a locally
365*ef8d499eSDavid van Moolenbroek 	 * owned unicast address or the unspecified ('any') address.  If it is
366*ef8d499eSDavid van Moolenbroek 	 * a unicast address, that is the source address to use for the packet.
367*ef8d499eSDavid van Moolenbroek 	 * Otherwise, fall back to the address to which the socket is bound,
368*ef8d499eSDavid van Moolenbroek 	 * which may also be the unspecified address or even a multicast
369*ef8d499eSDavid van Moolenbroek 	 * address.  In those case we will pick a source address further below.
370*ef8d499eSDavid van Moolenbroek 	 */
371*ef8d499eSDavid van Moolenbroek 	if ((r = pktsock_get_pktinfo(&udp->udp_pktsock, &pktopt, &ifdev,
372*ef8d499eSDavid van Moolenbroek 	    &src_addr)) != OK)
373*ef8d499eSDavid van Moolenbroek 		return r;
374*ef8d499eSDavid van Moolenbroek 
375*ef8d499eSDavid van Moolenbroek 	if (ifdev != NULL && !ip_addr_isany(&src_addr)) {
376*ef8d499eSDavid van Moolenbroek 		/* This is guaranteed to be a proper local unicast address. */
377*ef8d499eSDavid van Moolenbroek 		src_addrp = &src_addr;
378*ef8d499eSDavid van Moolenbroek 	} else {
379*ef8d499eSDavid van Moolenbroek 		src_addrp = &udp->udp_pcb->local_ip;
380*ef8d499eSDavid van Moolenbroek 
381*ef8d499eSDavid van Moolenbroek 		/*
382*ef8d499eSDavid van Moolenbroek 		 * If the socket is bound to a multicast address, use the
383*ef8d499eSDavid van Moolenbroek 		 * unspecified ('any') address as source address instead, until
384*ef8d499eSDavid van Moolenbroek 		 * we select a real source address (further below).  This
385*ef8d499eSDavid van Moolenbroek 		 * substitution keeps the rest of the code a bit simpler.
386*ef8d499eSDavid van Moolenbroek 		 */
387*ef8d499eSDavid van Moolenbroek 		if (ip_addr_ismulticast(src_addrp))
388*ef8d499eSDavid van Moolenbroek 			src_addrp = IP46_ADDR_ANY(IP_GET_TYPE(src_addrp));
389*ef8d499eSDavid van Moolenbroek 	}
390*ef8d499eSDavid van Moolenbroek 
391*ef8d499eSDavid van Moolenbroek 	/*
392*ef8d499eSDavid van Moolenbroek 	 * Determine the destination address to use.  If the socket is
393*ef8d499eSDavid van Moolenbroek 	 * connected, always ignore any address provided in the send call.
394*ef8d499eSDavid van Moolenbroek 	 */
395*ef8d499eSDavid van Moolenbroek 	if (!udpsock_is_conn(udp)) {
396*ef8d499eSDavid van Moolenbroek 		assert(addr != NULL); /* already checked in pre_send */
397*ef8d499eSDavid van Moolenbroek 
398*ef8d499eSDavid van Moolenbroek 		if ((r = ipsock_get_dst_addr(udpsock_get_ipsock(udp), addr,
399*ef8d499eSDavid van Moolenbroek 		    addr_len, src_addrp, &dst_addr, &dst_port)) != OK)
400*ef8d499eSDavid van Moolenbroek 			return r;
401*ef8d499eSDavid van Moolenbroek 
402*ef8d499eSDavid van Moolenbroek 		dst_addrp = &dst_addr;
403*ef8d499eSDavid van Moolenbroek 	} else {
404*ef8d499eSDavid van Moolenbroek 		dst_addrp = &udp->udp_pcb->remote_ip;
405*ef8d499eSDavid van Moolenbroek 		dst_port = udp->udp_pcb->remote_port;
406*ef8d499eSDavid van Moolenbroek 	}
407*ef8d499eSDavid van Moolenbroek 
408*ef8d499eSDavid van Moolenbroek 	/*
409*ef8d499eSDavid van Moolenbroek 	 * If the destination is a multicast address, select the outgoing
410*ef8d499eSDavid van Moolenbroek 	 * interface based on the multicast interface index, if one is set.
411*ef8d499eSDavid van Moolenbroek 	 * This must be done here in order to allow the code further below to
412*ef8d499eSDavid van Moolenbroek 	 * detect zone violations, because if we leave this selection to lwIP,
413*ef8d499eSDavid van Moolenbroek 	 * it will not perform zone violation detection at all.  Also note that
414*ef8d499eSDavid van Moolenbroek 	 * this case must *not* override an interface index already specified
415*ef8d499eSDavid van Moolenbroek 	 * using IPV6_PKTINFO, as per RFC 3542 Sec. 6.7.
416*ef8d499eSDavid van Moolenbroek 	 */
417*ef8d499eSDavid van Moolenbroek 	if (ifdev == NULL && ip_addr_ismulticast(dst_addrp)) {
418*ef8d499eSDavid van Moolenbroek 		ifindex = udp_get_multicast_netif_index(udp->udp_pcb);
419*ef8d499eSDavid van Moolenbroek 
420*ef8d499eSDavid van Moolenbroek 		if (ifindex != NETIF_NO_INDEX)
421*ef8d499eSDavid van Moolenbroek 			ifdev = ifdev_get_by_index(ifindex); /* (may fail) */
422*ef8d499eSDavid van Moolenbroek 	}
423*ef8d499eSDavid van Moolenbroek 
424*ef8d499eSDavid van Moolenbroek 	/*
425*ef8d499eSDavid van Moolenbroek 	 * If an interface has been determined already now, the send operation
426*ef8d499eSDavid van Moolenbroek 	 * will bypass routing.  In that case, we must perform our own checks
427*ef8d499eSDavid van Moolenbroek 	 * on address zone violations, because those will not be made anywhere
428*ef8d499eSDavid van Moolenbroek 	 * else.  Subsequent steps below will never introduce violations.
429*ef8d499eSDavid van Moolenbroek 	 */
430*ef8d499eSDavid van Moolenbroek 	if (ifdev != NULL && IP_IS_V6(dst_addrp)) {
431*ef8d499eSDavid van Moolenbroek 		if (ifaddr_is_zone_mismatch(ip_2_ip6(dst_addrp), ifdev))
432*ef8d499eSDavid van Moolenbroek 			return EHOSTUNREACH;
433*ef8d499eSDavid van Moolenbroek 
434*ef8d499eSDavid van Moolenbroek 		if (IP_IS_V6(src_addrp) &&
435*ef8d499eSDavid van Moolenbroek 		    ifaddr_is_zone_mismatch(ip_2_ip6(src_addrp), ifdev))
436*ef8d499eSDavid van Moolenbroek 			return EHOSTUNREACH;
437*ef8d499eSDavid van Moolenbroek 	}
438*ef8d499eSDavid van Moolenbroek 
439*ef8d499eSDavid van Moolenbroek 	/*
440*ef8d499eSDavid van Moolenbroek 	 * If we do not yet have an interface at this point, perform a route
441*ef8d499eSDavid van Moolenbroek 	 * lookup to determine the outgoing interface.  Unless MSG_DONTROUTE is
442*ef8d499eSDavid van Moolenbroek 	 * set (which covers SO_DONTROUTE as well), in which case we look for a
443*ef8d499eSDavid van Moolenbroek 	 * local subnet that matches the destination address.
444*ef8d499eSDavid van Moolenbroek 	 */
445*ef8d499eSDavid van Moolenbroek 	if (ifdev == NULL) {
446*ef8d499eSDavid van Moolenbroek 		if (!(flags & MSG_DONTROUTE)) {
447*ef8d499eSDavid van Moolenbroek 			/*
448*ef8d499eSDavid van Moolenbroek 			 * ip_route() should never be called with an
449*ef8d499eSDavid van Moolenbroek 			 * IPADDR_TYPE_ANY type address.  This is a lwIP-
450*ef8d499eSDavid van Moolenbroek 			 * internal requirement; while we override both routing
451*ef8d499eSDavid van Moolenbroek 			 * functions, we do not deviate from it.
452*ef8d499eSDavid van Moolenbroek 			 */
453*ef8d499eSDavid van Moolenbroek 			if (IP_IS_ANY_TYPE_VAL(*src_addrp))
454*ef8d499eSDavid van Moolenbroek 				src_addrp =
455*ef8d499eSDavid van Moolenbroek 				    IP46_ADDR_ANY(IP_GET_TYPE(dst_addrp));
456*ef8d499eSDavid van Moolenbroek 
457*ef8d499eSDavid van Moolenbroek 			/* Perform the route lookup. */
458*ef8d499eSDavid van Moolenbroek 			if ((netif = ip_route(src_addrp, dst_addrp)) == NULL)
459*ef8d499eSDavid van Moolenbroek 				return EHOSTUNREACH;
460*ef8d499eSDavid van Moolenbroek 
461*ef8d499eSDavid van Moolenbroek 			ifdev = netif_get_ifdev(netif);
462*ef8d499eSDavid van Moolenbroek 		} else {
463*ef8d499eSDavid van Moolenbroek 			if ((ifdev = ifaddr_map_by_subnet(dst_addrp)) == NULL)
464*ef8d499eSDavid van Moolenbroek 				return EHOSTUNREACH;
465*ef8d499eSDavid van Moolenbroek 		}
466*ef8d499eSDavid van Moolenbroek 	}
467*ef8d499eSDavid van Moolenbroek 
468*ef8d499eSDavid van Moolenbroek 	/*
469*ef8d499eSDavid van Moolenbroek 	 * At this point we have an outgoing interface.  If we do not have a
470*ef8d499eSDavid van Moolenbroek 	 * source address yet, pick one now.
471*ef8d499eSDavid van Moolenbroek 	 */
472*ef8d499eSDavid van Moolenbroek 	assert(ifdev != NULL);
473*ef8d499eSDavid van Moolenbroek 
474*ef8d499eSDavid van Moolenbroek 	if (ip_addr_isany(src_addrp)) {
475*ef8d499eSDavid van Moolenbroek 		src_addrp = ifaddr_select(dst_addrp, ifdev, NULL /*ifdevp*/);
476*ef8d499eSDavid van Moolenbroek 
477*ef8d499eSDavid van Moolenbroek 		if (src_addrp == NULL)
478*ef8d499eSDavid van Moolenbroek 			return EHOSTUNREACH;
479*ef8d499eSDavid van Moolenbroek 	}
480*ef8d499eSDavid van Moolenbroek 
481*ef8d499eSDavid van Moolenbroek 	/*
482*ef8d499eSDavid van Moolenbroek 	 * Now that we know the full conditions of what we are about to send,
483*ef8d499eSDavid van Moolenbroek 	 * check whether the packet size leaves enough room for lwIP to prepend
484*ef8d499eSDavid van Moolenbroek 	 * headers.  If so, allocate a chain of pbufs for the packet.
485*ef8d499eSDavid van Moolenbroek 	 */
486*ef8d499eSDavid van Moolenbroek 	assert(len <= UDP_MAX_PAYLOAD);
487*ef8d499eSDavid van Moolenbroek 
488*ef8d499eSDavid van Moolenbroek 	if (IP_IS_V6(dst_addrp))
489*ef8d499eSDavid van Moolenbroek 		hdrlen = IP6_HLEN + UDP_HLEN;
490*ef8d499eSDavid van Moolenbroek 	else
491*ef8d499eSDavid van Moolenbroek 		hdrlen = IP_HLEN + UDP_HLEN;
492*ef8d499eSDavid van Moolenbroek 
493*ef8d499eSDavid van Moolenbroek 	if (hdrlen + len > UDP_MAX_PAYLOAD)
494*ef8d499eSDavid van Moolenbroek 		return EMSGSIZE;
495*ef8d499eSDavid van Moolenbroek 
496*ef8d499eSDavid van Moolenbroek 	if ((pbuf = pchain_alloc(PBUF_TRANSPORT, len)) == NULL)
497*ef8d499eSDavid van Moolenbroek 		return ENOBUFS;
498*ef8d499eSDavid van Moolenbroek 
499*ef8d499eSDavid van Moolenbroek 	/* Copy in the packet data. */
500*ef8d499eSDavid van Moolenbroek 	if ((r = pktsock_get_data(&udp->udp_pktsock, data, len, pbuf)) != OK) {
501*ef8d499eSDavid van Moolenbroek 		pbuf_free(pbuf);
502*ef8d499eSDavid van Moolenbroek 
503*ef8d499eSDavid van Moolenbroek 		return r;
504*ef8d499eSDavid van Moolenbroek 	}
505*ef8d499eSDavid van Moolenbroek 
506*ef8d499eSDavid van Moolenbroek 	/*
507*ef8d499eSDavid van Moolenbroek 	 * Set broadcast/multicast flags for accounting purposes.  Only the
508*ef8d499eSDavid van Moolenbroek 	 * multicast flag is used for output accounting, but for loopback
509*ef8d499eSDavid van Moolenbroek 	 * traffic, both flags are copied and used for input accounting and
510*ef8d499eSDavid van Moolenbroek 	 * setting MSG_MCAST/MSG_BCAST.
511*ef8d499eSDavid van Moolenbroek 	 */
512*ef8d499eSDavid van Moolenbroek 	if (ip_addr_ismulticast(dst_addrp))
513*ef8d499eSDavid van Moolenbroek 		pbuf->flags |= PBUF_FLAG_LLMCAST;
514*ef8d499eSDavid van Moolenbroek 	else if (ip_addr_isbroadcast(dst_addrp, ifdev_get_netif(ifdev)))
515*ef8d499eSDavid van Moolenbroek 		pbuf->flags |= PBUF_FLAG_LLBCAST;
516*ef8d499eSDavid van Moolenbroek 
517*ef8d499eSDavid van Moolenbroek 	/* Send the packet. */
518*ef8d499eSDavid van Moolenbroek 	udpsock_swap_opt(udp, &pktopt);
519*ef8d499eSDavid van Moolenbroek 
520*ef8d499eSDavid van Moolenbroek 	assert(!ip_addr_isany(src_addrp));
521*ef8d499eSDavid van Moolenbroek 	assert(!ip_addr_ismulticast(src_addrp));
522*ef8d499eSDavid van Moolenbroek 
523*ef8d499eSDavid van Moolenbroek 	err = udp_sendto_if_src(udp->udp_pcb, pbuf, dst_addrp, dst_port,
524*ef8d499eSDavid van Moolenbroek 	    ifdev_get_netif(ifdev), src_addrp);
525*ef8d499eSDavid van Moolenbroek 
526*ef8d499eSDavid van Moolenbroek 	udpsock_swap_opt(udp, &pktopt);
527*ef8d499eSDavid van Moolenbroek 
528*ef8d499eSDavid van Moolenbroek 	/* Free the pbuf, as a copy has been made. */
529*ef8d499eSDavid van Moolenbroek 	pbuf_free(pbuf);
530*ef8d499eSDavid van Moolenbroek 
531*ef8d499eSDavid van Moolenbroek 	/*
532*ef8d499eSDavid van Moolenbroek 	 * On success, make sure to return the size of the sent packet as well.
533*ef8d499eSDavid van Moolenbroek 	 * As an aside: ctl_off need not be updated, as it is not returned.
534*ef8d499eSDavid van Moolenbroek 	 */
535*ef8d499eSDavid van Moolenbroek 	if ((r = util_convert_err(err)) == OK)
536*ef8d499eSDavid van Moolenbroek 		*off = len;
537*ef8d499eSDavid van Moolenbroek 	return r;
538*ef8d499eSDavid van Moolenbroek }
539*ef8d499eSDavid van Moolenbroek 
540*ef8d499eSDavid van Moolenbroek /*
541*ef8d499eSDavid van Moolenbroek  * Update the set of flag-type socket options on a UDP socket.
542*ef8d499eSDavid van Moolenbroek  */
543*ef8d499eSDavid van Moolenbroek static void
udpsock_setsockmask(struct sock * sock,unsigned int mask)544*ef8d499eSDavid van Moolenbroek udpsock_setsockmask(struct sock * sock, unsigned int mask)
545*ef8d499eSDavid van Moolenbroek {
546*ef8d499eSDavid van Moolenbroek 	struct udpsock *udp = (struct udpsock *)sock;
547*ef8d499eSDavid van Moolenbroek 
548*ef8d499eSDavid van Moolenbroek 	if (mask & SO_REUSEADDR)
549*ef8d499eSDavid van Moolenbroek 		ip_set_option(udp->udp_pcb, SOF_REUSEADDR);
550*ef8d499eSDavid van Moolenbroek 	else
551*ef8d499eSDavid van Moolenbroek 		ip_reset_option(udp->udp_pcb, SOF_REUSEADDR);
552*ef8d499eSDavid van Moolenbroek 
553*ef8d499eSDavid van Moolenbroek 	if (mask & SO_BROADCAST)
554*ef8d499eSDavid van Moolenbroek 		ip_set_option(udp->udp_pcb, SOF_BROADCAST);
555*ef8d499eSDavid van Moolenbroek 	else
556*ef8d499eSDavid van Moolenbroek 		ip_reset_option(udp->udp_pcb, SOF_BROADCAST);
557*ef8d499eSDavid van Moolenbroek }
558*ef8d499eSDavid van Moolenbroek 
559*ef8d499eSDavid van Moolenbroek /*
560*ef8d499eSDavid van Moolenbroek  * Prepare a helper structure for IP-level option processing.
561*ef8d499eSDavid van Moolenbroek  */
562*ef8d499eSDavid van Moolenbroek static void
udpsock_get_ipopts(struct udpsock * udp,struct ipopts * ipopts)563*ef8d499eSDavid van Moolenbroek udpsock_get_ipopts(struct udpsock * udp, struct ipopts * ipopts)
564*ef8d499eSDavid van Moolenbroek {
565*ef8d499eSDavid van Moolenbroek 
566*ef8d499eSDavid van Moolenbroek 	ipopts->local_ip = &udp->udp_pcb->local_ip;
567*ef8d499eSDavid van Moolenbroek 	ipopts->remote_ip = &udp->udp_pcb->remote_ip;
568*ef8d499eSDavid van Moolenbroek 	ipopts->tos = &udp->udp_pcb->tos;
569*ef8d499eSDavid van Moolenbroek 	ipopts->ttl = &udp->udp_pcb->ttl;
570*ef8d499eSDavid van Moolenbroek 	ipopts->sndmin = UDP_SNDBUF_MIN;
571*ef8d499eSDavid van Moolenbroek 	ipopts->sndmax = UDP_SNDBUF_MAX;
572*ef8d499eSDavid van Moolenbroek 	ipopts->rcvmin = UDP_RCVBUF_MIN;
573*ef8d499eSDavid van Moolenbroek 	ipopts->rcvmax = UDP_RCVBUF_MAX;
574*ef8d499eSDavid van Moolenbroek }
575*ef8d499eSDavid van Moolenbroek 
576*ef8d499eSDavid van Moolenbroek /*
577*ef8d499eSDavid van Moolenbroek  * Set socket options on a UDP socket.
578*ef8d499eSDavid van Moolenbroek  */
579*ef8d499eSDavid van Moolenbroek static int
udpsock_setsockopt(struct sock * sock,int level,int name,const struct sockdriver_data * data,socklen_t len)580*ef8d499eSDavid van Moolenbroek udpsock_setsockopt(struct sock * sock, int level, int name,
581*ef8d499eSDavid van Moolenbroek 	const struct sockdriver_data * data, socklen_t len)
582*ef8d499eSDavid van Moolenbroek {
583*ef8d499eSDavid van Moolenbroek 	struct udpsock *udp = (struct udpsock *)sock;
584*ef8d499eSDavid van Moolenbroek 	struct ipopts ipopts;
585*ef8d499eSDavid van Moolenbroek 	ip_addr_t ipaddr;
586*ef8d499eSDavid van Moolenbroek 	struct in_addr in_addr;
587*ef8d499eSDavid van Moolenbroek 	struct ifdev *ifdev;
588*ef8d499eSDavid van Moolenbroek 	unsigned int flags;
589*ef8d499eSDavid van Moolenbroek 	uint32_t ifindex;
590*ef8d499eSDavid van Moolenbroek 	uint8_t byte;
591*ef8d499eSDavid van Moolenbroek 	int r, val;
592*ef8d499eSDavid van Moolenbroek 
593*ef8d499eSDavid van Moolenbroek 	/*
594*ef8d499eSDavid van Moolenbroek 	 * Unfortunately, we have to duplicate most of the multicast options
595*ef8d499eSDavid van Moolenbroek 	 * rather than sharing them with rawsock at the pktsock level.  The
596*ef8d499eSDavid van Moolenbroek 	 * reason is that each of the PCBs have their own multicast abstraction
597*ef8d499eSDavid van Moolenbroek 	 * functions and so we cannot merge the rest.  Same for getsockopt.
598*ef8d499eSDavid van Moolenbroek 	 */
599*ef8d499eSDavid van Moolenbroek 
600*ef8d499eSDavid van Moolenbroek 	switch (level) {
601*ef8d499eSDavid van Moolenbroek 	case IPPROTO_IP:
602*ef8d499eSDavid van Moolenbroek 		if (udpsock_is_ipv6(udp))
603*ef8d499eSDavid van Moolenbroek 			break;
604*ef8d499eSDavid van Moolenbroek 
605*ef8d499eSDavid van Moolenbroek 		switch (name) {
606*ef8d499eSDavid van Moolenbroek 		case IP_MULTICAST_IF:
607*ef8d499eSDavid van Moolenbroek 			pktsock_set_mcaware(&udp->udp_pktsock);
608*ef8d499eSDavid van Moolenbroek 
609*ef8d499eSDavid van Moolenbroek 			if ((r = sockdriver_copyin_opt(data, &in_addr,
610*ef8d499eSDavid van Moolenbroek 			    sizeof(in_addr), len)) != OK)
611*ef8d499eSDavid van Moolenbroek 				return r;
612*ef8d499eSDavid van Moolenbroek 
613*ef8d499eSDavid van Moolenbroek 			ip_addr_set_ip4_u32(&ipaddr, in_addr.s_addr);
614*ef8d499eSDavid van Moolenbroek 
615*ef8d499eSDavid van Moolenbroek 			if ((ifdev = ifaddr_map_by_addr(&ipaddr)) == NULL)
616*ef8d499eSDavid van Moolenbroek 				return EADDRNOTAVAIL;
617*ef8d499eSDavid van Moolenbroek 
618*ef8d499eSDavid van Moolenbroek 			udp_set_multicast_netif_index(udp->udp_pcb,
619*ef8d499eSDavid van Moolenbroek 			    ifdev_get_index(ifdev));
620*ef8d499eSDavid van Moolenbroek 
621*ef8d499eSDavid van Moolenbroek 			return OK;
622*ef8d499eSDavid van Moolenbroek 
623*ef8d499eSDavid van Moolenbroek 		case IP_MULTICAST_LOOP:
624*ef8d499eSDavid van Moolenbroek 			pktsock_set_mcaware(&udp->udp_pktsock);
625*ef8d499eSDavid van Moolenbroek 
626*ef8d499eSDavid van Moolenbroek 			if ((r = sockdriver_copyin_opt(data, &byte,
627*ef8d499eSDavid van Moolenbroek 			    sizeof(byte), len)) != OK)
628*ef8d499eSDavid van Moolenbroek 				return r;
629*ef8d499eSDavid van Moolenbroek 
630*ef8d499eSDavid van Moolenbroek 			flags = udp_flags(udp->udp_pcb);
631*ef8d499eSDavid van Moolenbroek 
632*ef8d499eSDavid van Moolenbroek 			if (byte)
633*ef8d499eSDavid van Moolenbroek 				flags |= UDP_FLAGS_MULTICAST_LOOP;
634*ef8d499eSDavid van Moolenbroek 			else
635*ef8d499eSDavid van Moolenbroek 				flags &= ~UDP_FLAGS_MULTICAST_LOOP;
636*ef8d499eSDavid van Moolenbroek 
637*ef8d499eSDavid van Moolenbroek 			udp_setflags(udp->udp_pcb, flags);
638*ef8d499eSDavid van Moolenbroek 
639*ef8d499eSDavid van Moolenbroek 			return OK;
640*ef8d499eSDavid van Moolenbroek 
641*ef8d499eSDavid van Moolenbroek 		case IP_MULTICAST_TTL:
642*ef8d499eSDavid van Moolenbroek 			pktsock_set_mcaware(&udp->udp_pktsock);
643*ef8d499eSDavid van Moolenbroek 
644*ef8d499eSDavid van Moolenbroek 			if ((r = sockdriver_copyin_opt(data, &byte,
645*ef8d499eSDavid van Moolenbroek 			    sizeof(byte), len)) != OK)
646*ef8d499eSDavid van Moolenbroek 				return r;
647*ef8d499eSDavid van Moolenbroek 
648*ef8d499eSDavid van Moolenbroek 			udp_set_multicast_ttl(udp->udp_pcb, byte);
649*ef8d499eSDavid van Moolenbroek 
650*ef8d499eSDavid van Moolenbroek 			return OK;
651*ef8d499eSDavid van Moolenbroek 		}
652*ef8d499eSDavid van Moolenbroek 
653*ef8d499eSDavid van Moolenbroek 		break;
654*ef8d499eSDavid van Moolenbroek 
655*ef8d499eSDavid van Moolenbroek 	case IPPROTO_IPV6:
656*ef8d499eSDavid van Moolenbroek 		if (!udpsock_is_ipv6(udp))
657*ef8d499eSDavid van Moolenbroek 			break;
658*ef8d499eSDavid van Moolenbroek 
659*ef8d499eSDavid van Moolenbroek 		switch (name) {
660*ef8d499eSDavid van Moolenbroek 		case IPV6_MULTICAST_IF:
661*ef8d499eSDavid van Moolenbroek 			pktsock_set_mcaware(&udp->udp_pktsock);
662*ef8d499eSDavid van Moolenbroek 
663*ef8d499eSDavid van Moolenbroek 			if ((r = sockdriver_copyin_opt(data, &val, sizeof(val),
664*ef8d499eSDavid van Moolenbroek 			    len)) != OK)
665*ef8d499eSDavid van Moolenbroek 				return r;
666*ef8d499eSDavid van Moolenbroek 
667*ef8d499eSDavid van Moolenbroek 			if (val != 0) {
668*ef8d499eSDavid van Moolenbroek 				ifindex = (uint32_t)val;
669*ef8d499eSDavid van Moolenbroek 
670*ef8d499eSDavid van Moolenbroek 				ifdev = ifdev_get_by_index(ifindex);
671*ef8d499eSDavid van Moolenbroek 
672*ef8d499eSDavid van Moolenbroek 				if (ifdev == NULL)
673*ef8d499eSDavid van Moolenbroek 					return ENXIO;
674*ef8d499eSDavid van Moolenbroek 			} else
675*ef8d499eSDavid van Moolenbroek 				ifindex = NETIF_NO_INDEX;
676*ef8d499eSDavid van Moolenbroek 
677*ef8d499eSDavid van Moolenbroek 			udp_set_multicast_netif_index(udp->udp_pcb, ifindex);
678*ef8d499eSDavid van Moolenbroek 
679*ef8d499eSDavid van Moolenbroek 			return OK;
680*ef8d499eSDavid van Moolenbroek 
681*ef8d499eSDavid van Moolenbroek 		case IPV6_MULTICAST_LOOP:
682*ef8d499eSDavid van Moolenbroek 			pktsock_set_mcaware(&udp->udp_pktsock);
683*ef8d499eSDavid van Moolenbroek 
684*ef8d499eSDavid van Moolenbroek 			if ((r = sockdriver_copyin_opt(data, &val, sizeof(val),
685*ef8d499eSDavid van Moolenbroek 			    len)) != OK)
686*ef8d499eSDavid van Moolenbroek 				return r;
687*ef8d499eSDavid van Moolenbroek 
688*ef8d499eSDavid van Moolenbroek 			if (val < 0 || val > 1)
689*ef8d499eSDavid van Moolenbroek 				return EINVAL;
690*ef8d499eSDavid van Moolenbroek 
691*ef8d499eSDavid van Moolenbroek 			flags = udp_flags(udp->udp_pcb);
692*ef8d499eSDavid van Moolenbroek 
693*ef8d499eSDavid van Moolenbroek 			if (val)
694*ef8d499eSDavid van Moolenbroek 				flags |= UDP_FLAGS_MULTICAST_LOOP;
695*ef8d499eSDavid van Moolenbroek 			else
696*ef8d499eSDavid van Moolenbroek 				flags &= ~UDP_FLAGS_MULTICAST_LOOP;
697*ef8d499eSDavid van Moolenbroek 
698*ef8d499eSDavid van Moolenbroek 			/*
699*ef8d499eSDavid van Moolenbroek 			 * lwIP's IPv6 functionality does not actually check
700*ef8d499eSDavid van Moolenbroek 			 * this flag at all yet.  We set it in the hope that
701*ef8d499eSDavid van Moolenbroek 			 * one day this will magically start working.
702*ef8d499eSDavid van Moolenbroek 			 */
703*ef8d499eSDavid van Moolenbroek 			udp_setflags(udp->udp_pcb, flags);
704*ef8d499eSDavid van Moolenbroek 
705*ef8d499eSDavid van Moolenbroek 			return OK;
706*ef8d499eSDavid van Moolenbroek 
707*ef8d499eSDavid van Moolenbroek 		case IPV6_MULTICAST_HOPS:
708*ef8d499eSDavid van Moolenbroek 			pktsock_set_mcaware(&udp->udp_pktsock);
709*ef8d499eSDavid van Moolenbroek 
710*ef8d499eSDavid van Moolenbroek 			if ((r = sockdriver_copyin_opt(data, &val, sizeof(val),
711*ef8d499eSDavid van Moolenbroek 			    len)) != OK)
712*ef8d499eSDavid van Moolenbroek 				return r;
713*ef8d499eSDavid van Moolenbroek 
714*ef8d499eSDavid van Moolenbroek 			if (val < -1 || val > UINT8_MAX)
715*ef8d499eSDavid van Moolenbroek 				return EINVAL;
716*ef8d499eSDavid van Moolenbroek 
717*ef8d499eSDavid van Moolenbroek 			if (val == -1)
718*ef8d499eSDavid van Moolenbroek 				val = 1;
719*ef8d499eSDavid van Moolenbroek 
720*ef8d499eSDavid van Moolenbroek 			udp_set_multicast_ttl(udp->udp_pcb, val);
721*ef8d499eSDavid van Moolenbroek 
722*ef8d499eSDavid van Moolenbroek 			return OK;
723*ef8d499eSDavid van Moolenbroek 		}
724*ef8d499eSDavid van Moolenbroek 
725*ef8d499eSDavid van Moolenbroek 		break;
726*ef8d499eSDavid van Moolenbroek 	}
727*ef8d499eSDavid van Moolenbroek 
728*ef8d499eSDavid van Moolenbroek 	/* Handle all other options at the packet or IP level. */
729*ef8d499eSDavid van Moolenbroek 	udpsock_get_ipopts(udp, &ipopts);
730*ef8d499eSDavid van Moolenbroek 
731*ef8d499eSDavid van Moolenbroek 	return pktsock_setsockopt(&udp->udp_pktsock, level, name, data, len,
732*ef8d499eSDavid van Moolenbroek 	    &ipopts);
733*ef8d499eSDavid van Moolenbroek }
734*ef8d499eSDavid van Moolenbroek 
735*ef8d499eSDavid van Moolenbroek /*
736*ef8d499eSDavid van Moolenbroek  * Retrieve socket options on a UDP socket.
737*ef8d499eSDavid van Moolenbroek  */
738*ef8d499eSDavid van Moolenbroek static int
udpsock_getsockopt(struct sock * sock,int level,int name,const struct sockdriver_data * data,socklen_t * len)739*ef8d499eSDavid van Moolenbroek udpsock_getsockopt(struct sock * sock, int level, int name,
740*ef8d499eSDavid van Moolenbroek 	const struct sockdriver_data * data, socklen_t * len)
741*ef8d499eSDavid van Moolenbroek {
742*ef8d499eSDavid van Moolenbroek 	struct udpsock *udp = (struct udpsock *)sock;
743*ef8d499eSDavid van Moolenbroek 	struct ipopts ipopts;
744*ef8d499eSDavid van Moolenbroek 	const ip4_addr_t *ip4addr;
745*ef8d499eSDavid van Moolenbroek 	struct in_addr in_addr;
746*ef8d499eSDavid van Moolenbroek 	struct ifdev *ifdev;
747*ef8d499eSDavid van Moolenbroek 	unsigned int flags;
748*ef8d499eSDavid van Moolenbroek 	uint32_t ifindex;
749*ef8d499eSDavid van Moolenbroek 	uint8_t byte;
750*ef8d499eSDavid van Moolenbroek 	int val;
751*ef8d499eSDavid van Moolenbroek 
752*ef8d499eSDavid van Moolenbroek 	switch (level) {
753*ef8d499eSDavid van Moolenbroek 	case IPPROTO_IP:
754*ef8d499eSDavid van Moolenbroek 		if (udpsock_is_ipv6(udp))
755*ef8d499eSDavid van Moolenbroek 			break;
756*ef8d499eSDavid van Moolenbroek 
757*ef8d499eSDavid van Moolenbroek 		switch (name) {
758*ef8d499eSDavid van Moolenbroek 		case IP_MULTICAST_IF:
759*ef8d499eSDavid van Moolenbroek 			ifindex = udp_get_multicast_netif_index(udp->udp_pcb);
760*ef8d499eSDavid van Moolenbroek 
761*ef8d499eSDavid van Moolenbroek 			/*
762*ef8d499eSDavid van Moolenbroek 			 * Map back from the interface index to the IPv4
763*ef8d499eSDavid van Moolenbroek 			 * address assigned to the corresponding interface.
764*ef8d499eSDavid van Moolenbroek 			 * Should this not work out, return the 'any' address.
765*ef8d499eSDavid van Moolenbroek 			 */
766*ef8d499eSDavid van Moolenbroek 			if (ifindex != NETIF_NO_INDEX &&
767*ef8d499eSDavid van Moolenbroek 			   (ifdev = ifdev_get_by_index(ifindex)) != NULL) {
768*ef8d499eSDavid van Moolenbroek 				ip4addr =
769*ef8d499eSDavid van Moolenbroek 				    netif_ip4_addr(ifdev_get_netif(ifdev));
770*ef8d499eSDavid van Moolenbroek 
771*ef8d499eSDavid van Moolenbroek 				in_addr.s_addr = ip4_addr_get_u32(ip4addr);
772*ef8d499eSDavid van Moolenbroek 			} else
773*ef8d499eSDavid van Moolenbroek 				in_addr.s_addr = PP_HTONL(INADDR_ANY);
774*ef8d499eSDavid van Moolenbroek 
775*ef8d499eSDavid van Moolenbroek 			return sockdriver_copyout_opt(data, &in_addr,
776*ef8d499eSDavid van Moolenbroek 			    sizeof(in_addr), len);
777*ef8d499eSDavid van Moolenbroek 
778*ef8d499eSDavid van Moolenbroek 		case IP_MULTICAST_LOOP:
779*ef8d499eSDavid van Moolenbroek 			flags = udp_flags(udp->udp_pcb);
780*ef8d499eSDavid van Moolenbroek 
781*ef8d499eSDavid van Moolenbroek 			byte = !!(flags & UDP_FLAGS_MULTICAST_LOOP);
782*ef8d499eSDavid van Moolenbroek 
783*ef8d499eSDavid van Moolenbroek 			return sockdriver_copyout_opt(data, &byte,
784*ef8d499eSDavid van Moolenbroek 			    sizeof(byte), len);
785*ef8d499eSDavid van Moolenbroek 
786*ef8d499eSDavid van Moolenbroek 		case IP_MULTICAST_TTL:
787*ef8d499eSDavid van Moolenbroek 			byte = udp_get_multicast_ttl(udp->udp_pcb);
788*ef8d499eSDavid van Moolenbroek 
789*ef8d499eSDavid van Moolenbroek 			return sockdriver_copyout_opt(data, &byte,
790*ef8d499eSDavid van Moolenbroek 			    sizeof(byte), len);
791*ef8d499eSDavid van Moolenbroek 		}
792*ef8d499eSDavid van Moolenbroek 
793*ef8d499eSDavid van Moolenbroek 		break;
794*ef8d499eSDavid van Moolenbroek 
795*ef8d499eSDavid van Moolenbroek 	case IPPROTO_IPV6:
796*ef8d499eSDavid van Moolenbroek 		if (!udpsock_is_ipv6(udp))
797*ef8d499eSDavid van Moolenbroek 			break;
798*ef8d499eSDavid van Moolenbroek 
799*ef8d499eSDavid van Moolenbroek 		switch (name) {
800*ef8d499eSDavid van Moolenbroek 		case IPV6_MULTICAST_IF:
801*ef8d499eSDavid van Moolenbroek 			ifindex = udp_get_multicast_netif_index(udp->udp_pcb);
802*ef8d499eSDavid van Moolenbroek 
803*ef8d499eSDavid van Moolenbroek 			val = (int)ifindex;
804*ef8d499eSDavid van Moolenbroek 
805*ef8d499eSDavid van Moolenbroek 			return sockdriver_copyout_opt(data, &val, sizeof(val),
806*ef8d499eSDavid van Moolenbroek 			    len);
807*ef8d499eSDavid van Moolenbroek 
808*ef8d499eSDavid van Moolenbroek 		case IPV6_MULTICAST_LOOP:
809*ef8d499eSDavid van Moolenbroek 			flags = udp_flags(udp->udp_pcb);
810*ef8d499eSDavid van Moolenbroek 
811*ef8d499eSDavid van Moolenbroek 			val = !!(flags & UDP_FLAGS_MULTICAST_LOOP);
812*ef8d499eSDavid van Moolenbroek 
813*ef8d499eSDavid van Moolenbroek 			return sockdriver_copyout_opt(data, &val, sizeof(val),
814*ef8d499eSDavid van Moolenbroek 			    len);
815*ef8d499eSDavid van Moolenbroek 
816*ef8d499eSDavid van Moolenbroek 		case IPV6_MULTICAST_HOPS:
817*ef8d499eSDavid van Moolenbroek 			val = udp_get_multicast_ttl(udp->udp_pcb);
818*ef8d499eSDavid van Moolenbroek 
819*ef8d499eSDavid van Moolenbroek 			return sockdriver_copyout_opt(data, &val, sizeof(val),
820*ef8d499eSDavid van Moolenbroek 			    len);
821*ef8d499eSDavid van Moolenbroek 		}
822*ef8d499eSDavid van Moolenbroek 
823*ef8d499eSDavid van Moolenbroek 		break;
824*ef8d499eSDavid van Moolenbroek 	}
825*ef8d499eSDavid van Moolenbroek 
826*ef8d499eSDavid van Moolenbroek 	/* Handle all other options at the packet or IP level. */
827*ef8d499eSDavid van Moolenbroek 	udpsock_get_ipopts(udp, &ipopts);
828*ef8d499eSDavid van Moolenbroek 
829*ef8d499eSDavid van Moolenbroek 	return pktsock_getsockopt(&udp->udp_pktsock, level, name, data, len,
830*ef8d499eSDavid van Moolenbroek 	    &ipopts);
831*ef8d499eSDavid van Moolenbroek }
832*ef8d499eSDavid van Moolenbroek 
833*ef8d499eSDavid van Moolenbroek /*
834*ef8d499eSDavid van Moolenbroek  * Retrieve the local socket address of a UDP socket.
835*ef8d499eSDavid van Moolenbroek  */
836*ef8d499eSDavid van Moolenbroek static int
udpsock_getsockname(struct sock * sock,struct sockaddr * addr,socklen_t * addr_len)837*ef8d499eSDavid van Moolenbroek udpsock_getsockname(struct sock * sock, struct sockaddr * addr,
838*ef8d499eSDavid van Moolenbroek 	socklen_t * addr_len)
839*ef8d499eSDavid van Moolenbroek {
840*ef8d499eSDavid van Moolenbroek 	struct udpsock *udp = (struct udpsock *)sock;
841*ef8d499eSDavid van Moolenbroek 
842*ef8d499eSDavid van Moolenbroek 	ipsock_put_addr(udpsock_get_ipsock(udp), addr, addr_len,
843*ef8d499eSDavid van Moolenbroek 	    &udp->udp_pcb->local_ip, udp->udp_pcb->local_port);
844*ef8d499eSDavid van Moolenbroek 
845*ef8d499eSDavid van Moolenbroek 	return OK;
846*ef8d499eSDavid van Moolenbroek }
847*ef8d499eSDavid van Moolenbroek 
848*ef8d499eSDavid van Moolenbroek /*
849*ef8d499eSDavid van Moolenbroek  * Retrieve the remote socket address of a UDP socket.
850*ef8d499eSDavid van Moolenbroek  */
851*ef8d499eSDavid van Moolenbroek static int
udpsock_getpeername(struct sock * sock,struct sockaddr * addr,socklen_t * addr_len)852*ef8d499eSDavid van Moolenbroek udpsock_getpeername(struct sock * sock, struct sockaddr * addr,
853*ef8d499eSDavid van Moolenbroek 	socklen_t * addr_len)
854*ef8d499eSDavid van Moolenbroek {
855*ef8d499eSDavid van Moolenbroek 	struct udpsock *udp = (struct udpsock *)sock;
856*ef8d499eSDavid van Moolenbroek 
857*ef8d499eSDavid van Moolenbroek 	if (!udpsock_is_conn(udp))
858*ef8d499eSDavid van Moolenbroek 		return ENOTCONN;
859*ef8d499eSDavid van Moolenbroek 
860*ef8d499eSDavid van Moolenbroek 	ipsock_put_addr(udpsock_get_ipsock(udp), addr, addr_len,
861*ef8d499eSDavid van Moolenbroek 	    &udp->udp_pcb->remote_ip, udp->udp_pcb->remote_port);
862*ef8d499eSDavid van Moolenbroek 
863*ef8d499eSDavid van Moolenbroek 	return OK;
864*ef8d499eSDavid van Moolenbroek }
865*ef8d499eSDavid van Moolenbroek 
866*ef8d499eSDavid van Moolenbroek /*
867*ef8d499eSDavid van Moolenbroek  * Shut down a UDP socket for reading and/or writing.
868*ef8d499eSDavid van Moolenbroek  */
869*ef8d499eSDavid van Moolenbroek static int
udpsock_shutdown(struct sock * sock,unsigned int mask)870*ef8d499eSDavid van Moolenbroek udpsock_shutdown(struct sock * sock, unsigned int mask)
871*ef8d499eSDavid van Moolenbroek {
872*ef8d499eSDavid van Moolenbroek 	struct udpsock *udp = (struct udpsock *)sock;
873*ef8d499eSDavid van Moolenbroek 
874*ef8d499eSDavid van Moolenbroek 	if (mask & SFL_SHUT_RD)
875*ef8d499eSDavid van Moolenbroek 		udp_recv(udp->udp_pcb, NULL, NULL);
876*ef8d499eSDavid van Moolenbroek 
877*ef8d499eSDavid van Moolenbroek 	pktsock_shutdown(&udp->udp_pktsock, mask);
878*ef8d499eSDavid van Moolenbroek 
879*ef8d499eSDavid van Moolenbroek 	return OK;
880*ef8d499eSDavid van Moolenbroek }
881*ef8d499eSDavid van Moolenbroek 
882*ef8d499eSDavid van Moolenbroek /*
883*ef8d499eSDavid van Moolenbroek  * Close a UDP socket.
884*ef8d499eSDavid van Moolenbroek  */
885*ef8d499eSDavid van Moolenbroek static int
udpsock_close(struct sock * sock,int force __unused)886*ef8d499eSDavid van Moolenbroek udpsock_close(struct sock * sock, int force __unused)
887*ef8d499eSDavid van Moolenbroek {
888*ef8d499eSDavid van Moolenbroek 	struct udpsock *udp = (struct udpsock *)sock;
889*ef8d499eSDavid van Moolenbroek 
890*ef8d499eSDavid van Moolenbroek 	udp_recv(udp->udp_pcb, NULL, NULL);
891*ef8d499eSDavid van Moolenbroek 
892*ef8d499eSDavid van Moolenbroek 	udp_remove(udp->udp_pcb);
893*ef8d499eSDavid van Moolenbroek 	udp->udp_pcb = NULL;
894*ef8d499eSDavid van Moolenbroek 
895*ef8d499eSDavid van Moolenbroek 	pktsock_close(&udp->udp_pktsock);
896*ef8d499eSDavid van Moolenbroek 
897*ef8d499eSDavid van Moolenbroek 	return OK;
898*ef8d499eSDavid van Moolenbroek }
899*ef8d499eSDavid van Moolenbroek 
900*ef8d499eSDavid van Moolenbroek /*
901*ef8d499eSDavid van Moolenbroek  * Free up a closed UDP socket.
902*ef8d499eSDavid van Moolenbroek  */
903*ef8d499eSDavid van Moolenbroek static void
udpsock_free(struct sock * sock)904*ef8d499eSDavid van Moolenbroek udpsock_free(struct sock * sock)
905*ef8d499eSDavid van Moolenbroek {
906*ef8d499eSDavid van Moolenbroek 	struct udpsock *udp = (struct udpsock *)sock;
907*ef8d499eSDavid van Moolenbroek 
908*ef8d499eSDavid van Moolenbroek 	assert(udp->udp_pcb == NULL);
909*ef8d499eSDavid van Moolenbroek 
910*ef8d499eSDavid van Moolenbroek 	SIMPLEQ_INSERT_HEAD(&udp_freelist, udp, udp_next);
911*ef8d499eSDavid van Moolenbroek }
912*ef8d499eSDavid van Moolenbroek 
913*ef8d499eSDavid van Moolenbroek /*
914*ef8d499eSDavid van Moolenbroek  * Fill the given kinfo_pcb sysctl(7) structure with information about the UDP
915*ef8d499eSDavid van Moolenbroek  * PCB identified by the given pointer.
916*ef8d499eSDavid van Moolenbroek  */
917*ef8d499eSDavid van Moolenbroek static void
udpsock_get_info(struct kinfo_pcb * ki,const void * ptr)918*ef8d499eSDavid van Moolenbroek udpsock_get_info(struct kinfo_pcb * ki, const void * ptr)
919*ef8d499eSDavid van Moolenbroek {
920*ef8d499eSDavid van Moolenbroek 	const struct udp_pcb *pcb = (const struct udp_pcb *)ptr;
921*ef8d499eSDavid van Moolenbroek 	struct udpsock *udp;
922*ef8d499eSDavid van Moolenbroek 
923*ef8d499eSDavid van Moolenbroek 	ki->ki_type = SOCK_DGRAM;
924*ef8d499eSDavid van Moolenbroek 
925*ef8d499eSDavid van Moolenbroek 	/*
926*ef8d499eSDavid van Moolenbroek 	 * All UDP sockets should be created by this module, but protect
927*ef8d499eSDavid van Moolenbroek 	 * ourselves from the case that that is not true anyway.
928*ef8d499eSDavid van Moolenbroek 	 */
929*ef8d499eSDavid van Moolenbroek 	if (pcb->recv_arg != NULL) {
930*ef8d499eSDavid van Moolenbroek 		udp = (struct udpsock *)pcb->recv_arg;
931*ef8d499eSDavid van Moolenbroek 
932*ef8d499eSDavid van Moolenbroek 		assert(udp >= udp_array &&
933*ef8d499eSDavid van Moolenbroek 		    udp < &udp_array[__arraycount(udp_array)]);
934*ef8d499eSDavid van Moolenbroek 	} else
935*ef8d499eSDavid van Moolenbroek 		udp = NULL;
936*ef8d499eSDavid van Moolenbroek 
937*ef8d499eSDavid van Moolenbroek 	ipsock_get_info(ki, &pcb->local_ip, pcb->local_port, &pcb->remote_ip,
938*ef8d499eSDavid van Moolenbroek 	    pcb->remote_port);
939*ef8d499eSDavid van Moolenbroek 
940*ef8d499eSDavid van Moolenbroek 	if (udp != NULL) {
941*ef8d499eSDavid van Moolenbroek 		/* TODO: change this so that sockstat(1) may work one day. */
942*ef8d499eSDavid van Moolenbroek 		ki->ki_sockaddr = (uint64_t)(uintptr_t)udpsock_get_sock(udp);
943*ef8d499eSDavid van Moolenbroek 
944*ef8d499eSDavid van Moolenbroek 		ki->ki_rcvq = pktsock_get_recvlen(&udp->udp_pktsock);
945*ef8d499eSDavid van Moolenbroek 	}
946*ef8d499eSDavid van Moolenbroek }
947*ef8d499eSDavid van Moolenbroek 
948*ef8d499eSDavid van Moolenbroek /*
949*ef8d499eSDavid van Moolenbroek  * Given either NULL or a previously returned UDP PCB pointer, return the first
950*ef8d499eSDavid van Moolenbroek  * or next UDP PCB pointer, or NULL if there are no more.  Skip UDP PCBs that
951*ef8d499eSDavid van Moolenbroek  * are not bound to an address, as there is no use reporting them.
952*ef8d499eSDavid van Moolenbroek  */
953*ef8d499eSDavid van Moolenbroek static const void *
udpsock_enum(const void * last)954*ef8d499eSDavid van Moolenbroek udpsock_enum(const void * last)
955*ef8d499eSDavid van Moolenbroek {
956*ef8d499eSDavid van Moolenbroek 	const struct udp_pcb *pcb;
957*ef8d499eSDavid van Moolenbroek 
958*ef8d499eSDavid van Moolenbroek 	if (last != NULL)
959*ef8d499eSDavid van Moolenbroek 		pcb = (const void *)((const struct udp_pcb *)last)->next;
960*ef8d499eSDavid van Moolenbroek 	else
961*ef8d499eSDavid van Moolenbroek 		pcb = (const void *)udp_pcbs;
962*ef8d499eSDavid van Moolenbroek 
963*ef8d499eSDavid van Moolenbroek 	while (pcb != NULL && pcb->local_port == 0)
964*ef8d499eSDavid van Moolenbroek 		pcb = pcb->next;
965*ef8d499eSDavid van Moolenbroek 
966*ef8d499eSDavid van Moolenbroek 	return pcb;
967*ef8d499eSDavid van Moolenbroek }
968*ef8d499eSDavid van Moolenbroek 
969*ef8d499eSDavid van Moolenbroek /*
970*ef8d499eSDavid van Moolenbroek  * Obtain the list of UDP protocol control blocks, for sysctl(7).
971*ef8d499eSDavid van Moolenbroek  */
972*ef8d499eSDavid van Moolenbroek static ssize_t
udpsock_pcblist(struct rmib_call * call,struct rmib_node * node __unused,struct rmib_oldp * oldp,struct rmib_newp * newp __unused)973*ef8d499eSDavid van Moolenbroek udpsock_pcblist(struct rmib_call * call, struct rmib_node * node __unused,
974*ef8d499eSDavid van Moolenbroek 	struct rmib_oldp * oldp, struct rmib_newp * newp __unused)
975*ef8d499eSDavid van Moolenbroek {
976*ef8d499eSDavid van Moolenbroek 
977*ef8d499eSDavid van Moolenbroek 	return util_pcblist(call, oldp, udpsock_enum, udpsock_get_info);
978*ef8d499eSDavid van Moolenbroek }
979*ef8d499eSDavid van Moolenbroek 
980*ef8d499eSDavid van Moolenbroek static const struct sockevent_ops udpsock_ops = {
981*ef8d499eSDavid van Moolenbroek 	.sop_bind		= udpsock_bind,
982*ef8d499eSDavid van Moolenbroek 	.sop_connect		= udpsock_connect,
983*ef8d499eSDavid van Moolenbroek 	.sop_pre_send		= udpsock_pre_send,
984*ef8d499eSDavid van Moolenbroek 	.sop_send		= udpsock_send,
985*ef8d499eSDavid van Moolenbroek 	.sop_pre_recv		= pktsock_pre_recv,
986*ef8d499eSDavid van Moolenbroek 	.sop_recv		= pktsock_recv,
987*ef8d499eSDavid van Moolenbroek 	.sop_test_recv		= pktsock_test_recv,
988*ef8d499eSDavid van Moolenbroek 	.sop_ioctl		= ifconf_ioctl,
989*ef8d499eSDavid van Moolenbroek 	.sop_setsockmask	= udpsock_setsockmask,
990*ef8d499eSDavid van Moolenbroek 	.sop_setsockopt		= udpsock_setsockopt,
991*ef8d499eSDavid van Moolenbroek 	.sop_getsockopt		= udpsock_getsockopt,
992*ef8d499eSDavid van Moolenbroek 	.sop_getsockname	= udpsock_getsockname,
993*ef8d499eSDavid van Moolenbroek 	.sop_getpeername	= udpsock_getpeername,
994*ef8d499eSDavid van Moolenbroek 	.sop_shutdown		= udpsock_shutdown,
995*ef8d499eSDavid van Moolenbroek 	.sop_close		= udpsock_close,
996*ef8d499eSDavid van Moolenbroek 	.sop_free		= udpsock_free
997*ef8d499eSDavid van Moolenbroek };
998