diff options
author | Lorenzo Colitti <lorenzo@google.com> | 2014-02-14 13:19:27 +0900 |
---|---|---|
committer | Lorenzo Colitti <lorenzo@google.com> | 2014-02-22 13:19:36 +0900 |
commit | f3beefc96a5a4f295562f60d648bd6e795980a10 (patch) | |
tree | 8bd6900aee2eaba68feddf18f0c8183e5ba72261 /clatd_test.cpp | |
parent | 57d480d2b425ef20d8b6f84abd4e9e3209fa9422 (diff) | |
download | android_external_android-clat-f3beefc96a5a4f295562f60d648bd6e795980a10.tar.gz android_external_android-clat-f3beefc96a5a4f295562f60d648bd6e795980a10.tar.bz2 android_external_android-clat-f3beefc96a5a4f295562f60d648bd6e795980a10.zip |
Add a unit test for 464xlat translation code.
Bug: 11542311
Change-Id: Ibc7f156a6abb96c9bf4983ecd63b052fe308e422
Diffstat (limited to 'clatd_test.cpp')
-rw-r--r-- | clatd_test.cpp | 672 |
1 files changed, 672 insertions, 0 deletions
diff --git a/clatd_test.cpp b/clatd_test.cpp new file mode 100644 index 0000000..b70f9af --- /dev/null +++ b/clatd_test.cpp @@ -0,0 +1,672 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * clatd_test.cpp - unit tests for clatd + */ + +#include <iostream> + +#include <stdio.h> +#include <arpa/inet.h> +#include <sys/uio.h> + +#include <gtest/gtest.h> + +extern "C" { +#include "checksum.h" +#include "translate.h" +#include "config.h" +#include "clatd.h" +} + +// For convenience. +#define ARRAYSIZE(x) sizeof((x)) / sizeof((x)[0]) + +// Default translation parameters. +static const char kIPv4LocalAddr[] = "192.0.0.4"; +static const char kIPv6LocalAddr[] = "2001:db8:0:b11::464"; +static const char kIPv6PlatSubnet[] = "64:ff9b::"; + +// Test packet portions. Defined as macros because it's easy to concatenate them to make packets. +#define IPV4_HEADER(p, c1, c2) \ + 0x45, 0x00, 0, 41, /* Version=4, IHL=5, ToS=0x80, len=41 */ \ + 0x00, 0x00, 0x40, 0x00, /* ID=0x0000, flags=IP_DF, offset=0 */ \ + 55, (p), (c1), (c2), /* TTL=55, protocol=p, checksum=c1,c2 */ \ + 192, 0, 0, 4, /* Src=192.0.0.4 */ \ + 8, 8, 8, 8, /* Dst=8.8.8.8 */ +#define IPV4_UDP_HEADER IPV4_HEADER(IPPROTO_UDP, 0x73, 0xb0) +#define IPV4_ICMP_HEADER IPV4_HEADER(IPPROTO_ICMP, 0x73, 0xc0) + +#define IPV6_HEADER(p) \ + 0x60, 0x00, 0, 0, /* Version=6, tclass=0x00, flowlabel=0 */ \ + 0, 21, (p), 55, /* plen=11, nxthdr=p, hlim=55 */ \ + 0x20, 0x01, 0x0d, 0xb8, /* Src=2001:db8:0:b11::464 */ \ + 0x00, 0x00, 0x0b, 0x11, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x04, 0x64, \ + 0x00, 0x64, 0xff, 0x9b, /* Dst=64:ff9b::8.8.8.8 */ \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x08, 0x08, 0x08, 0x08, +#define IPV6_UDP_HEADER IPV6_HEADER(IPPROTO_UDP) +#define IPV6_ICMPV6_HEADER IPV6_HEADER(IPPROTO_ICMPV6) + +#define UDP_LEN 21 +#define UDP_HEADER \ + 0xc8, 0x8b, 0, 53, /* Port 51339->53 */ \ + 0x00, UDP_LEN, 0, 0, /* Length 21, checksum empty for now */ + +#define PAYLOAD 'H', 'e', 'l', 'l', 'o', ' ', 0x4e, 0xb8, 0x96, 0xe7, 0x95, 0x8c, 0x00 + +#define IPV4_PING \ + 0x08, 0x00, 0x88, 0xd0, /* Type 8, code 0, checksum 0x88d0 */ \ + 0xd0, 0x0d, 0x00, 0x03, /* ID=0xd00d, seq=3 */ + +#define IPV6_PING \ + 0x80, 0x00, 0xc3, 0x42, /* Type 128, code 0, checksum 0xc342 */ \ + 0xd0, 0x0d, 0x00, 0x03, /* ID=0xd00d, seq=3 */ + +// Macros to return pseudo-headers from packets. +#define IPV4_PSEUDOHEADER(ip, tlen) \ + ip[12], ip[13], ip[14], ip[15], /* Source address */ \ + ip[16], ip[17], ip[18], ip[19], /* Destination address */ \ + 0, ip[9], /* 0, protocol */ \ + ((tlen) >> 16) & 0xff, (tlen) & 0xff, /* Transport length */ + +#define IPV6_PSEUDOHEADER(ip6, protocol, tlen) \ + ip6[8], ip6[9], ip6[10], ip6[11], /* Source address */ \ + ip6[12], ip6[13], ip6[14], ip6[15], \ + ip6[16], ip6[17], ip6[18], ip6[19], \ + ip6[20], ip6[21], ip6[22], ip6[23], \ + ip6[24], ip6[25], ip6[26], ip6[27], /* Destination address */ \ + ip6[28], ip6[29], ip6[30], ip6[31], \ + ip6[32], ip6[33], ip6[34], ip6[35], \ + ip6[36], ip6[37], ip6[38], ip6[39], \ + ((tlen) >> 24) & 0xff, /* Transport length */ \ + ((tlen) >> 16) & 0xff, \ + ((tlen) >> 8) & 0xff, \ + (tlen) & 0xff, \ + 0, 0, 0, (protocol), + +// A fragmented DNS request. +static const char kIPv4Frag1[] = { + 0x45, 0x00, 0x00, 0x24, 0xfe, 0x47, 0x20, 0x00, 0x40, 0x11, + 0x8c, 0x6d, 0xc0, 0x00, 0x00, 0x04, 0x08, 0x08, 0x08, 0x08, + 0x14, 0x5d, 0x00, 0x35, 0x00, 0x29, 0x68, 0xbb, 0x50, 0x47, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00 +}; +static const char kIPv4Frag2[] = { + 0x45, 0x00, 0x00, 0x24, 0xfe, 0x47, 0x20, 0x02, 0x40, 0x11, + 0x8c, 0x6b, 0xc0, 0x00, 0x00, 0x04, 0x08, 0x08, 0x08, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x69, 0x70, 0x76, 0x34, 0x06, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65 +}; +static const char kIPv4Frag3[] = { + 0x45, 0x00, 0x00, 0x1d, 0xfe, 0x47, 0x00, 0x04, 0x40, 0x11, + 0xac, 0x70, 0xc0, 0x00, 0x00, 0x04, 0x08, 0x08, 0x08, 0x08, + 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01 +}; +static const char *kIPv4Fragments[] = { kIPv4Frag1, kIPv4Frag2, kIPv4Frag3 }; +static const int kIPv4FragLengths[] = { sizeof(kIPv4Frag1), sizeof(kIPv4Frag2), + sizeof(kIPv4Frag3) }; + +static const char kIPv6Frag1[] = { + 0x60, 0x00, 0x00, 0x00, 0x00, 0x18, 0x2c, 0x40, 0x20, 0x01, + 0x0d, 0xb8, 0x00, 0x00, 0x0b, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x64, 0x00, 0x64, 0xff, 0x9b, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, + 0x11, 0x00, 0x00, 0x01, 0x00, 0x00, 0xfe, 0x47, 0x14, 0x5d, + 0x00, 0x35, 0x00, 0x29, 0xeb, 0x91, 0x50, 0x47, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00 +}; + +static const char kIPv6Frag2[] = { + 0x60, 0x00, 0x00, 0x00, 0x00, 0x18, 0x2c, 0x40, 0x20, 0x01, + 0x0d, 0xb8, 0x00, 0x00, 0x0b, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x64, 0x00, 0x64, 0xff, 0x9b, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, + 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0xfe, 0x47, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x69, 0x70, 0x76, 0x34, 0x06, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65 +}; + +static const char kIPv6Frag3[] = { + 0x60, 0x00, 0x00, 0x00, 0x00, 0x11, 0x2c, 0x40, 0x20, 0x01, + 0x0d, 0xb8, 0x00, 0x00, 0x0b, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x64, 0x00, 0x64, 0xff, 0x9b, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, + 0x11, 0x00, 0x00, 0x20, 0x00, 0x00, 0xfe, 0x47, 0x03, 0x63, + 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01 +}; +static const char *kIPv6Fragments[] = { kIPv6Frag1, kIPv6Frag2, kIPv6Frag3 }; +static const int kIPv6FragLengths[] = { sizeof(kIPv6Frag1), sizeof(kIPv6Frag2), + sizeof(kIPv6Frag3) }; + +static const char kReassembledIPv4[] = { + 0x45, 0x00, 0x00, 0x3d, 0xfe, 0x47, 0x00, 0x00, 0x40, 0x11, + 0xac, 0x54, 0xc0, 0x00, 0x00, 0x04, 0x08, 0x08, 0x08, 0x08, + 0x14, 0x5d, 0x00, 0x35, 0x00, 0x29, 0x68, 0xbb, 0x50, 0x47, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x69, 0x70, 0x76, 0x34, 0x06, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, + 0x01 +}; + +// Expected checksums. +static const uint32_t kUdpPartialChecksum = 0xd5c8; +static const uint32_t kPayloadPartialChecksum = 0x31e9c; +static const uint16_t kUdpV4Checksum = 0xd0c7; +static const uint16_t kUdpV6Checksum = 0xa74a; + +int ip_version(const char *packet) { + int version = packet[0] >> 4; + return version; +} + +int is_ipv4_fragment(struct iphdr *ip) { + // A packet is a fragment if its fragment offset is nonzero or if the MF flag is set. + return ntohs(ip->frag_off) & (IP_OFFMASK | IP_MF); +} + +int is_ipv6_fragment(struct ip6_hdr *ip6, size_t len) { + if (ip6->ip6_nxt != IPPROTO_FRAGMENT) { + return 0; + } + struct ip6_frag *frag = (struct ip6_frag *) (ip6 + 1); + return len >= sizeof(*ip6) + sizeof(*frag) && + (frag->ip6f_offlg & (IP6F_OFF_MASK | IP6F_MORE_FRAG)); +} + +int ipv4_fragment_offset(struct iphdr *ip) { + return ntohs(ip->frag_off) & IP_OFFMASK; +} + +int ipv6_fragment_offset(struct ip6_frag *frag) { + return ntohs((frag->ip6f_offlg & IP6F_OFF_MASK) >> 3); +} + +void check_packet(const char *packet, size_t len, const char *msg) { + void *payload; + size_t payload_length = 0; + uint32_t pseudo_checksum = 0; + uint8_t protocol = 0; + int version = ip_version(packet); + switch (version) { + case 4: { + struct iphdr *ip = (struct iphdr *) packet; + ASSERT_GE(len, sizeof(*ip)) << msg << ": IPv4 packet shorter than IPv4 header\n"; + EXPECT_EQ(5, ip->ihl) << msg << ": Unsupported IP header length\n"; + EXPECT_EQ(len, ntohs(ip->tot_len)) << msg << ": Incorrect IPv4 length\n"; + EXPECT_EQ(0, ip_checksum(ip, sizeof(*ip))) << msg << ": Incorrect IP checksum\n"; + protocol = ip->protocol; + payload = ip + 1; + if (!is_ipv4_fragment(ip)) { + payload_length = len - sizeof(*ip); + pseudo_checksum = ipv4_pseudo_header_checksum(ip, payload_length); + } + ASSERT_TRUE(protocol == IPPROTO_TCP || protocol == IPPROTO_UDP || protocol == IPPROTO_ICMP) + << msg << ": Unsupported IPv4 protocol " << protocol << "\n"; + break; + } + case 6: { + struct ip6_hdr *ip6 = (struct ip6_hdr *) packet; + ASSERT_GE(len, sizeof(*ip6)) << msg << ": IPv6 packet shorter than IPv6 header\n"; + EXPECT_EQ(len - sizeof(*ip6), htons(ip6->ip6_plen)) << msg << ": Incorrect IPv6 length\n"; + + if (ip6->ip6_nxt == IPPROTO_FRAGMENT) { + struct ip6_frag *frag = (struct ip6_frag *) (ip6 + 1); + ASSERT_GE(len, sizeof(*ip6) + sizeof(*frag)) + << msg << ": IPv6 fragment: short fragment header\n"; + protocol = frag->ip6f_nxt; + payload = frag + 1; + // Even though the packet has a Fragment header, it might not be a fragment. + if (!is_ipv6_fragment(ip6, len)) { + payload_length = len - sizeof(*ip6) - sizeof(*frag); + } + } else { + // Since there are no extension headers except Fragment, this must be the payload. + protocol = ip6->ip6_nxt; + payload = ip6 + 1; + payload_length = len - sizeof(*ip6); + } + ASSERT_TRUE(protocol == IPPROTO_TCP || protocol == IPPROTO_UDP || protocol == IPPROTO_ICMPV6) + << msg << ": Unsupported IPv6 next header " << protocol; + if (payload_length) { + pseudo_checksum = ipv6_pseudo_header_checksum(ip6, payload_length, protocol); + } + break; + } + default: + FAIL() << msg << ": Unsupported IP version " << version << "\n"; + return; + } + + // If we understand the payload, verify the checksum. + if (payload_length) { + uint16_t checksum; + switch(protocol) { + case IPPROTO_UDP: + case IPPROTO_TCP: + case IPPROTO_ICMPV6: + checksum = ip_checksum_finish(ip_checksum_add(pseudo_checksum, payload, payload_length)); + break; + case IPPROTO_ICMP: + checksum = ip_checksum(payload, payload_length); + break; + default: + checksum = 0; // Don't check. + break; + } + EXPECT_EQ(0, checksum) << msg << ": Incorrect transport checksum\n"; + } + + if (protocol == IPPROTO_UDP) { + struct udphdr *udp = (struct udphdr *) payload; + EXPECT_NE(0, udp->check) << msg << ": UDP checksum 0 should be 0xffff"; + // If this is not a fragment, check the UDP length field. + if (payload_length) { + EXPECT_EQ(payload_length, ntohs(udp->len)) << msg << ": Incorrect UDP length\n"; + } + } +} + +void reassemble_packet(const char **fragments, const int lengths[], int numpackets, + char *reassembled, size_t *reassembled_len, const char *msg) { + struct iphdr *ip = NULL; + struct ip6_hdr *ip6 = NULL; + int total_length, pos = 0; + uint8_t protocol; + int version = ip_version(fragments[0]); + + for (int i = 0; i < numpackets; i++) { + const char *packet = fragments[i]; + int len = lengths[i]; + int headersize, payload_offset; + + ASSERT_EQ(ip_version(packet), version) << msg << ": Inconsistent fragment versions\n"; + check_packet(packet, len, "Fragment sanity check"); + + switch (version) { + case 4: { + struct iphdr *ip_orig = (struct iphdr *) packet; + headersize = sizeof(*ip_orig); + ASSERT_TRUE(is_ipv4_fragment(ip_orig)) + << msg << ": IPv4 fragment #" << i + 1 << " not a fragment\n"; + ASSERT_EQ(pos, ipv4_fragment_offset(ip_orig) * 8 + ((i != 0) ? sizeof(*ip): 0)) + << msg << ": IPv4 fragment #" << i + 1 << ": inconsistent offset\n"; + + headersize = sizeof(*ip_orig); + payload_offset = headersize; + if (pos == 0) { + ip = (struct iphdr *) reassembled; + } + break; + } + case 6: { + struct ip6_hdr *ip6_orig = (struct ip6_hdr *) packet; + struct ip6_frag *frag = (struct ip6_frag *) (ip6_orig + 1); + ASSERT_TRUE(is_ipv6_fragment(ip6_orig, len)) + << msg << ": IPv6 fragment #" << i + 1 << " not a fragment\n"; + ASSERT_EQ(pos, ipv6_fragment_offset(frag) * 8 + ((i != 0) ? sizeof(*ip6): 0)) + << msg << ": IPv6 fragment #" << i + 1 << ": inconsistent offset\n"; + + headersize = sizeof(*ip6_orig); + payload_offset = sizeof(*ip6_orig) + sizeof(*frag); + if (pos == 0) { + ip6 = (struct ip6_hdr *) reassembled; + protocol = frag->ip6f_nxt; + } + break; + } + default: + FAIL() << msg << ": Invalid IP version << " << version; + } + + // If this is the first fragment, copy the header. + if (pos == 0) { + ASSERT_LT(headersize, (int) *reassembled_len) << msg << ": Reassembly buffer too small\n"; + memcpy(reassembled, packet, headersize); + total_length = headersize; + pos += headersize; + } + + // Copy the payload. + int payload_length = len - payload_offset; + total_length += payload_length; + ASSERT_LT(total_length, (int) *reassembled_len) << msg << ": Reassembly buffer too small\n"; + memcpy(reassembled + pos, packet + payload_offset, payload_length); + pos += payload_length; + } + + + // Fix up the reassembled headers to reflect fragmentation and length (and IPv4 checksum). + ASSERT_EQ(total_length, pos) << msg << ": Reassembled packet length incorrect\n"; + if (ip) { + ip->frag_off &= ~htons(IP_MF); + ip->tot_len = htons(total_length); + ip->check = 0; + ip->check = ip_checksum(ip, sizeof(*ip)); + ASSERT_FALSE(is_ipv4_fragment(ip)) << msg << ": reassembled IPv4 packet is a fragment!\n"; + } + if (ip6) { + ip6->ip6_nxt = protocol; + ip6->ip6_plen = htons(total_length - sizeof(*ip6)); + ASSERT_FALSE(is_ipv6_fragment(ip6, ip6->ip6_plen)) + << msg << ": reassembled IPv6 packet is a fragment!\n"; + } + + *reassembled_len = total_length; +} + +void check_data_matches(const char *expected, const char *actual, size_t len, const char *msg) { + if (memcmp(expected, actual, len)) { + // Hex dump, 20 bytes per line, one space between bytes (1 byte = 3 chars), indented by 4. + int hexdump_len = len * 3 + (len / 20 + 1) * 5; + char expected_hexdump[hexdump_len], actual_hexdump[hexdump_len]; + unsigned pos = 0; + for (unsigned i = 0; i < len; i++) { + if (i % 20 == 0) { + sprintf(expected_hexdump + pos, "\n "); + sprintf(actual_hexdump + pos, "\n "); + pos += 4; + } + sprintf(expected_hexdump + pos, " %02x", expected[i]); + sprintf(actual_hexdump + pos, " %02x", actual[i]); + pos += 3; + } + FAIL() << msg << ": Translated packet doesn't match" + << "\n Expected:" << (char *) expected_hexdump + << "\n Actual:" << (char *) actual_hexdump << "\n"; + } +} + +void fix_udp_checksum(char* packet) { + uint32_t pseudo_checksum; + int version = ip_version(packet); + struct udphdr *udp; + switch (version) { + case 4: { + struct iphdr *ip = (struct iphdr *) packet; + udp = (struct udphdr *) (ip + 1); + pseudo_checksum = ipv4_pseudo_header_checksum(ip, ntohs(udp->len)); + break; + } + case 6: { + struct ip6_hdr *ip6 = (struct ip6_hdr *) packet; + udp = (struct udphdr *) (ip6 + 1); + pseudo_checksum = ipv6_pseudo_header_checksum(ip6, ntohs(udp->len), IPPROTO_UDP); + break; + } + default: + FAIL() << "unsupported IP version" << version << "\n"; + return; + } + + udp->check = 0; + udp->check = ip_checksum_finish(ip_checksum_add(pseudo_checksum, udp, ntohs(udp->len))); +} + +void do_translate_packet(const char *original, size_t original_len, char *out, size_t *outlen, + const char *msg) { + int fds[2]; + if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, fds)) { + abort(); + } + struct tun_data tunnel = { + "clat", "clat4", + fds[0], fds[1] + }; + struct tun_pi tun_header = { 0, 0 }; + + char foo[512]; + snprintf(foo, sizeof(foo), "%s: Invalid original packet", msg); + check_packet(original, original_len, foo); + + int read_fd; + uint16_t expected_proto; + int version = ip_version(original); + switch (version) { + case 4: + tun_header.proto = htons(ETH_P_IP); + expected_proto = htons(ETH_P_IPV6); + read_fd = fds[1]; + break; + case 6: + tun_header.proto = htons(ETH_P_IPV6); + expected_proto = htons(ETH_P_IP); + read_fd = fds[0]; + break; + default: + FAIL() << msg << ": Unsupported IP version " << version << "\n"; + break; + } + + translate_packet(&tunnel, &tun_header, original, original_len); + + struct tun_pi new_tun_header; + struct iovec iov[] = { + { &new_tun_header, sizeof(new_tun_header) }, + { out, *outlen } + }; + int len = readv(read_fd, iov, 2); + if (len > (int) sizeof(new_tun_header)) { + ASSERT_LT((size_t) len, *outlen) << msg << ": Translated packet buffer too small\n"; + EXPECT_EQ(expected_proto, new_tun_header.proto) << msg << "Unexpected tun proto\n"; + *outlen = len - sizeof(new_tun_header); + } else { + FAIL() << msg << ": Packet was not translated"; + *outlen = 0; + } +} + +void check_translated_packet(const char *original, size_t original_len, + const char *expected, size_t expected_len, const char *msg) { + char translated[MAXMTU]; + size_t translated_len = sizeof(translated); + do_translate_packet(original, original_len, translated, &translated_len, msg); + EXPECT_EQ(expected_len, translated_len) << msg << ": Translated packet length incorrect\n"; + check_data_matches(expected, translated, translated_len, msg); +} + +void check_fragment_translation(const char *original[], const int original_lengths[], + const char *expected[], const int expected_lengths[], + int numfragments, const char *msg) { + for (int i = 0; i < numfragments; i++) { + // Check that each of the fragments translates as expected. + char frag_msg[512]; + snprintf(frag_msg, sizeof(frag_msg), "%s: fragment #%d", msg, i + 1); + check_translated_packet(original[i], original_lengths[i], + expected[i], expected_lengths[i], frag_msg); + } + + // Sanity check that reassembling the original and translated fragments produces valid packets. + char reassembled[MAXMTU]; + size_t reassembled_len = sizeof(reassembled); + reassemble_packet(original, original_lengths, numfragments, reassembled, &reassembled_len, msg); + check_packet(reassembled, reassembled_len, msg); + + char translated[MAXMTU]; + size_t translated_len = sizeof(translated); + do_translate_packet(reassembled, reassembled_len, translated, &translated_len, msg); + check_packet(translated, translated_len, msg); +} + +struct clat_config Global_Clatd_Config; + +class ClatdTest : public ::testing::Test { + protected: + virtual void SetUp() { + inet_pton(AF_INET, kIPv4LocalAddr, &Global_Clatd_Config.ipv4_local_subnet); + inet_pton(AF_INET6, kIPv6PlatSubnet, &Global_Clatd_Config.plat_subnet); + inet_pton(AF_INET6, kIPv6LocalAddr, &Global_Clatd_Config.ipv6_local_subnet); + } +}; + +TEST_F(ClatdTest, Sanitycheck) { + // Sanity checks the data. + char v4_header[] = { IPV4_UDP_HEADER }; + ASSERT_EQ(sizeof(struct iphdr), sizeof(v4_header)) << "Test IPv4 header: incorrect length\n"; + + char v6_header[] = { IPV6_UDP_HEADER }; + ASSERT_EQ(sizeof(struct ip6_hdr), sizeof(v6_header)) << "Test IPv6 header: incorrect length\n"; + + char udp_header[] = { UDP_HEADER }; + ASSERT_EQ(sizeof(struct udphdr), sizeof(udp_header)) << "Test UDP header: incorrect length\n"; + + // Sanity checks check_packet. + struct udphdr *udp; + char v4_udp_packet[] = { IPV4_UDP_HEADER UDP_HEADER PAYLOAD }; + udp = (struct udphdr *) (v4_udp_packet + sizeof(struct iphdr)); + fix_udp_checksum(v4_udp_packet); + ASSERT_EQ(kUdpV4Checksum, udp->check) << "UDP/IPv4 packet checksum sanity check\n"; + check_packet(v4_udp_packet, sizeof(v4_udp_packet), "UDP/IPv4 packet sanity check"); + + char v6_udp_packet[] = { IPV6_UDP_HEADER UDP_HEADER PAYLOAD }; + udp = (struct udphdr *) (v6_udp_packet + sizeof(struct ip6_hdr)); + fix_udp_checksum(v6_udp_packet); + ASSERT_EQ(kUdpV6Checksum, udp->check) << "UDP/IPv6 packet checksum sanity check\n"; + check_packet(v6_udp_packet, sizeof(v6_udp_packet), "UDP/IPv6 packet sanity check"); + + char ipv4_ping[] = { IPV4_ICMP_HEADER IPV4_PING PAYLOAD }; + check_packet(ipv4_ping, sizeof(ipv4_ping), "IPv4 ping sanity check"); + + char ipv6_ping[] = { IPV6_ICMPV6_HEADER IPV6_PING PAYLOAD }; + check_packet(ipv6_ping, sizeof(ipv6_ping), "IPv6 ping sanity check"); + + // Sanity checks reassemble_packet. + char reassembled[MAXMTU]; + size_t total_length = sizeof(reassembled); + reassemble_packet(kIPv4Fragments, kIPv4FragLengths, ARRAYSIZE(kIPv4Fragments), + reassembled, &total_length, "Reassembly sanity check"); + check_packet(reassembled, total_length, "IPv4 Reassembled packet is valid"); + ASSERT_EQ(sizeof(kReassembledIPv4), total_length) << "IPv4 reassembly sanity check: length\n"; + ASSERT_TRUE(!is_ipv4_fragment((struct iphdr *) reassembled)) + << "Sanity check: reassembled packet is a fragment!\n"; + check_data_matches(kReassembledIPv4, reassembled, total_length, "IPv4 reassembly sanity check"); + + total_length = sizeof(reassembled); + reassemble_packet(kIPv6Fragments, kIPv6FragLengths, ARRAYSIZE(kIPv6Fragments), + reassembled, &total_length, "IPv6 reassembly sanity check"); + ASSERT_TRUE(!is_ipv6_fragment((struct ip6_hdr *) reassembled, total_length)) + << "Sanity check: reassembled packet is a fragment!\n"; + check_packet(reassembled, total_length, "IPv6 Reassembled packet is valid"); +} + +TEST_F(ClatdTest, PseudoChecksum) { + uint32_t pseudo_checksum; + + char v4_header[] = { IPV4_UDP_HEADER }; + char v4_pseudo_header[] = { IPV4_PSEUDOHEADER(v4_header, UDP_LEN) }; + pseudo_checksum = ipv4_pseudo_header_checksum((struct iphdr *) v4_header, UDP_LEN); + EXPECT_EQ(ip_checksum_finish(pseudo_checksum), + ip_checksum(v4_pseudo_header, sizeof(v4_pseudo_header))) + << "ipv4_pseudo_header_checksum incorrect\n"; + + char v6_header[] = { IPV6_UDP_HEADER }; + char v6_pseudo_header[] = { IPV6_PSEUDOHEADER(v6_header, IPPROTO_UDP, UDP_LEN) }; + pseudo_checksum = ipv6_pseudo_header_checksum((struct ip6_hdr *) v6_header, UDP_LEN, IPPROTO_UDP); + EXPECT_EQ(ip_checksum_finish(pseudo_checksum), + ip_checksum(v6_pseudo_header, sizeof(v6_pseudo_header))) + << "ipv6_pseudo_header_checksum incorrect\n"; +} + +TEST_F(ClatdTest, TransportChecksum) { + char udphdr[] = { UDP_HEADER }; + char payload[] = { PAYLOAD }; + EXPECT_EQ(kUdpPartialChecksum, ip_checksum_add(0, udphdr, sizeof(udphdr))) + << "UDP partial checksum\n"; + EXPECT_EQ(kPayloadPartialChecksum, ip_checksum_add(0, payload, sizeof(payload))) + << "Payload partial checksum\n"; + + char ip[] = { IPV4_UDP_HEADER }; + char ip6[] = { IPV6_UDP_HEADER }; + uint32_t ipv4_pseudo_sum = ipv4_pseudo_header_checksum((struct iphdr *) ip, UDP_LEN); + uint32_t ipv6_pseudo_sum = ipv6_pseudo_header_checksum((struct ip6_hdr *) ip6, UDP_LEN, + IPPROTO_UDP); + + EXPECT_EQ(0x3ad0, ipv4_pseudo_sum) << "IPv4 pseudo-checksum sanity check\n"; + EXPECT_EQ(0x2644b, ipv6_pseudo_sum) << "IPv6 pseudo-checksum sanity check\n"; + EXPECT_EQ( + kUdpV4Checksum, + ip_checksum_finish(ipv4_pseudo_sum + kUdpPartialChecksum + kPayloadPartialChecksum)) + << "Unexpected UDP/IPv4 checksum\n"; + EXPECT_EQ( + kUdpV6Checksum, + ip_checksum_finish(ipv6_pseudo_sum + kUdpPartialChecksum + kPayloadPartialChecksum)) + << "Unexpected UDP/IPv6 checksum\n"; + + EXPECT_EQ(kUdpV6Checksum, + ip_checksum_adjust(kUdpV4Checksum, ipv4_pseudo_sum, ipv6_pseudo_sum)) + << "Adjust IPv4/UDP checksum to IPv6\n"; + EXPECT_EQ(kUdpV4Checksum, + ip_checksum_adjust(kUdpV6Checksum, ipv6_pseudo_sum, ipv4_pseudo_sum)) + << "Adjust IPv6/UDP checksum to IPv4\n"; +} + +TEST_F(ClatdTest, AdjustChecksum) { + struct checksum_data { + uint16_t checksum; + uint32_t old_hdr_sum; + uint32_t new_hdr_sum; + uint16_t result; + } DATA[] = { + { 0x1423, 0xb8ec, 0x2d757, 0xf5b5 }, + { 0xf5b5, 0x2d757, 0xb8ec, 0x1423 }, + { 0xdd2f, 0x5555, 0x3285, 0x0000 }, + { 0x1215, 0x5560, 0x15560 + 20, 0x1200 }, + { 0xd0c7, 0x3ad0, 0x2644b, 0xa74a }, + }; + unsigned i, failed = 0; + + for (i = 0; i < ARRAYSIZE(DATA); i++) { + struct checksum_data *data = DATA + i; + uint16_t result = ip_checksum_adjust(data->checksum, data->old_hdr_sum, data->new_hdr_sum); + EXPECT_EQ(result, data->result) + << "Incorrect checksum" << std::showbase << std::hex + << "\n Expected: " << data->result + << "\n Actual: " << result + << "\n checksum=" << data->checksum + << " old_sum=" << data->old_hdr_sum << " new_sum=" << data->new_hdr_sum << "\n"; + } +} + +TEST_F(ClatdTest, Translate) { + char udp_ipv4[] = { IPV4_UDP_HEADER UDP_HEADER PAYLOAD }; + char udp_ipv6[] = { IPV6_UDP_HEADER UDP_HEADER PAYLOAD }; + fix_udp_checksum(udp_ipv4); + fix_udp_checksum(udp_ipv6); + check_translated_packet(udp_ipv4, sizeof(udp_ipv4), udp_ipv6, sizeof(udp_ipv6), + "UDP/IPv4 -> UDP/IPv6 translation"); + check_translated_packet(udp_ipv6, sizeof(udp_ipv6), udp_ipv4, sizeof(udp_ipv4), + "UDP/IPv6 -> UDP/IPv4 translation"); + + char ipv4_ping[] = { IPV4_ICMP_HEADER IPV4_PING PAYLOAD }; + char ipv6_ping[] = { IPV6_ICMPV6_HEADER IPV6_PING PAYLOAD }; + check_translated_packet(ipv4_ping, sizeof(ipv4_ping), ipv6_ping, sizeof(ipv6_ping), + "ICMP->ICMPv6 translation"); + check_translated_packet(ipv6_ping, sizeof(ipv6_ping), ipv4_ping, sizeof(ipv4_ping), + "ICMPv6->ICMP translation"); +} + +TEST_F(ClatdTest, Fragmentation) { + int len, i; + check_fragment_translation(kIPv4Fragments, kIPv4FragLengths, + kIPv6Fragments, kIPv6FragLengths, + ARRAYSIZE(kIPv4Fragments), "IPv4->IPv6 fragment translation"); + + check_fragment_translation(kIPv6Fragments, kIPv6FragLengths, + kIPv4Fragments, kIPv4FragLengths, + ARRAYSIZE(kIPv6Fragments), "IPv6->IPv4 fragment translation"); +} |