iPXE
tcpip.c
Go to the documentation of this file.
00001 #include <stdint.h>
00002 #include <stdlib.h>
00003 #include <string.h>
00004 #include <errno.h>
00005 #include <byteswap.h>
00006 #include <ipxe/iobuf.h>
00007 #include <ipxe/tables.h>
00008 #include <ipxe/ipstat.h>
00009 #include <ipxe/netdevice.h>
00010 #include <ipxe/tcpip.h>
00011 
00012 /** @file
00013  *
00014  * Transport-network layer interface
00015  *
00016  * This file contains functions and utilities for the
00017  * TCP/IP transport-network layer interface
00018  */
00019 
00020 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00021 
00022 /**
00023  * Process a received TCP/IP packet
00024  *
00025  * @v iobuf             I/O buffer
00026  * @v netdev            Network device
00027  * @v tcpip_proto       Transport-layer protocol number
00028  * @v st_src            Partially-filled source address
00029  * @v st_dest           Partially-filled destination address
00030  * @v pshdr_csum        Pseudo-header checksum
00031  * @v stats             IP statistics
00032  * @ret rc              Return status code
00033  *
00034  * This function expects a transport-layer segment from the network
00035  * layer.  The network layer should fill in as much as it can of the
00036  * source and destination addresses (i.e. it should fill in the
00037  * address family and the network-layer addresses, but leave the ports
00038  * and the rest of the structures as zero).
00039  */
00040 int tcpip_rx ( struct io_buffer *iobuf, struct net_device *netdev,
00041                uint8_t tcpip_proto, struct sockaddr_tcpip *st_src,
00042                struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum,
00043                struct ip_statistics *stats ) {
00044         struct tcpip_protocol *tcpip;
00045 
00046         /* Hand off packet to the appropriate transport-layer protocol */
00047         for_each_table_entry ( tcpip, TCPIP_PROTOCOLS ) {
00048                 if ( tcpip->tcpip_proto == tcpip_proto ) {
00049                         DBG ( "TCP/IP received %s packet\n", tcpip->name );
00050                         stats->in_delivers++;
00051                         return tcpip->rx ( iobuf, netdev, st_src, st_dest,
00052                                            pshdr_csum );
00053                 }
00054         }
00055 
00056         DBG ( "Unrecognised TCP/IP protocol %d\n", tcpip_proto );
00057         stats->in_unknown_protos++;
00058         free_iob ( iobuf );
00059         return -EPROTONOSUPPORT;
00060 }
00061 
00062 /**
00063  * Find TCP/IP network-layer protocol
00064  *
00065  * @v sa_family         Address family
00066  * @ret tcpip_net       TCP/IP network-layer protocol, or NULL if not found
00067  */
00068 struct tcpip_net_protocol * tcpip_net_protocol ( sa_family_t sa_family ) {
00069         struct tcpip_net_protocol *tcpip_net;
00070 
00071         for_each_table_entry ( tcpip_net, TCPIP_NET_PROTOCOLS ) {
00072                 if ( tcpip_net->sa_family == sa_family )
00073                         return tcpip_net;
00074         }
00075 
00076         DBG ( "Unrecognised TCP/IP address family %d\n", sa_family );
00077         return NULL;
00078 }
00079 
00080 /**
00081  * Transmit a TCP/IP packet
00082  *
00083  * @v iobuf             I/O buffer
00084  * @v tcpip_protocol    Transport-layer protocol
00085  * @v st_src            Source address, or NULL to use route default
00086  * @v st_dest           Destination address
00087  * @v netdev            Network device to use if no route found, or NULL
00088  * @v trans_csum        Transport-layer checksum to complete, or NULL
00089  * @ret rc              Return status code
00090  */
00091 int tcpip_tx ( struct io_buffer *iobuf, struct tcpip_protocol *tcpip_protocol,
00092                struct sockaddr_tcpip *st_src, struct sockaddr_tcpip *st_dest,
00093                struct net_device *netdev, uint16_t *trans_csum ) {
00094         struct tcpip_net_protocol *tcpip_net;
00095 
00096         /* Hand off packet to the appropriate network-layer protocol */
00097         tcpip_net = tcpip_net_protocol ( st_dest->st_family );
00098         if ( tcpip_net ) {
00099                 DBG ( "TCP/IP sending %s packet\n", tcpip_net->name );
00100                 return tcpip_net->tx ( iobuf, tcpip_protocol, st_src, st_dest,
00101                                        netdev, trans_csum );
00102         }
00103 
00104         free_iob ( iobuf );
00105         return -EAFNOSUPPORT;
00106 }
00107 
00108 /**
00109  * Determine transmitting network device
00110  *
00111  * @v st_dest           Destination address
00112  * @ret netdev          Network device, or NULL
00113  */
00114 struct net_device * tcpip_netdev ( struct sockaddr_tcpip *st_dest ) {
00115         struct tcpip_net_protocol *tcpip_net;
00116 
00117         /* Hand off to the appropriate network-layer protocol */
00118         tcpip_net = tcpip_net_protocol ( st_dest->st_family );
00119         if ( tcpip_net )
00120                 return tcpip_net->netdev ( st_dest );
00121 
00122         return NULL;
00123 }
00124 
00125 /**
00126  * Determine maximum transmission unit
00127  *
00128  * @v st_dest           Destination address
00129  * @ret mtu             Maximum transmission unit
00130  */
00131 size_t tcpip_mtu ( struct sockaddr_tcpip *st_dest ) {
00132         struct tcpip_net_protocol *tcpip_net;
00133         struct net_device *netdev;
00134         size_t mtu;
00135 
00136         /* Find appropriate network-layer protocol */
00137         tcpip_net = tcpip_net_protocol ( st_dest->st_family );
00138         if ( ! tcpip_net )
00139                 return 0;
00140 
00141         /* Find transmitting network device */
00142         netdev = tcpip_net->netdev ( st_dest );
00143         if ( ! netdev )
00144                 return 0;
00145 
00146         /* Calculate MTU */
00147         mtu = ( netdev->mtu - tcpip_net->header_len );
00148 
00149         return mtu;
00150 }
00151 
00152 /**
00153  * Calculate continued TCP/IP checkum
00154  *
00155  * @v partial           Checksum of already-summed data, in network byte order
00156  * @v data              Data buffer
00157  * @v len               Length of data buffer
00158  * @ret cksum           Updated checksum, in network byte order
00159  *
00160  * Calculates a TCP/IP-style 16-bit checksum over the data block.  The
00161  * checksum is returned in network byte order.
00162  *
00163  * This function may be used to add new data to an existing checksum.
00164  * The function assumes that both the old data and the new data start
00165  * on even byte offsets; if this is not the case then you will need to
00166  * byte-swap either the input partial checksum, the output checksum,
00167  * or both.  Deciding which to swap is left as an exercise for the
00168  * interested reader.
00169  */
00170 uint16_t generic_tcpip_continue_chksum ( uint16_t partial,
00171                                          const void *data, size_t len ) {
00172         unsigned int cksum = ( ( ~partial ) & 0xffff );
00173         unsigned int value;
00174         unsigned int i;
00175         
00176         for ( i = 0 ; i < len ; i++ ) {
00177                 value = * ( ( uint8_t * ) data + i );
00178                 if ( i & 1 ) {
00179                         /* Odd bytes: swap on little-endian systems */
00180                         value = be16_to_cpu ( value );
00181                 } else {
00182                         /* Even bytes: swap on big-endian systems */
00183                         value = le16_to_cpu ( value );
00184                 }
00185                 cksum += value;
00186                 if ( cksum > 0xffff )
00187                         cksum -= 0xffff;
00188         }
00189         
00190         return ( ~cksum );
00191 }
00192 
00193 /**
00194  * Calculate TCP/IP checkum
00195  *
00196  * @v data              Data buffer
00197  * @v len               Length of data buffer
00198  * @ret cksum           Checksum, in network byte order
00199  *
00200  * Calculates a TCP/IP-style 16-bit checksum over the data block.  The
00201  * checksum is returned in network byte order.
00202  */
00203 uint16_t tcpip_chksum ( const void *data, size_t len ) {
00204         return tcpip_continue_chksum ( TCPIP_EMPTY_CSUM, data, len );
00205 }
00206 
00207 /**
00208  * Bind to local TCP/IP port
00209  *
00210  * @v st_local          Local TCP/IP socket address, or NULL
00211  * @v available         Function to check port availability
00212  * @ret port            Local port number, or negative error
00213  */
00214 int tcpip_bind ( struct sockaddr_tcpip *st_local,
00215                  int ( * available ) ( int port ) ) {
00216         uint16_t flags = 0;
00217         uint16_t try_port = 0;
00218         uint16_t min_port;
00219         uint16_t max_port;
00220         unsigned int offset;
00221         unsigned int i;
00222 
00223         /* Extract parameters from local socket address */
00224         if ( st_local ) {
00225                 flags = st_local->st_flags;
00226                 try_port = ntohs ( st_local->st_port );
00227         }
00228 
00229         /* If an explicit port is specified, check its availability */
00230         if ( try_port )
00231                 return available ( try_port );
00232 
00233         /* Otherwise, find an available port in the range [1,1023] or
00234          * [1025,65535] as appropriate.
00235          */
00236         min_port = ( ( ( ~flags ) & TCPIP_BIND_PRIVILEGED ) + 1 );
00237         max_port = ( ( flags & TCPIP_BIND_PRIVILEGED ) - 1 );
00238         offset = random();
00239         for ( i = 0 ; i <= max_port ; i++ ) {
00240                 try_port = ( ( i + offset ) & max_port );
00241                 if ( try_port < min_port )
00242                         continue;
00243                 if ( available ( try_port ) < 0 )
00244                         continue;
00245                 return try_port;
00246         }
00247         return -EADDRINUSE;
00248 }