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

Generated on Tue Apr 6 20:01:11 2010 for gPXE by  doxygen 1.5.7.1