fddf81df9cb5c8225fa70a89f940866f904788fd
[gpxe.git] / src / net / udp.c
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>
13
14 /** @file
15  *
16  * UDP protocol
17  */
18
19 /**
20  * A UDP connection
21  *
22  */
23 struct udp_connection {
24         /** Reference counter */
25         struct refcnt refcnt;
26         /** List of UDP connections */
27         struct list_head list;
28
29         /** Data transfer interface */
30         struct xfer_interface xfer;
31
32         /** Local socket address */
33         struct sockaddr_tcpip local;
34         /** Remote socket address */
35         struct sockaddr_tcpip peer;
36 };
37
38 /**
39  * List of registered UDP connections
40  */
41 static LIST_HEAD ( udp_conns );
42
43 /* Forward declatations */
44 static struct xfer_interface_operations udp_xfer_operations;
45 struct tcpip_protocol udp_protocol;
46
47 /**
48  * Bind UDP connection to local port
49  *
50  * @v udp               UDP connection
51  * @ret rc              Return status code
52  *
53  * Opens the UDP connection and binds to the specified local port.  If
54  * no local port is specified, the first available port will be used.
55  */
56 static int udp_bind ( struct udp_connection *udp ) {
57         struct udp_connection *existing;
58         static uint16_t try_port = 1023;
59
60         /* If no port specified, find the first available port */
61         if ( ! udp->local.st_port ) {
62                 while ( try_port ) {
63                         try_port++;
64                         if ( try_port < 1024 )
65                                 continue;
66                         udp->local.st_port = htons ( try_port );
67                         if ( udp_bind ( udp ) == 0 )
68                                 return 0;
69                 }
70                 return -EADDRINUSE;
71         }
72
73         /* Attempt bind to local port */
74         list_for_each_entry ( existing, &udp_conns, list ) {
75                 if ( existing->local.st_port == udp->local.st_port ) {
76                         DBGC ( udp, "UDP %p could not bind: port %d in use\n",
77                                udp, ntohs ( udp->local.st_port ) );
78                         return -EADDRINUSE;
79                 }
80         }
81
82         /* Add to UDP connection list */
83         DBGC ( udp, "UDP %p bound to port %d\n",
84                udp, ntohs ( udp->local.st_port ) );
85
86         return 0;
87 }
88
89 /**
90  * Open a UDP connection
91  *
92  * @v xfer              Data transfer interface
93  * @v peer              Peer socket address, or NULL
94  * @v local             Local socket address, or NULL
95  * @v promisc           Socket is promiscuous
96  * @ret rc              Return status code
97  */
98 static int udp_open_common ( struct xfer_interface *xfer,
99                              struct sockaddr *peer, struct sockaddr *local,
100                              int promisc ) {
101         struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer;
102         struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local;
103         struct udp_connection *udp;
104         int rc;
105
106         /* Allocate and initialise structure */
107         udp = zalloc ( sizeof ( *udp ) );
108         if ( ! udp )
109                 return -ENOMEM;
110         DBGC ( udp, "UDP %p allocated\n", udp );
111         xfer_init ( &udp->xfer, &udp_xfer_operations, &udp->refcnt );
112         if ( st_peer )
113                 memcpy ( &udp->peer, st_peer, sizeof ( udp->peer ) );
114         if ( st_local )
115                 memcpy ( &udp->local, st_local, sizeof ( udp->local ) );
116
117         /* Bind to local port */
118         if ( ! promisc ) {
119                 if ( ( rc = udp_bind ( udp ) ) != 0 )
120                         goto err;
121         }
122
123         /* Attach parent interface, transfer reference to connection
124          * list and return
125          */
126         xfer_plug_plug ( &udp->xfer, xfer );
127         list_add ( &udp->list, &udp_conns );
128         return 0;
129
130  err:
131         ref_put ( &udp->refcnt );
132         return rc;
133 }
134
135 /**
136  * Open a UDP connection
137  *
138  * @v xfer              Data transfer interface
139  * @v peer              Peer socket address
140  * @v local             Local socket address, or NULL
141  * @ret rc              Return status code
142  */
143 int udp_open ( struct xfer_interface *xfer, struct sockaddr *peer,
144                struct sockaddr *local ) {
145         return udp_open_common ( xfer, peer, local, 0 );
146 }
147
148 /**
149  * Open a promiscuous UDP connection
150  *
151  * @v xfer              Data transfer interface
152  * @ret rc              Return status code
153  *
154  * Promiscuous UDP connections are required in order to support the
155  * PXE API.
156  */
157 int udp_open_promisc ( struct xfer_interface *xfer ) {
158         return udp_open_common ( xfer, NULL, NULL, 1 );
159 }
160
161 /**
162  * Close a UDP connection
163  *
164  * @v udp               UDP connection
165  * @v rc                Reason for close
166  */
167 static void udp_close ( struct udp_connection *udp, int rc ) {
168
169         /* Close data transfer interface */
170         xfer_nullify ( &udp->xfer );
171         xfer_close ( &udp->xfer, rc );
172
173         /* Remove from list of connections and drop list's reference */
174         list_del ( &udp->list );
175         ref_put ( &udp->refcnt );
176
177         DBGC ( udp, "UDP %p closed\n", udp );
178 }
179
180 /**
181  * Transmit data via a UDP connection to a specified address
182  *
183  * @v udp               UDP connection
184  * @v iobuf             I/O buffer
185  * @v src_port          Source port, or 0 to use default
186  * @v dest              Destination address, or NULL to use default
187  * @v netdev            Network device, or NULL to use default
188  * @ret rc              Return status code
189  */
190 static int udp_tx ( struct udp_connection *udp, struct io_buffer *iobuf,
191                     unsigned int src_port, struct sockaddr_tcpip *dest,
192                     struct net_device *netdev ) {
193         struct udp_header *udphdr;
194         size_t len;
195         int rc;
196
197         /* Check we can accommodate the header */
198         if ( ( rc = iob_ensure_headroom ( iobuf, UDP_MAX_HLEN ) ) != 0 ) {
199                 free_iob ( iobuf );
200                 return rc;
201         }
202
203         /* Fill in default values if not explicitly provided */
204         if ( ! src_port )
205                 src_port = udp->local.st_port;
206         if ( ! dest )
207                 dest = &udp->peer;
208
209         /* Add the UDP header */
210         udphdr = iob_push ( iobuf, sizeof ( *udphdr ) );
211         len = iob_len ( iobuf );
212         udphdr->dest = dest->st_port;
213         udphdr->src = src_port;
214         udphdr->len = htons ( len );
215         udphdr->chksum = 0;
216         udphdr->chksum = tcpip_chksum ( udphdr, len );
217
218         /* Dump debugging information */
219         DBGC ( udp, "UDP %p TX %d->%d len %d\n", udp,
220                ntohs ( udphdr->src ), ntohs ( udphdr->dest ),
221                ntohs ( udphdr->len ) );
222
223         /* Send it to the next layer for processing */
224         if ( ( rc = tcpip_tx ( iobuf, &udp_protocol, dest, netdev,
225                                &udphdr->chksum ) ) != 0 ) {
226                 DBGC ( udp, "UDP %p could not transmit packet: %s\n",
227                        udp, strerror ( rc ) );
228                 return rc;
229         }
230
231         return 0;
232 }
233
234 /**
235  * Identify UDP connection by local address
236  *
237  * @v local             Local address
238  * @ret udp             UDP connection, or NULL
239  */
240 static struct udp_connection * udp_demux ( struct sockaddr_tcpip *local ) {
241         static const struct sockaddr_tcpip empty_sockaddr;
242         struct udp_connection *udp;
243
244         list_for_each_entry ( udp, &udp_conns, list ) {
245                 if ( ( ( udp->local.st_family == local->st_family ) ||
246                        ( udp->local.st_family == 0 ) ) &&
247                      ( ( udp->local.st_port == local->st_port ) ||
248                        ( udp->local.st_port == 0 ) ) &&
249                      ( ( memcmp ( udp->local.pad, local->pad,
250                                   sizeof ( udp->local.pad ) ) == 0 ) ||
251                        ( memcmp ( udp->local.pad, empty_sockaddr.pad,
252                                   sizeof ( udp->local.pad ) ) == 0 ) ) ) {
253                         return udp;
254                 }
255         }
256         return NULL;
257 }
258
259 /**
260  * Process a received packet
261  *
262  * @v iobuf             I/O buffer
263  * @v st_src            Partially-filled source address
264  * @v st_dest           Partially-filled destination address
265  * @v pshdr_csum        Pseudo-header checksum
266  * @ret rc              Return status code
267  */
268 static int udp_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
269                     struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum ) {
270         struct udp_header *udphdr = iobuf->data;
271         struct udp_connection *udp;
272         struct xfer_metadata meta;
273         size_t ulen;
274         unsigned int csum;
275         int rc = 0;
276
277         /* Sanity check packet */
278         if ( iob_len ( iobuf ) < sizeof ( *udphdr ) ) {
279                 DBG ( "UDP packet too short at %zd bytes (min %zd bytes)\n",
280                       iob_len ( iobuf ), sizeof ( *udphdr ) );
281                 
282                 rc = -EINVAL;
283                 goto done;
284         }
285         ulen = ntohs ( udphdr->len );
286         if ( ulen < sizeof ( *udphdr ) ) {
287                 DBG ( "UDP length too short at %zd bytes "
288                       "(header is %zd bytes)\n", ulen, sizeof ( *udphdr ) );
289                 rc = -EINVAL;
290                 goto done;
291         }
292         if ( ulen > iob_len ( iobuf ) ) {
293                 DBG ( "UDP length too long at %zd bytes (packet is %zd "
294                       "bytes)\n", ulen, iob_len ( iobuf ) );
295                 rc = -EINVAL;
296                 goto done;
297         }
298         if ( udphdr->chksum ) {
299                 csum = tcpip_continue_chksum ( pshdr_csum, iobuf->data, ulen );
300                 if ( csum != 0 ) {
301                         DBG ( "UDP checksum incorrect (is %04x including "
302                               "checksum field, should be 0000)\n", csum );
303                         rc = -EINVAL;
304                         goto done;
305                 }
306         }
307
308         /* Parse parameters from header and strip header */
309         st_src->st_port = udphdr->src;
310         st_dest->st_port = udphdr->dest;
311         udp = udp_demux ( st_dest );
312         iob_unput ( iobuf, ( iob_len ( iobuf ) - ulen ) );
313         iob_pull ( iobuf, sizeof ( *udphdr ) );
314
315         /* Dump debugging information */
316         DBGC ( udp, "UDP %p RX %d<-%d len %zd\n", udp,
317                ntohs ( udphdr->dest ), ntohs ( udphdr->src ), ulen );
318
319         /* Ignore if no matching connection found */
320         if ( ! udp ) {
321                 DBG ( "No UDP connection listening on port %d\n",
322                       ntohs ( udphdr->dest ) );
323                 rc = -ENOTCONN;
324                 goto done;
325         }
326
327         /* Pass data to application */
328         memset ( &meta, 0, sizeof ( meta ) );
329         meta.src = ( struct sockaddr * ) st_src;
330         meta.dest = ( struct sockaddr * ) st_dest;
331         rc = xfer_deliver_iob_meta ( &udp->xfer, iobuf, &meta );
332         iobuf = NULL;
333
334  done:
335         free_iob ( iobuf );
336         return rc;
337 }
338
339 struct tcpip_protocol udp_protocol __tcpip_protocol = {
340         .name = "UDP",
341         .rx = udp_rx,
342         .tcpip_proto = IP_UDP,
343 };
344
345 /***************************************************************************
346  *
347  * Data transfer interface
348  *
349  ***************************************************************************
350  */
351
352 /**
353  * Close interface
354  *
355  * @v xfer              Data transfer interface
356  * @v rc                Reason for close
357  */
358 static void udp_xfer_close ( struct xfer_interface *xfer, int rc ) {
359         struct udp_connection *udp =
360                 container_of ( xfer, struct udp_connection, xfer );
361
362         /* Close connection */
363         udp_close ( udp, rc );
364 }
365
366 /**
367  * Allocate I/O buffer for UDP
368  *
369  * @v xfer              Data transfer interface
370  * @v len               Payload size
371  * @ret iobuf           I/O buffer, or NULL
372  */
373 static struct io_buffer * udp_alloc_iob ( struct xfer_interface *xfer,
374                                           size_t len ) {
375         struct udp_connection *udp =
376                 container_of ( xfer, struct udp_connection, xfer );     
377         struct io_buffer *iobuf;
378
379         iobuf = alloc_iob ( UDP_MAX_HLEN + len );
380         if ( ! iobuf ) {
381                 DBGC ( udp, "UDP %p cannot allocate buffer of length %zd\n",
382                        udp, len );
383                 return NULL;
384         }
385         iob_reserve ( iobuf, UDP_MAX_HLEN );
386         return iobuf;
387 }
388
389 /**
390  * Deliver datagram as I/O buffer
391  *
392  * @v xfer              Data transfer interface
393  * @v iobuf             Datagram I/O buffer
394  * @v meta              Data transfer metadata, or NULL
395  * @ret rc              Return status code
396  */
397 static int udp_xfer_deliver_iob ( struct xfer_interface *xfer,
398                                   struct io_buffer *iobuf,
399                                   struct xfer_metadata *meta ) {
400         struct udp_connection *udp =
401                 container_of ( xfer, struct udp_connection, xfer );
402         struct sockaddr_tcpip *src;
403         struct sockaddr_tcpip *dest = NULL;
404         struct net_device *netdev = NULL;
405         unsigned int src_port = 0;
406
407         /* Apply xfer metadata */
408         if ( meta ) {
409                 src = ( struct sockaddr_tcpip * ) meta->src;
410                 if ( src )
411                         src_port = src->st_port;
412                 dest = ( struct sockaddr_tcpip * ) meta->dest;
413                 netdev = meta->netdev;
414         }
415
416         /* Transmit data, if possible */
417         udp_tx ( udp, iobuf, src_port, dest, netdev );
418
419         return 0;
420 }
421
422 /** UDP data transfer interface operations */
423 static struct xfer_interface_operations udp_xfer_operations = {
424         .close          = udp_xfer_close,
425         .vredirect      = ignore_xfer_vredirect,
426         .window         = unlimited_xfer_window,
427         .alloc_iob      = udp_alloc_iob,
428         .deliver_iob    = udp_xfer_deliver_iob,
429         .deliver_raw    = xfer_deliver_as_iob,
430 };
431
432 /***************************************************************************
433  *
434  * Openers
435  *
436  ***************************************************************************
437  */
438
439 /** UDP socket opener */
440 struct socket_opener udp_socket_opener __socket_opener = {
441         .semantics      = UDP_SOCK_DGRAM,
442         .family         = AF_INET,
443         .open           = udp_open,
444 };
445
446 /** Linkage hack */
447 int udp_sock_dgram = UDP_SOCK_DGRAM;
448
449 /**
450  * Open UDP URI
451  *
452  * @v xfer              Data transfer interface
453  * @v uri               URI
454  * @ret rc              Return status code
455  */
456 static int udp_open_uri ( struct xfer_interface *xfer, struct uri *uri ) {
457         struct sockaddr_tcpip peer;
458
459         /* Sanity check */
460         if ( ! uri->host )
461                 return -EINVAL;
462
463         memset ( &peer, 0, sizeof ( peer ) );
464         peer.st_port = htons ( uri_port ( uri, 0 ) );
465         return xfer_open_named_socket ( xfer, SOCK_DGRAM,
466                                         ( struct sockaddr * ) &peer,
467                                         uri->host, NULL );
468 }
469
470 /** UDP URI opener */
471 struct uri_opener udp_uri_opener __uri_opener = {
472         .scheme         = "udp",
473         .open           = udp_open_uri,
474 };