aboutsummaryrefslogtreecommitdiffstats
path: root/test/packetdrill/packet_checksum.c
blob: d5164b3433fe808a2cd2b5f77d312dfdf4c46c58 (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
/*
 * Copyright 2013 Google Inc.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */
/*
 * Author: ncardwell@google.com (Neal Cardwell)
 *
 * Implementation for a module to checksum TCP/IP packets.
 */

#include "packet_checksum.h"

#include "checksum.h"
#include "icmp.h"
#include "icmpv6.h"
#include "ip.h"
#include "ipv6.h"
#include "tcp.h"

static void checksum_ipv4_packet(struct packet *packet)
{
	struct ipv4 *ipv4 = packet->ipv4;

	/* Fill in IPv4 header checksum. */
	ipv4->check = 0;
	ipv4->check = ipv4_checksum(ipv4, ipv4_header_len(ipv4));
	assert(packet->ip_bytes >= ntohs(ipv4->tot_len));

	/* Find the length of layer 4 header, options, and payload. */
	const int l4_bytes = ntohs(ipv4->tot_len) - ipv4_header_len(ipv4);
	assert(l4_bytes > 0);

	/* Fill in IPv4-based layer 4 checksum. */
	if (packet->tcp != NULL) {
		struct tcp *tcp = packet->tcp;
		tcp->check = 0;
		tcp->check = tcp_udp_v4_checksum(ipv4->src_ip,
						 ipv4->dst_ip,
						 IPPROTO_TCP, tcp, l4_bytes);
	} else if (packet->udp != NULL) {
		struct udp *udp = packet->udp;
		udp->check = 0;
		udp->check = tcp_udp_v4_checksum(ipv4->src_ip,
						 ipv4->dst_ip,
						 IPPROTO_UDP, udp, l4_bytes);
	} else if (packet->icmpv4 != NULL) {
		struct icmpv4 *icmpv4 = packet->icmpv4;
		icmpv4->checksum = 0;
		icmpv4->checksum = ipv4_checksum(icmpv4, l4_bytes);
	} else {
		assert(!"not TCP or ICMP");
	}
}

static void checksum_ipv6_packet(struct packet *packet)
{
	struct ipv6 *ipv6 = packet->ipv6;

	/* IPv6 has no header checksum. */
	/* For now we do not support IPv6 extension headers. */
	assert(packet->ip_bytes >= sizeof(*ipv6) + ntohs(ipv6->payload_len));

	/* Find the length of layer 4 header, options, and payload. */
	const int l4_bytes = ntohs(ipv6->payload_len);
	assert(l4_bytes > 0);

	/* Fill in IPv6-based layer 4 checksum. */
	if (packet->tcp != NULL) {
		struct tcp *tcp = packet->tcp;
		tcp->check = 0;
		tcp->check = tcp_udp_v6_checksum(&ipv6->src_ip,
						 &ipv6->dst_ip,
						 IPPROTO_TCP, tcp, l4_bytes);
	} else if (packet->udp != NULL) {
		struct udp *udp = packet->udp;
		udp->check = 0;
		udp->check = tcp_udp_v6_checksum(&ipv6->src_ip,
						 &ipv6->dst_ip,
						 IPPROTO_UDP, udp, l4_bytes);
	} else if (packet->icmpv6 != NULL) {
		/* IPv6 ICMP has a pseudo-header checksum, like TCP. */
		struct icmpv6 *icmpv6 = packet->icmpv6;
		icmpv6->checksum = 0;
		icmpv6->checksum =
			tcp_udp_v6_checksum(&ipv6->src_ip,
					    &ipv6->dst_ip,
					    IPPROTO_ICMPV6, icmpv6, l4_bytes);
	} else {
		assert(!"not TCP or ICMP");
	}
}

void checksum_packet(struct packet *packet)
{
	int address_family = packet_address_family(packet);
	if (address_family == AF_INET)
		return checksum_ipv4_packet(packet);
	else if (address_family == AF_INET6)
		return checksum_ipv6_packet(packet);
	else
		assert(!"bad ip version");
}