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