iPXE
udp.c
Go to the documentation of this file.
00001 #include <stdint.h>
00002 #include <stdlib.h>
00003 #include <string.h>
00004 #include <assert.h>
00005 #include <byteswap.h>
00006 #include <errno.h>
00007 #include <ipxe/tcpip.h>
00008 #include <ipxe/iobuf.h>
00009 #include <ipxe/xfer.h>
00010 #include <ipxe/open.h>
00011 #include <ipxe/uri.h>
00012 #include <ipxe/netdevice.h>
00013 #include <ipxe/udp.h>
00014 
00015 /** @file
00016  *
00017  * UDP protocol
00018  */
00019 
00020 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00021 
00022 /**
00023  * A UDP connection
00024  *
00025  */
00026 struct udp_connection {
00027         /** Reference counter */
00028         struct refcnt refcnt;
00029         /** List of UDP connections */
00030         struct list_head list;
00031 
00032         /** Data transfer interface */
00033         struct interface xfer;
00034 
00035         /** Local socket address */
00036         struct sockaddr_tcpip local;
00037         /** Remote socket address */
00038         struct sockaddr_tcpip peer;
00039 };
00040 
00041 /**
00042  * List of registered UDP connections
00043  */
00044 static LIST_HEAD ( udp_conns );
00045 
00046 /* Forward declatations */
00047 static struct interface_descriptor udp_xfer_desc;
00048 struct tcpip_protocol udp_protocol __tcpip_protocol;
00049 
00050 /**
00051  * Check if local UDP port is available
00052  *
00053  * @v port              Local port number
00054  * @ret port            Local port number, or negative error
00055  */
00056 static int udp_port_available ( int port ) {
00057         struct udp_connection *udp;
00058 
00059         list_for_each_entry ( udp, &udp_conns, list ) {
00060                 if ( udp->local.st_port == htons ( port ) )
00061                         return -EADDRINUSE;
00062         }
00063         return port;
00064 }
00065 
00066 /**
00067  * Open a UDP connection
00068  *
00069  * @v xfer              Data transfer interface
00070  * @v peer              Peer socket address, or NULL
00071  * @v local             Local socket address, or NULL
00072  * @v promisc           Socket is promiscuous
00073  * @ret rc              Return status code
00074  */
00075 static int udp_open_common ( struct interface *xfer,
00076                              struct sockaddr *peer, struct sockaddr *local,
00077                              int promisc ) {
00078         struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer;
00079         struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local;
00080         struct udp_connection *udp;
00081         int port;
00082         int rc;
00083 
00084         /* Allocate and initialise structure */
00085         udp = zalloc ( sizeof ( *udp ) );
00086         if ( ! udp )
00087                 return -ENOMEM;
00088         DBGC ( udp, "UDP %p allocated\n", udp );
00089         ref_init ( &udp->refcnt, NULL );
00090         intf_init ( &udp->xfer, &udp_xfer_desc, &udp->refcnt );
00091         if ( st_peer )
00092                 memcpy ( &udp->peer, st_peer, sizeof ( udp->peer ) );
00093         if ( st_local )
00094                 memcpy ( &udp->local, st_local, sizeof ( udp->local ) );
00095 
00096         /* Bind to local port */
00097         if ( ! promisc ) {
00098                 port = tcpip_bind ( st_local, udp_port_available );
00099                 if ( port < 0 ) {
00100                         rc = port;
00101                         DBGC ( udp, "UDP %p could not bind: %s\n",
00102                                udp, strerror ( rc ) );
00103                         goto err;
00104                 }
00105                 udp->local.st_port = htons ( port );
00106                 DBGC ( udp, "UDP %p bound to port %d\n",
00107                        udp, ntohs ( udp->local.st_port ) );
00108         }
00109 
00110         /* Attach parent interface, transfer reference to connection
00111          * list and return
00112          */
00113         intf_plug_plug ( &udp->xfer, xfer );
00114         list_add ( &udp->list, &udp_conns );
00115         return 0;
00116 
00117  err:
00118         ref_put ( &udp->refcnt );
00119         return rc;
00120 }
00121 
00122 /**
00123  * Open a UDP connection
00124  *
00125  * @v xfer              Data transfer interface
00126  * @v peer              Peer socket address
00127  * @v local             Local socket address, or NULL
00128  * @ret rc              Return status code
00129  */
00130 int udp_open ( struct interface *xfer, struct sockaddr *peer,
00131                struct sockaddr *local ) {
00132         return udp_open_common ( xfer, peer, local, 0 );
00133 }
00134 
00135 /**
00136  * Open a promiscuous UDP connection
00137  *
00138  * @v xfer              Data transfer interface
00139  * @ret rc              Return status code
00140  *
00141  * Promiscuous UDP connections are required in order to support the
00142  * PXE API.
00143  */
00144 int udp_open_promisc ( struct interface *xfer ) {
00145         return udp_open_common ( xfer, NULL, NULL, 1 );
00146 }
00147 
00148 /**
00149  * Close a UDP connection
00150  *
00151  * @v udp               UDP connection
00152  * @v rc                Reason for close
00153  */
00154 static void udp_close ( struct udp_connection *udp, int rc ) {
00155 
00156         /* Close data transfer interface */
00157         intf_shutdown ( &udp->xfer, rc );
00158 
00159         /* Remove from list of connections and drop list's reference */
00160         list_del ( &udp->list );
00161         ref_put ( &udp->refcnt );
00162 
00163         DBGC ( udp, "UDP %p closed\n", udp );
00164 }
00165 
00166 /**
00167  * Transmit data via a UDP connection to a specified address
00168  *
00169  * @v udp               UDP connection
00170  * @v iobuf             I/O buffer
00171  * @v src               Source address, or NULL to use default
00172  * @v dest              Destination address, or NULL to use default
00173  * @v netdev            Network device, or NULL to use default
00174  * @ret rc              Return status code
00175  */
00176 static int udp_tx ( struct udp_connection *udp, struct io_buffer *iobuf,
00177                     struct sockaddr_tcpip *src, struct sockaddr_tcpip *dest,
00178                     struct net_device *netdev ) {
00179         struct udp_header *udphdr;
00180         size_t len;
00181         int rc;
00182 
00183         /* Check we can accommodate the header */
00184         if ( ( rc = iob_ensure_headroom ( iobuf,
00185                                           MAX_LL_NET_HEADER_LEN ) ) != 0 ) {
00186                 free_iob ( iobuf );
00187                 return rc;
00188         }
00189 
00190         /* Fill in default values if not explicitly provided */
00191         if ( ! src )
00192                 src = &udp->local;
00193         if ( ! dest )
00194                 dest = &udp->peer;
00195 
00196         /* Add the UDP header */
00197         udphdr = iob_push ( iobuf, sizeof ( *udphdr ) );
00198         len = iob_len ( iobuf );
00199         udphdr->dest = dest->st_port;
00200         udphdr->src = src->st_port;
00201         udphdr->len = htons ( len );
00202         udphdr->chksum = 0;
00203         udphdr->chksum = tcpip_chksum ( udphdr, len );
00204 
00205         /* Dump debugging information */
00206         DBGC2 ( udp, "UDP %p TX %d->%d len %d\n", udp,
00207                 ntohs ( udphdr->src ), ntohs ( udphdr->dest ),
00208                 ntohs ( udphdr->len ) );
00209 
00210         /* Send it to the next layer for processing */
00211         if ( ( rc = tcpip_tx ( iobuf, &udp_protocol, src, dest, netdev,
00212                                &udphdr->chksum ) ) != 0 ) {
00213                 DBGC ( udp, "UDP %p could not transmit packet: %s\n",
00214                        udp, strerror ( rc ) );
00215                 return rc;
00216         }
00217 
00218         return 0;
00219 }
00220 
00221 /**
00222  * Identify UDP connection by local address
00223  *
00224  * @v local             Local address
00225  * @ret udp             UDP connection, or NULL
00226  */
00227 static struct udp_connection * udp_demux ( struct sockaddr_tcpip *local ) {
00228         static const struct sockaddr_tcpip empty_sockaddr = { .pad = { 0, } };
00229         struct udp_connection *udp;
00230 
00231         list_for_each_entry ( udp, &udp_conns, list ) {
00232                 if ( ( ( udp->local.st_family == local->st_family ) ||
00233                        ( udp->local.st_family == 0 ) ) &&
00234                      ( ( udp->local.st_port == local->st_port ) ||
00235                        ( udp->local.st_port == 0 ) ) &&
00236                      ( ( memcmp ( udp->local.pad, local->pad,
00237                                   sizeof ( udp->local.pad ) ) == 0 ) ||
00238                        ( memcmp ( udp->local.pad, empty_sockaddr.pad,
00239                                   sizeof ( udp->local.pad ) ) == 0 ) ) ) {
00240                         return udp;
00241                 }
00242         }
00243         return NULL;
00244 }
00245 
00246 /**
00247  * Process a received packet
00248  *
00249  * @v iobuf             I/O buffer
00250  * @v netdev            Network device
00251  * @v st_src            Partially-filled source address
00252  * @v st_dest           Partially-filled destination address
00253  * @v pshdr_csum        Pseudo-header checksum
00254  * @ret rc              Return status code
00255  */
00256 static int udp_rx ( struct io_buffer *iobuf,
00257                     struct net_device *netdev __unused,
00258                     struct sockaddr_tcpip *st_src,
00259                     struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum ) {
00260         struct udp_header *udphdr = iobuf->data;
00261         struct udp_connection *udp;
00262         struct xfer_metadata meta;
00263         size_t ulen;
00264         unsigned int csum;
00265         int rc = 0;
00266 
00267         /* Sanity check packet */
00268         if ( iob_len ( iobuf ) < sizeof ( *udphdr ) ) {
00269                 DBG ( "UDP packet too short at %zd bytes (min %zd bytes)\n",
00270                       iob_len ( iobuf ), sizeof ( *udphdr ) );
00271                 
00272                 rc = -EINVAL;
00273                 goto done;
00274         }
00275         ulen = ntohs ( udphdr->len );
00276         if ( ulen < sizeof ( *udphdr ) ) {
00277                 DBG ( "UDP length too short at %zd bytes "
00278                       "(header is %zd bytes)\n", ulen, sizeof ( *udphdr ) );
00279                 rc = -EINVAL;
00280                 goto done;
00281         }
00282         if ( ulen > iob_len ( iobuf ) ) {
00283                 DBG ( "UDP length too long at %zd bytes (packet is %zd "
00284                       "bytes)\n", ulen, iob_len ( iobuf ) );
00285                 rc = -EINVAL;
00286                 goto done;
00287         }
00288         if ( udphdr->chksum ) {
00289                 csum = tcpip_continue_chksum ( pshdr_csum, iobuf->data, ulen );
00290                 if ( csum != 0 ) {
00291                         DBG ( "UDP checksum incorrect (is %04x including "
00292                               "checksum field, should be 0000)\n", csum );
00293                         rc = -EINVAL;
00294                         goto done;
00295                 }
00296         }
00297 
00298         /* Parse parameters from header and strip header */
00299         st_src->st_port = udphdr->src;
00300         st_dest->st_port = udphdr->dest;
00301         udp = udp_demux ( st_dest );
00302         iob_unput ( iobuf, ( iob_len ( iobuf ) - ulen ) );
00303         iob_pull ( iobuf, sizeof ( *udphdr ) );
00304 
00305         /* Dump debugging information */
00306         DBGC2 ( udp, "UDP %p RX %d<-%d len %zd\n", udp,
00307                 ntohs ( udphdr->dest ), ntohs ( udphdr->src ), ulen );
00308 
00309         /* Ignore if no matching connection found */
00310         if ( ! udp ) {
00311                 DBG ( "No UDP connection listening on port %d\n",
00312                       ntohs ( udphdr->dest ) );
00313                 rc = -ENOTCONN;
00314                 goto done;
00315         }
00316 
00317         /* Pass data to application */
00318         memset ( &meta, 0, sizeof ( meta ) );
00319         meta.src = ( struct sockaddr * ) st_src;
00320         meta.dest = ( struct sockaddr * ) st_dest;
00321         rc = xfer_deliver ( &udp->xfer, iob_disown ( iobuf ), &meta );
00322 
00323  done:
00324         free_iob ( iobuf );
00325         return rc;
00326 }
00327 
00328 struct tcpip_protocol udp_protocol __tcpip_protocol = {
00329         .name = "UDP",
00330         .rx = udp_rx,
00331         .zero_csum = TCPIP_NEGATIVE_ZERO_CSUM,
00332         .tcpip_proto = IP_UDP,
00333 };
00334 
00335 /***************************************************************************
00336  *
00337  * Data transfer interface
00338  *
00339  ***************************************************************************
00340  */
00341 
00342 /**
00343  * Allocate I/O buffer for UDP
00344  *
00345  * @v udp               UDP connection
00346  * @v len               Payload size
00347  * @ret iobuf           I/O buffer, or NULL
00348  */
00349 static struct io_buffer * udp_xfer_alloc_iob ( struct udp_connection *udp,
00350                                                size_t len ) {
00351         struct io_buffer *iobuf;
00352 
00353         iobuf = alloc_iob ( MAX_LL_NET_HEADER_LEN + len );
00354         if ( ! iobuf ) {
00355                 DBGC ( udp, "UDP %p cannot allocate buffer of length %zd\n",
00356                        udp, len );
00357                 return NULL;
00358         }
00359         iob_reserve ( iobuf, MAX_LL_NET_HEADER_LEN );
00360         return iobuf;
00361 }
00362 
00363 /**
00364  * Deliver datagram as I/O buffer
00365  *
00366  * @v udp               UDP connection
00367  * @v iobuf             Datagram I/O buffer
00368  * @v meta              Data transfer metadata
00369  * @ret rc              Return status code
00370  */
00371 static int udp_xfer_deliver ( struct udp_connection *udp,
00372                               struct io_buffer *iobuf,
00373                               struct xfer_metadata *meta ) {
00374 
00375         /* Transmit data, if possible */
00376         return udp_tx ( udp, iobuf, ( ( struct sockaddr_tcpip * ) meta->src ),
00377                         ( ( struct sockaddr_tcpip * ) meta->dest ),
00378                         meta->netdev );
00379 }
00380 
00381 /** UDP data transfer interface operations */
00382 static struct interface_operation udp_xfer_operations[] = {
00383         INTF_OP ( xfer_deliver, struct udp_connection *, udp_xfer_deliver ),
00384         INTF_OP ( xfer_alloc_iob, struct udp_connection *, udp_xfer_alloc_iob ),
00385         INTF_OP ( intf_close, struct udp_connection *, udp_close ),
00386 };
00387 
00388 /** UDP data transfer interface descriptor */
00389 static struct interface_descriptor udp_xfer_desc =
00390         INTF_DESC ( struct udp_connection, xfer, udp_xfer_operations );
00391 
00392 /***************************************************************************
00393  *
00394  * Openers
00395  *
00396  ***************************************************************************
00397  */
00398 
00399 /** UDP IPv4 socket opener */
00400 struct socket_opener udp_ipv4_socket_opener __socket_opener = {
00401         .semantics      = UDP_SOCK_DGRAM,
00402         .family         = AF_INET,
00403         .open           = udp_open,
00404 };
00405 
00406 /** UDP IPv6 socket opener */
00407 struct socket_opener udp_ipv6_socket_opener __socket_opener = {
00408         .semantics      = UDP_SOCK_DGRAM,
00409         .family         = AF_INET6,
00410         .open           = udp_open,
00411 };
00412 
00413 /** Linkage hack */
00414 int udp_sock_dgram = UDP_SOCK_DGRAM;
00415 
00416 /**
00417  * Open UDP URI
00418  *
00419  * @v xfer              Data transfer interface
00420  * @v uri               URI
00421  * @ret rc              Return status code
00422  */
00423 static int udp_open_uri ( struct interface *xfer, struct uri *uri ) {
00424         struct sockaddr_tcpip peer;
00425 
00426         /* Sanity check */
00427         if ( ! uri->host )
00428                 return -EINVAL;
00429 
00430         memset ( &peer, 0, sizeof ( peer ) );
00431         peer.st_port = htons ( uri_port ( uri, 0 ) );
00432         return xfer_open_named_socket ( xfer, SOCK_DGRAM,
00433                                         ( struct sockaddr * ) &peer,
00434                                         uri->host, NULL );
00435 }
00436 
00437 /** UDP URI opener */
00438 struct uri_opener udp_uri_opener __uri_opener = {
00439         .scheme         = "udp",
00440         .open           = udp_open_uri,
00441 };