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