source: bootcd/isolinux/syslinux-6.03/gpxe/src/net/udp.c @ 7b6b7ba

Last change on this file since 7b6b7ba was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 11.5 KB
Line 
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
19FILE_LICENCE ( GPL2_OR_LATER );
20
21/**
22 * A UDP connection
23 *
24 */
25struct udp_connection {
26        /** Reference counter */
27        struct refcnt refcnt;
28        /** List of UDP connections */
29        struct list_head list;
30
31        /** Data transfer interface */
32        struct xfer_interface xfer;
33
34        /** Local socket address */
35        struct sockaddr_tcpip local;
36        /** Remote socket address */
37        struct sockaddr_tcpip peer;
38};
39
40/**
41 * List of registered UDP connections
42 */
43static LIST_HEAD ( udp_conns );
44
45/* Forward declatations */
46static struct xfer_interface_operations udp_xfer_operations;
47struct tcpip_protocol udp_protocol;
48
49/**
50 * Bind UDP connection to local port
51 *
52 * @v udp               UDP connection
53 * @ret rc              Return status code
54 *
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.
57 */
58static int udp_bind ( struct udp_connection *udp ) {
59        struct udp_connection *existing;
60        static uint16_t try_port = 1023;
61
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;
71                }
72                return -EADDRINUSE;
73        }
74
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;
81                }
82        }
83
84        /* Add to UDP connection list */
85        DBGC ( udp, "UDP %p bound to port %d\n",
86               udp, ntohs ( udp->local.st_port ) );
87
88        return 0;
89}
90
91/**
92 * Open a UDP connection
93 *
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
99 */
100static 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;
107
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 ) );
118
119        /* Bind to local port */
120        if ( ! promisc ) {
121                if ( ( rc = udp_bind ( udp ) ) != 0 )
122                        goto err;
123        }
124
125        /* Attach parent interface, transfer reference to connection
126         * list and return
127         */
128        xfer_plug_plug ( &udp->xfer, xfer );
129        list_add ( &udp->list, &udp_conns );
130        return 0;
131
132 err:
133        ref_put ( &udp->refcnt );
134        return rc;
135}
136
137/**
138 * Open a UDP connection
139 *
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
144 */
145int udp_open ( struct xfer_interface *xfer, struct sockaddr *peer,
146               struct sockaddr *local ) {
147        return udp_open_common ( xfer, peer, local, 0 );
148}
149
150/**
151 * Open a promiscuous UDP connection
152 *
153 * @v xfer              Data transfer interface
154 * @ret rc              Return status code
155 *
156 * Promiscuous UDP connections are required in order to support the
157 * PXE API.
158 */
159int udp_open_promisc ( struct xfer_interface *xfer ) {
160        return udp_open_common ( xfer, NULL, NULL, 1 );
161}
162
163/**
164 * Close a UDP connection
165 *
166 * @v udp               UDP connection
167 * @v rc                Reason for close
168 */
169static void udp_close ( struct udp_connection *udp, int rc ) {
170
171        /* Close data transfer interface */
172        xfer_nullify ( &udp->xfer );
173        xfer_close ( &udp->xfer, rc );
174
175        /* Remove from list of connections and drop list's reference */
176        list_del ( &udp->list );
177        ref_put ( &udp->refcnt );
178
179        DBGC ( udp, "UDP %p closed\n", udp );
180}
181
182/**
183 * Transmit data via a UDP connection to a specified address
184 *
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
191 */
192static 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;
198
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;
203        }
204
205        /* Fill in default values if not explicitly provided */
206        if ( ! src )
207                src = &udp->local;
208        if ( ! dest )
209                dest = &udp->peer;
210
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 );
219
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 ) );
224
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;
231        }
232
233        return 0;
234}
235
236/**
237 * Identify UDP connection by local address
238 *
239 * @v local             Local address
240 * @ret udp             UDP connection, or NULL
241 */
242static struct udp_connection * udp_demux ( struct sockaddr_tcpip *local ) {
243        static const struct sockaddr_tcpip empty_sockaddr = { .pad = { 0, } };
244        struct udp_connection *udp;
245
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;
256                }
257        }
258        return NULL;
259}
260
261/**
262 * Process a received packet
263 *
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
269 */
270static 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;
278
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 ) );
283               
284                rc = -EINVAL;
285                goto done;
286        }
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;
293        }
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;
299        }
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;
307                }
308        }
309
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 ) );
316
317        /* Dump debugging information */
318        DBGC ( udp, "UDP %p RX %d<-%d len %zd\n", udp,
319               ntohs ( udphdr->dest ), ntohs ( udphdr->src ), ulen );
320
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;
327        }
328
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 );
334
335 done:
336        free_iob ( iobuf );
337        return rc;
338}
339
340struct tcpip_protocol udp_protocol __tcpip_protocol = {
341        .name = "UDP",
342        .rx = udp_rx,
343        .tcpip_proto = IP_UDP,
344};
345
346/***************************************************************************
347 *
348 * Data transfer interface
349 *
350 ***************************************************************************
351 */
352
353/**
354 * Close interface
355 *
356 * @v xfer              Data transfer interface
357 * @v rc                Reason for close
358 */
359static void udp_xfer_close ( struct xfer_interface *xfer, int rc ) {
360        struct udp_connection *udp =
361                container_of ( xfer, struct udp_connection, xfer );
362
363        /* Close connection */
364        udp_close ( udp, rc );
365}
366
367/**
368 * Allocate I/O buffer for UDP
369 *
370 * @v xfer              Data transfer interface
371 * @v len               Payload size
372 * @ret iobuf           I/O buffer, or NULL
373 */
374static 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;
379
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;
385        }
386        iob_reserve ( iobuf, UDP_MAX_HLEN );
387        return iobuf;
388}
389
390/**
391 * Deliver datagram as I/O buffer
392 *
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
397 */
398static 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 );
403
404        /* Transmit data, if possible */
405        udp_tx ( udp, iobuf, ( ( struct sockaddr_tcpip * ) meta->src ),
406                 ( ( struct sockaddr_tcpip * ) meta->dest ), meta->netdev );
407
408        return 0;
409}
410
411/** UDP data transfer interface operations */
412static 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,
419};
420
421/***************************************************************************
422 *
423 * Openers
424 *
425 ***************************************************************************
426 */
427
428/** UDP socket opener */
429struct socket_opener udp_socket_opener __socket_opener = {
430        .semantics      = UDP_SOCK_DGRAM,
431        .family         = AF_INET,
432        .open           = udp_open,
433};
434
435/** Linkage hack */
436int udp_sock_dgram = UDP_SOCK_DGRAM;
437
438/**
439 * Open UDP URI
440 *
441 * @v xfer              Data transfer interface
442 * @v uri               URI
443 * @ret rc              Return status code
444 */
445static int udp_open_uri ( struct xfer_interface *xfer, struct uri *uri ) {
446        struct sockaddr_tcpip peer;
447
448        /* Sanity check */
449        if ( ! uri->host )
450                return -EINVAL;
451
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 );
457}
458
459/** UDP URI opener */
460struct uri_opener udp_uri_opener __uri_opener = {
461        .scheme         = "udp",
462        .open           = udp_open_uri,
463};
Note: See TracBrowser for help on using the repository browser.