DO NOT MERGE: Fix up checksums instead of recalculating them.
Currently the checksums of translated packets are calculated
from scratch by checksumming the translated packet. This is slow
and does not work in the case of fragments, because the whole
packet is not available. Instead, calculate the checksum by
adjusting the checksum of the original packet.
Bug: 11542311
Bug: 12116252
Change-Id: I457347753d1bb5b23e3ae759bf2e4388102772d7
diff --git a/ipv4.c b/ipv4.c
index b5cbf80..1d5b0b2 100644
--- a/ipv4.c
+++ b/ipv4.c
@@ -70,7 +70,7 @@
uint8_t nxthdr;
const char *next_header;
size_t len_left;
- uint32_t checksum;
+ uint32_t old_sum, new_sum;
int iov_len;
if(len < sizeof(struct iphdr)) {
@@ -121,14 +121,17 @@
out[pos].iov_len = sizeof(struct ip6_hdr);
// Calculate the pseudo-header checksum.
- checksum = ipv6_pseudo_header_checksum(0, ip6_targ, len_left);
+ old_sum = ipv4_pseudo_header_checksum(0, header, len_left);
+ new_sum = ipv6_pseudo_header_checksum(0, ip6_targ, len_left);
if (nxthdr == IPPROTO_ICMPV6) {
- iov_len = icmp_packet(out, pos + 1, (const struct icmphdr *) next_header, checksum, len_left);
+ iov_len = icmp_packet(out, pos + 1, (const struct icmphdr *) next_header, new_sum, len_left);
} else if (nxthdr == IPPROTO_TCP) {
- iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, checksum, len_left);
+ iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, old_sum, new_sum,
+ len_left);
} else if (nxthdr == IPPROTO_UDP) {
- iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, checksum, len_left);
+ iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, old_sum, new_sum,
+ len_left);
} else if (nxthdr == IPPROTO_GRE) {
iov_len = generic_packet(out, pos + 1, next_header, len_left);
} else {