summaryrefslogtreecommitdiffstats
path: root/translate.c
diff options
context:
space:
mode:
authorLorenzo Colitti <lorenzo@google.com>2014-02-10 09:20:05 +0900
committerLorenzo Colitti <lorenzo@google.com>2014-02-14 13:33:04 +0900
commit5a50c0283346a197cda7af19e68f611f14b8fe57 (patch)
tree1cccafeba5bbd41374daa0aed35379faa4c26a88 /translate.c
parenta33592bd08a20c6a521b8508975b7a74ecdf4f03 (diff)
downloadandroid_external_android-clat-5a50c0283346a197cda7af19e68f611f14b8fe57.tar.gz
android_external_android-clat-5a50c0283346a197cda7af19e68f611f14b8fe57.tar.bz2
android_external_android-clat-5a50c0283346a197cda7af19e68f611f14b8fe57.zip
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: I6b78a94ca5bd96b13ee2653b6200551193b3dcc1
Diffstat (limited to 'translate.c')
-rw-r--r--translate.c49
1 files changed, 32 insertions, 17 deletions
diff --git a/translate.c b/translate.c
index 00ea0b9..9a0f1b5 100644
--- a/translate.c
+++ b/translate.c
@@ -208,12 +208,10 @@ int icmp_to_icmp6(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t
// The pseudo-header checksum was calculated on the transport length of the original IPv4
// packet that we were asked to translate. This transport length is 20 bytes smaller than it
// needs to be, because the ICMP error contains an IPv4 header, which we will be translating to
- // an IPv6 header, which is 20 bytes longer. Fix it up here. This is simpler than the
- // alternative, which is to always update the pseudo-header checksum in all UDP/TCP/ICMP
- // translation functions (rather than pre-calculating it when translating the IPv4 header).
+ // an IPv6 header, which is 20 bytes longer. Fix it up here.
// We only need to do this for ICMP->ICMPv6, not ICMPv6->ICMP, because ICMP does not use the
// pseudo-header when calculating its checksum (as the IPv4 header has its own checksum).
- checksum = htonl(ntohl(checksum) + 20);
+ checksum = checksum + htons(20);
} else if (icmp6_type == ICMP6_ECHO_REQUEST || icmp6_type == ICMP6_ECHO_REPLY) {
// Ping packet.
icmp6_targ->icmp6_id = icmp->un.echo.id;
@@ -298,10 +296,12 @@ int generic_packet(clat_packet out, int pos, const char *payload, size_t len) {
* takes a udp packet and sets it up for translation
* out - output packet
* udp - pointer to udp header in packet
- * checksum - pseudo-header checksum
+ * old_sum - pseudo-header checksum of old header
+ * new_sum - pseudo-header checksum of new header
* len - size of ip payload
*/
-int udp_packet(clat_packet out, int pos, const struct udphdr *udp, uint32_t checksum, size_t len) {
+int udp_packet(clat_packet out, int pos, const struct udphdr *udp,
+ uint32_t old_sum, uint32_t new_sum, size_t len) {
const char *payload;
size_t payload_size;
@@ -313,7 +313,7 @@ int udp_packet(clat_packet out, int pos, const struct udphdr *udp, uint32_t chec
payload = (const char *) (udp + 1);
payload_size = len - sizeof(struct udphdr);
- return udp_translate(out, pos, udp, checksum, payload, payload_size);
+ return udp_translate(out, pos, udp, old_sum, new_sum, payload, payload_size);
}
/* function: tcp_packet
@@ -324,7 +324,8 @@ int udp_packet(clat_packet out, int pos, const struct udphdr *udp, uint32_t chec
* len - size of ip payload
* returns: the highest position in the output clat_packet that's filled in
*/
-int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp, uint32_t checksum, size_t len) {
+int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp,
+ uint32_t old_sum, uint32_t new_sum, size_t len) {
const char *payload;
size_t payload_size, header_size;
@@ -347,20 +348,21 @@ int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp, uint32_t chec
payload = ((const char *) tcp) + header_size;
payload_size = len - header_size;
- return tcp_translate(out, pos, tcp, header_size, checksum, payload, payload_size);
+ return tcp_translate(out, pos, tcp, header_size, old_sum, new_sum, payload, payload_size);
}
/* function: udp_translate
* common between ipv4/ipv6 - setup checksum and send udp packet
* out - output packet
* udp - udp header
- * checksum - pseudo-header checksum
+ * old_sum - pseudo-header checksum of old header
+ * new_sum - pseudo-header checksum of new header
* payload - tcp payload
* payload_size - size of payload
* returns: the highest position in the output clat_packet that's filled in
*/
-int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t checksum,
- const char *payload, size_t payload_size) {
+int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t old_sum,
+ uint32_t new_sum, const char *payload, size_t payload_size) {
struct udphdr *udp_targ = out[pos].iov_base;
memcpy(udp_targ, udp, sizeof(struct udphdr));
@@ -369,8 +371,22 @@ int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t c
out[CLAT_POS_PAYLOAD].iov_base = (char *) payload;
out[CLAT_POS_PAYLOAD].iov_len = payload_size;
- udp_targ->check = 0; // Checksum field must be 0 when calculating checksum.
- udp_targ->check = packet_checksum(checksum, out, pos);
+ if (udp_targ->check) {
+ udp_targ->check = ip_checksum_adjust(udp->check, old_sum, new_sum);
+ } else {
+ // Zero checksums are special. RFC 768 says, "An all zero transmitted checksum value means that
+ // the transmitter generated no checksum (for debugging or for higher level protocols that
+ // don't care)." However, in IPv6 zero UDP checksums were only permitted by RFC 6935 (2013). So
+ // for safety we recompute it.
+ udp_targ->check = 0; // Checksum field must be 0 when calculating checksum.
+ udp_targ->check = packet_checksum(new_sum, out, pos);
+ }
+
+ // RFC 768: "If the computed checksum is zero, it is transmitted as all ones (the equivalent
+ // in one's complement arithmetic)."
+ if (!udp_targ->check) {
+ udp_targ->check = 0xffff;
+ }
return CLAT_POS_PAYLOAD + 1;
}
@@ -389,7 +405,7 @@ int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t c
* TODO: hosts without pmtu discovery - non DF packets will rely on fragmentation (unimplemented)
*/
int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t header_size,
- uint32_t checksum, const char *payload, size_t payload_size) {
+ uint32_t old_sum, uint32_t new_sum, const char *payload, size_t payload_size) {
struct tcphdr *tcp_targ = out[pos].iov_base;
out[pos].iov_len = header_size;
@@ -406,8 +422,7 @@ int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t hea
out[CLAT_POS_PAYLOAD].iov_base = (char *)payload;
out[CLAT_POS_PAYLOAD].iov_len = payload_size;
- tcp_targ->check = 0; // Checksum field must be 0 when calculating checksum.
- tcp_targ->check = packet_checksum(checksum, out, pos);
+ tcp_targ->check = ip_checksum_adjust(tcp->check, old_sum, new_sum);
return CLAT_POS_PAYLOAD + 1;
}