summaryrefslogtreecommitdiffstats
path: root/debian/grub-extras/disabled/gpxe/src/net/tcpip.c
blob: 932fd482a1a854df2b379ab3fcae77ec447ad7ea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <byteswap.h>
#include <gpxe/iobuf.h>
#include <gpxe/tables.h>
#include <gpxe/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 );

/** Process a received TCP/IP packet
 *
 * @v iobuf		I/O buffer
 * @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
 * @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, uint8_t tcpip_proto, 
	       struct sockaddr_tcpip *st_src,
	       struct sockaddr_tcpip *st_dest,
	       uint16_t pshdr_csum ) {
	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 );
			return tcpip->rx ( iobuf, st_src, st_dest, pshdr_csum );
		}
	}

	DBG ( "Unrecognised TCP/IP protocol %d\n", tcpip_proto );
	free_iob ( iobuf );
	return -EPROTONOSUPPORT;
}

/** 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 */
	for_each_table_entry ( tcpip_net, TCPIP_NET_PROTOCOLS ) {
		if ( tcpip_net->sa_family == st_dest->st_family ) {
			DBG ( "TCP/IP sending %s packet\n", tcpip_net->name );
			return tcpip_net->tx ( iobuf, tcpip_protocol, st_src,
					       st_dest, netdev, trans_csum );
		}
	}
	
	DBG ( "Unrecognised TCP/IP address family %d\n", st_dest->st_family );
	free_iob ( iobuf );
	return -EAFNOSUPPORT;
}

/**
 * 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 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 );
}