22546a49b66f6e5ea281f87e305474ab33b9c4be
[gpxe.git] / src / net / tcpip.c
1 #include <stdint.h>
2 #include <string.h>
3 #include <errno.h>
4 #include <malloc.h>
5 #include <byteswap.h>
6 #include <gpxe/in.h>
7 #include <gpxe/ip.h>
8 #include <gpxe/pkbuff.h>
9 #include <gpxe/tables.h>
10 #include <gpxe/netdevice.h>
11 #include <gpxe/tcpip.h>
12
13 /** @file
14  *
15  * Transport-network layer interface
16  *
17  * This file contains functions and utilities for the transport-network layer interface
18  */
19
20 /** Registered network-layer protocols that support TCPIP */
21 static struct tcpip_net_protocol tcpip_net_protocols[0] __table_start ( tcpip_net_protocols );
22 static struct tcpip_net_protocol tcpip_net_protocols_end[0] __table_end ( tcpip_net_protocols );
23
24 /** Registered transport-layer protocols that support TCPIP */
25 static struct tcpip_protocol tcpip_protocols[0] __table_start ( tcpip_protocols );
26 static struct tcpip_protocol tcpip_protocols_end[0] __table_end ( tcpip_protocols );
27
28 /** Process a received packet
29  *
30  * @v pkb               Packet buffer
31  * @v trans_proto       Transport-layer protocol number
32  * @v src               Source network-layer address
33  * @v dest              Destination network-layer address
34  *
35  * This function expects a transport-layer segment from the network-layer
36  */
37 void tcpip_rx ( struct pk_buff *pkb, uint8_t trans_proto, struct in_addr *src,
38                 struct in_addr *dest ) {
39         struct tcpip_protocol *tcpip;
40
41         /* Identify the transport layer protocol */
42         for ( tcpip = tcpip_protocols; tcpip <= tcpip_protocols_end; ++tcpip ) {
43                 if ( tcpip->trans_proto == trans_proto ) {
44                         DBG ( "Packet sent to %s module", tcpip->name );
45                         tcpip->rx ( pkb, src, dest );
46                 }
47         }
48 }
49
50 /** Transmit a transport-layer segment
51  *
52  * @v pkb               Packet buffer
53  * @v trans_proto       Transport-layer protocol
54  * @v sock              Destination socket address
55  * @ret                 Status
56  */
57 int tcpip_tx ( struct pk_buff *pkb, struct tcpip_protocol *tcpip,
58                struct sockaddr *sock ) {
59
60         /* Identify the network layer protocol and send it using xxx_tx() */
61         switch ( sock->sa_family ) {
62         case AF_INET: /* IPv4 network family */
63                 return ipv4_tx ( pkb, tcpip, &sock->sin.sin_addr );
64         case AF_INET6: /* IPv6 network family */
65                 return ipv6_tx ( pkb, tcpip, &sock->sin6.sin6_addr );
66         }
67         DBG ( "Network family %d not supported", sock->sa_family );
68         return -EAFNOSUPPORT;
69 }
70
71 /**
72  * Calculate continued TCP/IP checkum
73  *
74  * @v partial           Checksum of already-summed data, in network byte order
75  * @v data              Data buffer
76  * @v len               Length of data buffer
77  * @ret cksum           Updated checksum, in network byte order
78  *
79  * Calculates a TCP/IP-style 16-bit checksum over the data block.  The
80  * checksum is returned in network byte order.
81  *
82  * This function may be used to add new data to an existing checksum.
83  * The function assumes that both the old data and the new data start
84  * on even byte offsets; if this is not the case then you will need to
85  * byte-swap either the input partial checksum, the output checksum,
86  * or both.  Deciding which to swap is left as an exercise for the
87  * interested reader.
88  */
89 unsigned int tcpip_continue_chksum ( unsigned int partial, const void *data,
90                                      size_t len ) {
91         unsigned int cksum = ( ( ~partial ) & 0xffff );
92         unsigned int value;
93         unsigned int i;
94         
95         for ( i = 0 ; i < len ; i++ ) {
96                 value = * ( ( uint8_t * ) data + i );
97                 if ( i & 1 ) {
98                         /* Odd bytes: swap on little-endian systems */
99                         value = be16_to_cpu ( value );
100                 } else {
101                         /* Even bytes: swap on big-endian systems */
102                         value = le16_to_cpu ( value );
103                 }
104                 cksum += value;
105                 if ( cksum > 0xffff )
106                         cksum -= 0xffff;
107         }
108         
109         return ( ( ~cksum ) & 0xffff );
110 }
111
112 /**
113  * Calculate TCP/IP checkum
114  *
115  * @v data              Data buffer
116  * @v len               Length of data buffer
117  * @ret cksum           Checksum, in network byte order
118  *
119  * Calculates a TCP/IP-style 16-bit checksum over the data block.  The
120  * checksum is returned in network byte order.
121  */
122 unsigned int tcpip_chksum ( const void *data, size_t len ) {
123         return tcpip_continue_chksum ( 0xffff, data, len );
124 }