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");
}
|