[ipv6] Enable router solicitations to timeout
[gpxe.git] / src / net / udp.c
blob2c63ea234d412fe8776dd422f4db358cd6c7c149
1 #include <stdint.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <assert.h>
5 #include <byteswap.h>
6 #include <errno.h>
7 #include <gpxe/tcpip.h>
8 #include <gpxe/iobuf.h>
9 #include <gpxe/xfer.h>
10 #include <gpxe/open.h>
11 #include <gpxe/uri.h>
12 #include <gpxe/udp.h>
14 /** @file
16 * UDP protocol
19 FILE_LICENCE ( GPL2_OR_LATER );
21 /**
22 * A UDP connection
25 struct udp_connection {
26 /** Reference counter */
27 struct refcnt refcnt;
28 /** List of UDP connections */
29 struct list_head list;
31 /** Data transfer interface */
32 struct xfer_interface xfer;
34 /** Local socket address */
35 struct sockaddr_tcpip local;
36 /** Remote socket address */
37 struct sockaddr_tcpip peer;
40 /**
41 * List of registered UDP connections
43 static LIST_HEAD ( udp_conns );
45 /* Forward declatations */
46 static struct xfer_interface_operations udp_xfer_operations;
47 struct tcpip_protocol udp_protocol;
49 /**
50 * Bind UDP connection to local port
52 * @v udp UDP connection
53 * @ret rc Return status code
55 * Opens the UDP connection and binds to the specified local port. If
56 * no local port is specified, the first available port will be used.
58 static int udp_bind ( struct udp_connection *udp ) {
59 struct udp_connection *existing;
60 static uint16_t try_port = 1023;
62 /* If no port specified, find the first available port */
63 if ( ! udp->local.st_port ) {
64 while ( try_port ) {
65 try_port++;
66 if ( try_port < 1024 )
67 continue;
68 udp->local.st_port = htons ( try_port );
69 if ( udp_bind ( udp ) == 0 )
70 return 0;
72 return -EADDRINUSE;
75 /* Attempt bind to local port */
76 list_for_each_entry ( existing, &udp_conns, list ) {
77 if ( existing->local.st_port == udp->local.st_port ) {
78 DBGC ( udp, "UDP %p could not bind: port %d in use\n",
79 udp, ntohs ( udp->local.st_port ) );
80 return -EADDRINUSE;
84 /* Add to UDP connection list */
85 DBGC ( udp, "UDP %p bound to port %d\n",
86 udp, ntohs ( udp->local.st_port ) );
88 return 0;
91 /**
92 * Open a UDP connection
94 * @v xfer Data transfer interface
95 * @v peer Peer socket address, or NULL
96 * @v local Local socket address, or NULL
97 * @v promisc Socket is promiscuous
98 * @ret rc Return status code
100 static int udp_open_common ( struct xfer_interface *xfer,
101 struct sockaddr *peer, struct sockaddr *local,
102 int promisc ) {
103 struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer;
104 struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local;
105 struct udp_connection *udp;
106 int rc;
108 /* Allocate and initialise structure */
109 udp = zalloc ( sizeof ( *udp ) );
110 if ( ! udp )
111 return -ENOMEM;
112 DBGC ( udp, "UDP %p allocated\n", udp );
113 ref_init ( &udp->refcnt, NULL );
114 xfer_init ( &udp->xfer, &udp_xfer_operations, &udp->refcnt );
115 if ( st_peer )
116 memcpy ( &udp->peer, st_peer, sizeof ( udp->peer ) );
117 if ( st_local )
118 memcpy ( &udp->local, st_local, sizeof ( udp->local ) );
120 /* Bind to local port */
121 if ( ! promisc ) {
122 if ( ( rc = udp_bind ( udp ) ) != 0 )
123 goto err;
126 /* Attach parent interface, transfer reference to connection
127 * list and return
129 xfer_plug_plug ( &udp->xfer, xfer );
130 list_add ( &udp->list, &udp_conns );
131 return 0;
133 err:
134 ref_put ( &udp->refcnt );
135 return rc;
139 * Open a UDP connection
141 * @v xfer Data transfer interface
142 * @v peer Peer socket address
143 * @v local Local socket address, or NULL
144 * @ret rc Return status code
146 int udp_open ( struct xfer_interface *xfer, struct sockaddr *peer,
147 struct sockaddr *local ) {
148 return udp_open_common ( xfer, peer, local, 0 );
152 * Open a promiscuous UDP connection
154 * @v xfer Data transfer interface
155 * @ret rc Return status code
157 * Promiscuous UDP connections are required in order to support the
158 * PXE API.
160 int udp_open_promisc ( struct xfer_interface *xfer ) {
161 return udp_open_common ( xfer, NULL, NULL, 1 );
165 * Close a UDP connection
167 * @v udp UDP connection
168 * @v rc Reason for close
170 static void udp_close ( struct udp_connection *udp, int rc ) {
172 /* Close data transfer interface */
173 xfer_nullify ( &udp->xfer );
174 xfer_close ( &udp->xfer, rc );
176 /* Remove from list of connections and drop list's reference */
177 list_del ( &udp->list );
178 ref_put ( &udp->refcnt );
180 DBGC ( udp, "UDP %p closed\n", udp );
184 * Transmit data via a UDP connection to a specified address
186 * @v udp UDP connection
187 * @v iobuf I/O buffer
188 * @v src Source address, or NULL to use default
189 * @v dest Destination address, or NULL to use default
190 * @v netdev Network device, or NULL to use default
191 * @ret rc Return status code
193 static int udp_tx ( struct udp_connection *udp, struct io_buffer *iobuf,
194 struct sockaddr_tcpip *src, struct sockaddr_tcpip *dest,
195 struct net_device *netdev ) {
196 struct udp_header *udphdr;
197 size_t len;
198 int rc;
200 /* Check we can accommodate the header */
201 if ( ( rc = iob_ensure_headroom ( iobuf, UDP_MAX_HLEN ) ) != 0 ) {
202 free_iob ( iobuf );
203 return rc;
206 /* Fill in default values if not explicitly provided */
207 if ( ! src )
208 src = &udp->local;
209 if ( ! dest )
210 dest = &udp->peer;
212 /* Add the UDP header */
213 udphdr = iob_push ( iobuf, sizeof ( *udphdr ) );
214 len = iob_len ( iobuf );
215 udphdr->dest = dest->st_port;
216 udphdr->src = src->st_port;
217 udphdr->len = htons ( len );
218 udphdr->chksum = 0;
219 udphdr->chksum = tcpip_chksum ( udphdr, len );
221 /* Dump debugging information */
222 DBGC ( udp, "UDP %p TX %d->%d len %d\n", udp,
223 ntohs ( udphdr->src ), ntohs ( udphdr->dest ),
224 ntohs ( udphdr->len ) );
226 /* Send it to the next layer for processing */
227 if ( ( rc = tcpip_tx ( iobuf, &udp_protocol, src, dest, netdev,
228 &udphdr->chksum ) ) != 0 ) {
229 DBGC ( udp, "UDP %p could not transmit packet: %s\n",
230 udp, strerror ( rc ) );
231 return rc;
234 return 0;
238 * Identify UDP connection by local address
240 * @v local Local address
241 * @ret udp UDP connection, or NULL
243 static struct udp_connection * udp_demux ( struct sockaddr_tcpip *local ) {
244 static const struct sockaddr_tcpip empty_sockaddr = { .pad = { 0, } };
245 struct udp_connection *udp;
247 list_for_each_entry ( udp, &udp_conns, list ) {
248 if ( ( ( udp->local.st_family == local->st_family ) ||
249 ( udp->local.st_family == 0 ) ) &&
250 ( ( udp->local.st_port == local->st_port ) ||
251 ( udp->local.st_port == 0 ) ) &&
252 ( ( memcmp ( udp->local.pad, local->pad,
253 sizeof ( udp->local.pad ) ) == 0 ) ||
254 ( memcmp ( udp->local.pad, empty_sockaddr.pad,
255 sizeof ( udp->local.pad ) ) == 0 ) ) ) {
256 return udp;
259 return NULL;
263 * Process a received packet
265 * @v iobuf I/O buffer
266 * @v st_src Partially-filled source address
267 * @v st_dest Partially-filled destination address
268 * @v pshdr_csum Pseudo-header checksum
269 * @ret rc Return status code
271 static int udp_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
272 struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum ) {
273 struct udp_header *udphdr = iobuf->data;
274 struct udp_connection *udp;
275 struct xfer_metadata meta;
276 size_t ulen;
277 unsigned int csum;
278 int rc = 0;
280 /* Sanity check packet */
281 if ( iob_len ( iobuf ) < sizeof ( *udphdr ) ) {
282 DBG ( "UDP packet too short at %zd bytes (min %zd bytes)\n",
283 iob_len ( iobuf ), sizeof ( *udphdr ) );
285 rc = -EINVAL;
286 goto done;
288 ulen = ntohs ( udphdr->len );
289 if ( ulen < sizeof ( *udphdr ) ) {
290 DBG ( "UDP length too short at %zd bytes "
291 "(header is %zd bytes)\n", ulen, sizeof ( *udphdr ) );
292 rc = -EINVAL;
293 goto done;
295 if ( ulen > iob_len ( iobuf ) ) {
296 DBG ( "UDP length too long at %zd bytes (packet is %zd "
297 "bytes)\n", ulen, iob_len ( iobuf ) );
298 rc = -EINVAL;
299 goto done;
301 if ( udphdr->chksum ) {
302 csum = tcpip_continue_chksum ( pshdr_csum, iobuf->data, ulen );
303 if ( csum != 0 ) {
304 DBG ( "UDP checksum incorrect (is %04x including "
305 "checksum field, should be 0000)\n", csum );
306 rc = -EINVAL;
307 goto done;
311 /* Parse parameters from header and strip header */
312 st_src->st_port = udphdr->src;
313 st_dest->st_port = udphdr->dest;
314 udp = udp_demux ( st_dest );
315 iob_unput ( iobuf, ( iob_len ( iobuf ) - ulen ) );
316 iob_pull ( iobuf, sizeof ( *udphdr ) );
318 /* Dump debugging information */
319 DBGC ( udp, "UDP %p RX %d<-%d len %zd\n", udp,
320 ntohs ( udphdr->dest ), ntohs ( udphdr->src ), ulen );
322 /* Ignore if no matching connection found */
323 if ( ! udp ) {
324 DBG ( "No UDP connection listening on port %d\n",
325 ntohs ( udphdr->dest ) );
326 rc = -ENOTCONN;
327 goto done;
330 /* Pass data to application */
331 memset ( &meta, 0, sizeof ( meta ) );
332 meta.src = ( struct sockaddr * ) st_src;
333 meta.dest = ( struct sockaddr * ) st_dest;
334 rc = xfer_deliver_iob_meta ( &udp->xfer, iob_disown ( iobuf ), &meta );
336 done:
337 free_iob ( iobuf );
338 return rc;
341 struct tcpip_protocol udp_protocol __tcpip_protocol = {
342 .name = "UDP",
343 .rx = udp_rx,
344 .tcpip_proto = IP_UDP,
347 /***************************************************************************
349 * Data transfer interface
351 ***************************************************************************
355 * Close interface
357 * @v xfer Data transfer interface
358 * @v rc Reason for close
360 static void udp_xfer_close ( struct xfer_interface *xfer, int rc ) {
361 struct udp_connection *udp =
362 container_of ( xfer, struct udp_connection, xfer );
364 /* Close connection */
365 udp_close ( udp, rc );
369 * Allocate I/O buffer for UDP
371 * @v xfer Data transfer interface
372 * @v len Payload size
373 * @ret iobuf I/O buffer, or NULL
375 static struct io_buffer * udp_alloc_iob ( struct xfer_interface *xfer,
376 size_t len ) {
377 struct udp_connection *udp =
378 container_of ( xfer, struct udp_connection, xfer );
379 struct io_buffer *iobuf;
381 iobuf = alloc_iob ( UDP_MAX_HLEN + len );
382 if ( ! iobuf ) {
383 DBGC ( udp, "UDP %p cannot allocate buffer of length %zd\n",
384 udp, len );
385 return NULL;
387 iob_reserve ( iobuf, UDP_MAX_HLEN );
388 return iobuf;
392 * Deliver datagram as I/O buffer
394 * @v xfer Data transfer interface
395 * @v iobuf Datagram I/O buffer
396 * @v meta Data transfer metadata
397 * @ret rc Return status code
399 static int udp_xfer_deliver_iob ( struct xfer_interface *xfer,
400 struct io_buffer *iobuf,
401 struct xfer_metadata *meta ) {
402 struct udp_connection *udp =
403 container_of ( xfer, struct udp_connection, xfer );
405 /* Transmit data, if possible */
406 udp_tx ( udp, iobuf, ( ( struct sockaddr_tcpip * ) meta->src ),
407 ( ( struct sockaddr_tcpip * ) meta->dest ), meta->netdev );
409 return 0;
412 /** UDP data transfer interface operations */
413 static struct xfer_interface_operations udp_xfer_operations = {
414 .close = udp_xfer_close,
415 .vredirect = ignore_xfer_vredirect,
416 .window = unlimited_xfer_window,
417 .alloc_iob = udp_alloc_iob,
418 .deliver_iob = udp_xfer_deliver_iob,
419 .deliver_raw = xfer_deliver_as_iob,
422 /***************************************************************************
424 * Openers
426 ***************************************************************************
429 /** UDP socket opener */
430 struct socket_opener udp_socket_opener __socket_opener = {
431 .semantics = UDP_SOCK_DGRAM,
432 .family = AF_INET,
433 .open = udp_open,
436 /** UDP over IPv6 socket opener */
437 struct socket_opener udp_socket_opener_v6 __socket_opener = {
438 .semantics = UDP_SOCK_DGRAM,
439 .family = AF_INET6,
440 .open = udp_open,
443 /** Linkage hack */
444 int udp_sock_dgram = UDP_SOCK_DGRAM;
447 * Open UDP URI
449 * @v xfer Data transfer interface
450 * @v uri URI
451 * @ret rc Return status code
453 static int udp_open_uri ( struct xfer_interface *xfer, struct uri *uri ) {
454 struct sockaddr_tcpip peer;
456 /* Sanity check */
457 if ( ! uri->host )
458 return -EINVAL;
460 memset ( &peer, 0, sizeof ( peer ) );
461 peer.st_port = htons ( uri_port ( uri, 0 ) );
462 return xfer_open_named_socket ( xfer, SOCK_DGRAM,
463 ( struct sockaddr * ) &peer,
464 uri->host, NULL );
467 /** UDP URI opener */
468 struct uri_opener udp_uri_opener __uri_opener = {
469 .scheme = "udp",
470 .open = udp_open_uri,