123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- #include <stdint.h>
- #include <stdlib.h>
- #include <string.h>
- #include <errno.h>
- #include <byteswap.h>
- #include <ipxe/iobuf.h>
- #include <ipxe/tables.h>
- #include <ipxe/ipstat.h>
- #include <ipxe/netdevice.h>
- #include <ipxe/tcpip.h>
-
- /** @file
- *
- * Transport-network layer interface
- *
- * This file contains functions and utilities for the
- * TCP/IP transport-network layer interface
- */
-
- FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
- /**
- * Process a received TCP/IP packet
- *
- * @v iobuf I/O buffer
- * @v netdev Network device
- * @v tcpip_proto Transport-layer protocol number
- * @v st_src Partially-filled source address
- * @v st_dest Partially-filled destination address
- * @v pshdr_csum Pseudo-header checksum
- * @v stats IP statistics
- * @ret rc Return status code
- *
- * This function expects a transport-layer segment from the network
- * layer. The network layer should fill in as much as it can of the
- * source and destination addresses (i.e. it should fill in the
- * address family and the network-layer addresses, but leave the ports
- * and the rest of the structures as zero).
- */
- int tcpip_rx ( struct io_buffer *iobuf, struct net_device *netdev,
- uint8_t tcpip_proto, struct sockaddr_tcpip *st_src,
- struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum,
- struct ip_statistics *stats ) {
- struct tcpip_protocol *tcpip;
-
- /* Hand off packet to the appropriate transport-layer protocol */
- for_each_table_entry ( tcpip, TCPIP_PROTOCOLS ) {
- if ( tcpip->tcpip_proto == tcpip_proto ) {
- DBG ( "TCP/IP received %s packet\n", tcpip->name );
- stats->in_delivers++;
- return tcpip->rx ( iobuf, netdev, st_src, st_dest,
- pshdr_csum );
- }
- }
-
- DBG ( "Unrecognised TCP/IP protocol %d\n", tcpip_proto );
- stats->in_unknown_protos++;
- free_iob ( iobuf );
- return -EPROTONOSUPPORT;
- }
-
- /**
- * Find TCP/IP network-layer protocol
- *
- * @v sa_family Address family
- * @ret tcpip_net TCP/IP network-layer protocol, or NULL if not found
- */
- struct tcpip_net_protocol * tcpip_net_protocol ( sa_family_t sa_family ) {
- struct tcpip_net_protocol *tcpip_net;
-
- for_each_table_entry ( tcpip_net, TCPIP_NET_PROTOCOLS ) {
- if ( tcpip_net->sa_family == sa_family )
- return tcpip_net;
- }
-
- DBG ( "Unrecognised TCP/IP address family %d\n", sa_family );
- return NULL;
- }
-
- /**
- * Transmit a TCP/IP packet
- *
- * @v iobuf I/O buffer
- * @v tcpip_protocol Transport-layer protocol
- * @v st_src Source address, or NULL to use route default
- * @v st_dest Destination address
- * @v netdev Network device to use if no route found, or NULL
- * @v trans_csum Transport-layer checksum to complete, or NULL
- * @ret rc Return status code
- */
- int tcpip_tx ( struct io_buffer *iobuf, struct tcpip_protocol *tcpip_protocol,
- struct sockaddr_tcpip *st_src, struct sockaddr_tcpip *st_dest,
- struct net_device *netdev, uint16_t *trans_csum ) {
- struct tcpip_net_protocol *tcpip_net;
-
- /* Hand off packet to the appropriate network-layer protocol */
- tcpip_net = tcpip_net_protocol ( st_dest->st_family );
- if ( tcpip_net ) {
- DBG ( "TCP/IP sending %s packet\n", tcpip_net->name );
- return tcpip_net->tx ( iobuf, tcpip_protocol, st_src, st_dest,
- netdev, trans_csum );
- }
-
- free_iob ( iobuf );
- return -EAFNOSUPPORT;
- }
-
- /**
- * Determine transmitting network device
- *
- * @v st_dest Destination address
- * @ret netdev Network device, or NULL
- */
- struct net_device * tcpip_netdev ( struct sockaddr_tcpip *st_dest ) {
- struct tcpip_net_protocol *tcpip_net;
-
- /* Hand off to the appropriate network-layer protocol */
- tcpip_net = tcpip_net_protocol ( st_dest->st_family );
- if ( tcpip_net )
- return tcpip_net->netdev ( st_dest );
-
- return NULL;
- }
-
- /**
- * Determine maximum transmission unit
- *
- * @v st_dest Destination address
- * @ret mtu Maximum transmission unit
- */
- size_t tcpip_mtu ( struct sockaddr_tcpip *st_dest ) {
- struct tcpip_net_protocol *tcpip_net;
- struct net_device *netdev;
- size_t mtu;
-
- /* Find appropriate network-layer protocol */
- tcpip_net = tcpip_net_protocol ( st_dest->st_family );
- if ( ! tcpip_net )
- return 0;
-
- /* Find transmitting network device */
- netdev = tcpip_net->netdev ( st_dest );
- if ( ! netdev )
- return 0;
-
- /* Calculate MTU */
- mtu = ( netdev->max_pkt_len - netdev->ll_protocol->ll_header_len -
- tcpip_net->header_len );
-
- return mtu;
- }
-
- /**
- * Calculate continued TCP/IP checkum
- *
- * @v partial Checksum of already-summed data, in network byte order
- * @v data Data buffer
- * @v len Length of data buffer
- * @ret cksum Updated checksum, in network byte order
- *
- * Calculates a TCP/IP-style 16-bit checksum over the data block. The
- * checksum is returned in network byte order.
- *
- * This function may be used to add new data to an existing checksum.
- * The function assumes that both the old data and the new data start
- * on even byte offsets; if this is not the case then you will need to
- * byte-swap either the input partial checksum, the output checksum,
- * or both. Deciding which to swap is left as an exercise for the
- * interested reader.
- */
- uint16_t generic_tcpip_continue_chksum ( uint16_t partial,
- const void *data, size_t len ) {
- unsigned int cksum = ( ( ~partial ) & 0xffff );
- unsigned int value;
- unsigned int i;
-
- for ( i = 0 ; i < len ; i++ ) {
- value = * ( ( uint8_t * ) data + i );
- if ( i & 1 ) {
- /* Odd bytes: swap on little-endian systems */
- value = be16_to_cpu ( value );
- } else {
- /* Even bytes: swap on big-endian systems */
- value = le16_to_cpu ( value );
- }
- cksum += value;
- if ( cksum > 0xffff )
- cksum -= 0xffff;
- }
-
- return ( ~cksum );
- }
-
- /**
- * Calculate TCP/IP checkum
- *
- * @v data Data buffer
- * @v len Length of data buffer
- * @ret cksum Checksum, in network byte order
- *
- * Calculates a TCP/IP-style 16-bit checksum over the data block. The
- * checksum is returned in network byte order.
- */
- uint16_t tcpip_chksum ( const void *data, size_t len ) {
- return tcpip_continue_chksum ( TCPIP_EMPTY_CSUM, data, len );
- }
-
- /**
- * Bind to local TCP/IP port
- *
- * @v st_local Local TCP/IP socket address, or NULL
- * @v available Function to check port availability
- * @ret port Local port number, or negative error
- */
- int tcpip_bind ( struct sockaddr_tcpip *st_local,
- int ( * available ) ( int port ) ) {
- uint16_t flags = 0;
- uint16_t try_port = 0;
- uint16_t min_port;
- uint16_t max_port;
- unsigned int offset;
- unsigned int i;
-
- /* Extract parameters from local socket address */
- if ( st_local ) {
- flags = st_local->st_flags;
- try_port = ntohs ( st_local->st_port );
- }
-
- /* If an explicit port is specified, check its availability */
- if ( try_port )
- return available ( try_port );
-
- /* Otherwise, find an available port in the range [1,1023] or
- * [1025,65535] as appropriate.
- */
- min_port = ( ( ( ~flags ) & TCPIP_BIND_PRIVILEGED ) + 1 );
- max_port = ( ( flags & TCPIP_BIND_PRIVILEGED ) - 1 );
- offset = random();
- for ( i = 0 ; i <= max_port ; i++ ) {
- try_port = ( ( i + offset ) & max_port );
- if ( try_port < min_port )
- continue;
- if ( available ( try_port ) < 0 )
- continue;
- return try_port;
- }
- return -EADDRINUSE;
- }
|