gpxe import
[grub-extras.git] / gpxe / src / net / udp.c
blob771655e0629596db8ee47cd47b1ac1980c27f73c
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 xfer_init ( &udp->xfer, &udp_xfer_operations, &udp->refcnt );
114 if ( st_peer )
115 memcpy ( &udp->peer, st_peer, sizeof ( udp->peer ) );
116 if ( st_local )
117 memcpy ( &udp->local, st_local, sizeof ( udp->local ) );
119 /* Bind to local port */
120 if ( ! promisc ) {
121 if ( ( rc = udp_bind ( udp ) ) != 0 )
122 goto err;
125 /* Attach parent interface, transfer reference to connection
126 * list and return
128 xfer_plug_plug ( &udp->xfer, xfer );
129 list_add ( &udp->list, &udp_conns );
130 return 0;
132 err:
133 ref_put ( &udp->refcnt );
134 return rc;
138 * Open a UDP connection
140 * @v xfer Data transfer interface
141 * @v peer Peer socket address
142 * @v local Local socket address, or NULL
143 * @ret rc Return status code
145 int udp_open ( struct xfer_interface *xfer, struct sockaddr *peer,
146 struct sockaddr *local ) {
147 return udp_open_common ( xfer, peer, local, 0 );
151 * Open a promiscuous UDP connection
153 * @v xfer Data transfer interface
154 * @ret rc Return status code
156 * Promiscuous UDP connections are required in order to support the
157 * PXE API.
159 int udp_open_promisc ( struct xfer_interface *xfer ) {
160 return udp_open_common ( xfer, NULL, NULL, 1 );
164 * Close a UDP connection
166 * @v udp UDP connection
167 * @v rc Reason for close
169 static void udp_close ( struct udp_connection *udp, int rc ) {
171 /* Close data transfer interface */
172 xfer_nullify ( &udp->xfer );
173 xfer_close ( &udp->xfer, rc );
175 /* Remove from list of connections and drop list's reference */
176 list_del ( &udp->list );
177 ref_put ( &udp->refcnt );
179 DBGC ( udp, "UDP %p closed\n", udp );
183 * Transmit data via a UDP connection to a specified address
185 * @v udp UDP connection
186 * @v iobuf I/O buffer
187 * @v src Source address, or NULL to use default
188 * @v dest Destination address, or NULL to use default
189 * @v netdev Network device, or NULL to use default
190 * @ret rc Return status code
192 static int udp_tx ( struct udp_connection *udp, struct io_buffer *iobuf,
193 struct sockaddr_tcpip *src, struct sockaddr_tcpip *dest,
194 struct net_device *netdev ) {
195 struct udp_header *udphdr;
196 size_t len;
197 int rc;
199 /* Check we can accommodate the header */
200 if ( ( rc = iob_ensure_headroom ( iobuf, UDP_MAX_HLEN ) ) != 0 ) {
201 free_iob ( iobuf );
202 return rc;
205 /* Fill in default values if not explicitly provided */
206 if ( ! src )
207 src = &udp->local;
208 if ( ! dest )
209 dest = &udp->peer;
211 /* Add the UDP header */
212 udphdr = iob_push ( iobuf, sizeof ( *udphdr ) );
213 len = iob_len ( iobuf );
214 udphdr->dest = dest->st_port;
215 udphdr->src = src->st_port;
216 udphdr->len = htons ( len );
217 udphdr->chksum = 0;
218 udphdr->chksum = tcpip_chksum ( udphdr, len );
220 /* Dump debugging information */
221 DBGC ( udp, "UDP %p TX %d->%d len %d\n", udp,
222 ntohs ( udphdr->src ), ntohs ( udphdr->dest ),
223 ntohs ( udphdr->len ) );
225 /* Send it to the next layer for processing */
226 if ( ( rc = tcpip_tx ( iobuf, &udp_protocol, src, dest, netdev,
227 &udphdr->chksum ) ) != 0 ) {
228 DBGC ( udp, "UDP %p could not transmit packet: %s\n",
229 udp, strerror ( rc ) );
230 return rc;
233 return 0;
237 * Identify UDP connection by local address
239 * @v local Local address
240 * @ret udp UDP connection, or NULL
242 static struct udp_connection * udp_demux ( struct sockaddr_tcpip *local ) {
243 static const struct sockaddr_tcpip empty_sockaddr = { .pad = { 0, } };
244 struct udp_connection *udp;
246 list_for_each_entry ( udp, &udp_conns, list ) {
247 if ( ( ( udp->local.st_family == local->st_family ) ||
248 ( udp->local.st_family == 0 ) ) &&
249 ( ( udp->local.st_port == local->st_port ) ||
250 ( udp->local.st_port == 0 ) ) &&
251 ( ( memcmp ( udp->local.pad, local->pad,
252 sizeof ( udp->local.pad ) ) == 0 ) ||
253 ( memcmp ( udp->local.pad, empty_sockaddr.pad,
254 sizeof ( udp->local.pad ) ) == 0 ) ) ) {
255 return udp;
258 return NULL;
262 * Process a received packet
264 * @v iobuf I/O buffer
265 * @v st_src Partially-filled source address
266 * @v st_dest Partially-filled destination address
267 * @v pshdr_csum Pseudo-header checksum
268 * @ret rc Return status code
270 static int udp_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
271 struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum ) {
272 struct udp_header *udphdr = iobuf->data;
273 struct udp_connection *udp;
274 struct xfer_metadata meta;
275 size_t ulen;
276 unsigned int csum;
277 int rc = 0;
279 /* Sanity check packet */
280 if ( iob_len ( iobuf ) < sizeof ( *udphdr ) ) {
281 DBG ( "UDP packet too short at %zd bytes (min %zd bytes)\n",
282 iob_len ( iobuf ), sizeof ( *udphdr ) );
284 rc = -EINVAL;
285 goto done;
287 ulen = ntohs ( udphdr->len );
288 if ( ulen < sizeof ( *udphdr ) ) {
289 DBG ( "UDP length too short at %zd bytes "
290 "(header is %zd bytes)\n", ulen, sizeof ( *udphdr ) );
291 rc = -EINVAL;
292 goto done;
294 if ( ulen > iob_len ( iobuf ) ) {
295 DBG ( "UDP length too long at %zd bytes (packet is %zd "
296 "bytes)\n", ulen, iob_len ( iobuf ) );
297 rc = -EINVAL;
298 goto done;
300 if ( udphdr->chksum ) {
301 csum = tcpip_continue_chksum ( pshdr_csum, iobuf->data, ulen );
302 if ( csum != 0 ) {
303 DBG ( "UDP checksum incorrect (is %04x including "
304 "checksum field, should be 0000)\n", csum );
305 rc = -EINVAL;
306 goto done;
310 /* Parse parameters from header and strip header */
311 st_src->st_port = udphdr->src;
312 st_dest->st_port = udphdr->dest;
313 udp = udp_demux ( st_dest );
314 iob_unput ( iobuf, ( iob_len ( iobuf ) - ulen ) );
315 iob_pull ( iobuf, sizeof ( *udphdr ) );
317 /* Dump debugging information */
318 DBGC ( udp, "UDP %p RX %d<-%d len %zd\n", udp,
319 ntohs ( udphdr->dest ), ntohs ( udphdr->src ), ulen );
321 /* Ignore if no matching connection found */
322 if ( ! udp ) {
323 DBG ( "No UDP connection listening on port %d\n",
324 ntohs ( udphdr->dest ) );
325 rc = -ENOTCONN;
326 goto done;
329 /* Pass data to application */
330 memset ( &meta, 0, sizeof ( meta ) );
331 meta.src = ( struct sockaddr * ) st_src;
332 meta.dest = ( struct sockaddr * ) st_dest;
333 rc = xfer_deliver_iob_meta ( &udp->xfer, iob_disown ( iobuf ), &meta );
335 done:
336 free_iob ( iobuf );
337 return rc;
340 struct tcpip_protocol udp_protocol __tcpip_protocol = {
341 .name = "UDP",
342 .rx = udp_rx,
343 .tcpip_proto = IP_UDP,
346 /***************************************************************************
348 * Data transfer interface
350 ***************************************************************************
354 * Close interface
356 * @v xfer Data transfer interface
357 * @v rc Reason for close
359 static void udp_xfer_close ( struct xfer_interface *xfer, int rc ) {
360 struct udp_connection *udp =
361 container_of ( xfer, struct udp_connection, xfer );
363 /* Close connection */
364 udp_close ( udp, rc );
368 * Allocate I/O buffer for UDP
370 * @v xfer Data transfer interface
371 * @v len Payload size
372 * @ret iobuf I/O buffer, or NULL
374 static struct io_buffer * udp_alloc_iob ( struct xfer_interface *xfer,
375 size_t len ) {
376 struct udp_connection *udp =
377 container_of ( xfer, struct udp_connection, xfer );
378 struct io_buffer *iobuf;
380 iobuf = alloc_iob ( UDP_MAX_HLEN + len );
381 if ( ! iobuf ) {
382 DBGC ( udp, "UDP %p cannot allocate buffer of length %zd\n",
383 udp, len );
384 return NULL;
386 iob_reserve ( iobuf, UDP_MAX_HLEN );
387 return iobuf;
391 * Deliver datagram as I/O buffer
393 * @v xfer Data transfer interface
394 * @v iobuf Datagram I/O buffer
395 * @v meta Data transfer metadata
396 * @ret rc Return status code
398 static int udp_xfer_deliver_iob ( struct xfer_interface *xfer,
399 struct io_buffer *iobuf,
400 struct xfer_metadata *meta ) {
401 struct udp_connection *udp =
402 container_of ( xfer, struct udp_connection, xfer );
404 /* Transmit data, if possible */
405 udp_tx ( udp, iobuf, ( ( struct sockaddr_tcpip * ) meta->src ),
406 ( ( struct sockaddr_tcpip * ) meta->dest ), meta->netdev );
408 return 0;
411 /** UDP data transfer interface operations */
412 static struct xfer_interface_operations udp_xfer_operations = {
413 .close = udp_xfer_close,
414 .vredirect = ignore_xfer_vredirect,
415 .window = unlimited_xfer_window,
416 .alloc_iob = udp_alloc_iob,
417 .deliver_iob = udp_xfer_deliver_iob,
418 .deliver_raw = xfer_deliver_as_iob,
421 /***************************************************************************
423 * Openers
425 ***************************************************************************
428 /** UDP socket opener */
429 struct socket_opener udp_socket_opener __socket_opener = {
430 .semantics = UDP_SOCK_DGRAM,
431 .family = AF_INET,
432 .open = udp_open,
435 /** Linkage hack */
436 int udp_sock_dgram = UDP_SOCK_DGRAM;
439 * Open UDP URI
441 * @v xfer Data transfer interface
442 * @v uri URI
443 * @ret rc Return status code
445 static int udp_open_uri ( struct xfer_interface *xfer, struct uri *uri ) {
446 struct sockaddr_tcpip peer;
448 /* Sanity check */
449 if ( ! uri->host )
450 return -EINVAL;
452 memset ( &peer, 0, sizeof ( peer ) );
453 peer.st_port = htons ( uri_port ( uri, 0 ) );
454 return xfer_open_named_socket ( xfer, SOCK_DGRAM,
455 ( struct sockaddr * ) &peer,
456 uri->host, NULL );
459 /** UDP URI opener */
460 struct uri_opener udp_uri_opener __uri_opener = {
461 .scheme = "udp",
462 .open = udp_open_uri,