summaryrefslogtreecommitdiffstats
path: root/src/net/tcpip_if.c
blob: 5c5e596216a43d92fe46de0e7520e33fce65d657 (plain) (blame)
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
136
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <malloc.h>
#include <byteswap.h>
#include <gpxe/in.h>
#include <gpxe/ip.h>
#include <gpxe/pkbuff.h>
#include <gpxe/tables.h>
#include <gpxe/netdevice.h>
#include <gpxe/tcpip_if.h>

/** @file
 *
 * Transport-network layer interface
 *
 * This file contains functions and utilities for the transport-network layer interface
 */

/** Registered network-layer protocols that support TCPIP */
static struct tcpip_net_protocol tcpip_net_protocols[0] __table_start ( tcpip_net_protocols );
static struct tcpip_net_protocol tcpip_net_protocols_end[0] __table_end ( tcpip_net_protocols );

struct tcpip_protocol;

/** Registered transport-layer protocols that support TCPIP */
static struct tcpip_protocol tcpip_protocols[0] __table_start ( tcpip_protocols );
static struct tcpip_protocol tcpip_protocols_end[0] __table_end ( tcpip_protocols );

/** Identify TCPIP network-layer protocol
 *
 * @v sa_family	 Network address family
 * @ret tcpip	   Protocol supporting TCPIP, or NULL
 */
static struct tcpip_net_protocol * tcpip_find_protocol ( sa_family_t sa_family ) {
	struct tcpip_net_protocol *tcpip_net;

	for ( tcpip_net = tcpip_net_protocols; 
		tcpip_net < tcpip_net_protocols_end; ++tcpip_net ) {
		if ( tcpip_net->sa_family == sa_family ) {
			return tcpip_net;
		}
	}
	return NULL;
}

/** Identify TCPIP transport-layer protocol
 *
 * @v trans_proto	Transport-layer protocol number, IP_XXX
 * @ret tcpip_protocol	Transport-layer protocol, or NULL
 */
struct tcpip_protocol* find_tcpip_protocol ( uint8_t trans_proto ) {
	struct tcpip_protocol *tcpip;

	for ( tcpip = tcpip_protocols; tcpip <= tcpip_protocols_end; 
		++tcpip ) {
		if ( tcpip->trans_proto == trans_proto ) {
			return tcpip;
		}
	}
	return NULL;
}

/** Process a received packet
 *
 * @v pkb		Packet buffer
 * @v trans_proto	Transport-layer protocol number
 * @v src		Source network-layer address
 * @v dest		Destination network-layer address
 *
 * This function expects a transport-layer segment from the network-layer
 */
void trans_rx ( struct pk_buff *pkb, uint8_t trans_proto, struct in_addr *src,
		struct in_addr *dest ) {
	struct tcpip_protocol *tcpip;

	/* Identify the transport layer protocol */
	for ( tcpip = tcpip_protocols; tcpip <= tcpip_protocols_end; ++tcpip ) {
		if ( tcpip->trans_proto == trans_proto ) {
			DBG ( "Packet sent to %s module", tcpip->name );
			tcpip->rx ( pkb, src, dest );
		}
	}
}

/** Transmit a transport-layer segment
 *
 * @v pkb		Packet buffer
 * @v trans_proto	Transport-layer protocol
 * @v sock		Destination socket address
 * @ret			Status
 */
int trans_tx ( struct pk_buff *pkb, struct tcpip_protocol *tcpip,
	       struct sockaddr *sock ) {

	/* Identify the network layer protocol and send it using xxx_tx() */
	switch ( sock->sa_family ) {
	case AF_INET: /* IPv4 network family */
		return ipv4_tx ( pkb, tcpip, &sock->sin.sin_addr );
	case AF_INET6: /* IPv6 network family */
		return ipv6_tx ( pkb, tcpip, &sock->sin6.sin6_addr );
	}
	DBG ( "Network family %d not supported", sock->sa_family );
	return -EAFNOSUPPORT;
}

/**
 * Calculate internet checksum
 *
 * @v data      Pointer to the data
 * @v len       Length of data to be checksummed
 * @ret chksum  16 bit internet checksum
 *
 * This function calculates the internet checksum (refer RFC1071) for "len"
 * bytes beginning at the location "data"
 */
uint16_t calc_chksum ( void *data, size_t len ) {
	register long sum = 0;
	uint16_t checksum;
	unsigned short *temp;
	while ( len > 1 ) {
		temp = (unsigned short*) data++;
		sum += *temp;
		len -= 2;
	}
	if ( len > 0 ) {
		sum += *(unsigned char *)data;
	}
	while ( sum >> 16 ) {
		sum = ( sum & 0xffff ) + ( sum >> 16 );
	}
	checksum = ~sum;
	return checksum;
}