1 #include <stdint.h>
2 #include <string.h>
3 #include <errno.h>
4 #include <byteswap.h>
5 #include <gpxe/iobuf.h>
6 #include <gpxe/tables.h>
7 #include <gpxe/tcpip.h>
8
9 /** @file
10 *
11 * Transport-network layer interface
12 *
13 * This file contains functions and utilities for the
14 * TCP/IP transport-network layer interface
15 */
16
17 FILE_LICENCE ( GPL2_OR_LATER );
18
19 /** Process a received TCP/IP packet
20 *
21 * @v iobuf I/O buffer
22 * @v tcpip_proto Transport-layer protocol number
23 * @v st_src Partially-filled source address
24 * @v st_dest Partially-filled destination address
25 * @v pshdr_csum Pseudo-header checksum
26 * @ret rc Return status code
27 *
28 * This function expects a transport-layer segment from the network
29 * layer. The network layer should fill in as much as it can of the
30 * source and destination addresses (i.e. it should fill in the
31 * address family and the network-layer addresses, but leave the ports
32 * and the rest of the structures as zero).
33 */
tcpip_rx(struct io_buffer * iobuf,uint8_t tcpip_proto,struct sockaddr_tcpip * st_src,struct sockaddr_tcpip * st_dest,uint16_t pshdr_csum)34 int tcpip_rx ( struct io_buffer *iobuf, uint8_t tcpip_proto,
35 struct sockaddr_tcpip *st_src,
36 struct sockaddr_tcpip *st_dest,
37 uint16_t pshdr_csum ) {
38 struct tcpip_protocol *tcpip;
39
40 /* Hand off packet to the appropriate transport-layer protocol */
41 for_each_table_entry ( tcpip, TCPIP_PROTOCOLS ) {
42 if ( tcpip->tcpip_proto == tcpip_proto ) {
43 DBG ( "TCP/IP received %s packet\n", tcpip->name );
44 return tcpip->rx ( iobuf, st_src, st_dest, pshdr_csum );
45 }
46 }
47
48 DBG ( "Unrecognised TCP/IP protocol %d\n", tcpip_proto );
49 free_iob ( iobuf );
50 return -EPROTONOSUPPORT;
51 }
52
53 /** Transmit a TCP/IP packet
54 *
55 * @v iobuf I/O buffer
56 * @v tcpip_protocol Transport-layer protocol
57 * @v st_src Source address, or NULL to use route default
58 * @v st_dest Destination address
59 * @v netdev Network device to use if no route found, or NULL
60 * @v trans_csum Transport-layer checksum to complete, or NULL
61 * @ret rc Return status code
62 */
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)63 int tcpip_tx ( struct io_buffer *iobuf, struct tcpip_protocol *tcpip_protocol,
64 struct sockaddr_tcpip *st_src, struct sockaddr_tcpip *st_dest,
65 struct net_device *netdev, uint16_t *trans_csum ) {
66 struct tcpip_net_protocol *tcpip_net;
67
68 /* Hand off packet to the appropriate network-layer protocol */
69 for_each_table_entry ( tcpip_net, TCPIP_NET_PROTOCOLS ) {
70 if ( tcpip_net->sa_family == st_dest->st_family ) {
71 DBG ( "TCP/IP sending %s packet\n", tcpip_net->name );
72 return tcpip_net->tx ( iobuf, tcpip_protocol, st_src,
73 st_dest, netdev, trans_csum );
74 }
75 }
76
77 DBG ( "Unrecognised TCP/IP address family %d\n", st_dest->st_family );
78 free_iob ( iobuf );
79 return -EAFNOSUPPORT;
80 }
81
82 /**
83 * Calculate continued TCP/IP checkum
84 *
85 * @v partial Checksum of already-summed data, in network byte order
86 * @v data Data buffer
87 * @v len Length of data buffer
88 * @ret cksum Updated checksum, in network byte order
89 *
90 * Calculates a TCP/IP-style 16-bit checksum over the data block. The
91 * checksum is returned in network byte order.
92 *
93 * This function may be used to add new data to an existing checksum.
94 * The function assumes that both the old data and the new data start
95 * on even byte offsets; if this is not the case then you will need to
96 * byte-swap either the input partial checksum, the output checksum,
97 * or both. Deciding which to swap is left as an exercise for the
98 * interested reader.
99 */
tcpip_continue_chksum(uint16_t partial,const void * data,size_t len)100 uint16_t tcpip_continue_chksum ( uint16_t partial, const void *data,
101 size_t len ) {
102 unsigned int cksum = ( ( ~partial ) & 0xffff );
103 unsigned int value;
104 unsigned int i;
105
106 for ( i = 0 ; i < len ; i++ ) {
107 value = * ( ( uint8_t * ) data + i );
108 if ( i & 1 ) {
109 /* Odd bytes: swap on little-endian systems */
110 value = be16_to_cpu ( value );
111 } else {
112 /* Even bytes: swap on big-endian systems */
113 value = le16_to_cpu ( value );
114 }
115 cksum += value;
116 if ( cksum > 0xffff )
117 cksum -= 0xffff;
118 }
119
120 return ( ~cksum );
121 }
122
123 /**
124 * Calculate TCP/IP checkum
125 *
126 * @v data Data buffer
127 * @v len Length of data buffer
128 * @ret cksum Checksum, in network byte order
129 *
130 * Calculates a TCP/IP-style 16-bit checksum over the data block. The
131 * checksum is returned in network byte order.
132 */
tcpip_chksum(const void * data,size_t len)133 uint16_t tcpip_chksum ( const void *data, size_t len ) {
134 return tcpip_continue_chksum ( TCPIP_EMPTY_CSUM, data, len );
135 }
136