| /* |
| * Copyright 2018 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. |
| * |
| * ClatdControllerTest.cpp - unit tests for ClatdController.cpp |
| */ |
| |
| #include <arpa/inet.h> |
| #include <netinet/in.h> |
| #include <string> |
| |
| #include <gtest/gtest.h> |
| |
| #include <android-base/stringprintf.h> |
| #include <android-base/strings.h> |
| #include <netutils/ifc.h> |
| |
| extern "C" { |
| #include <netutils/checksum.h> |
| } |
| |
| #include "ClatdController.h" |
| #include "IptablesBaseTest.h" |
| #include "NetworkController.h" |
| #include "tun_interface.h" |
| |
| static const char kIPv4LocalAddr[] = "192.0.0.4"; |
| |
| namespace android { |
| namespace net { |
| |
| using android::base::StringPrintf; |
| |
| // Mock functions for isIpv4AddressFree. |
| bool neverFree(in_addr_t /* addr */) { |
| return 0; |
| } |
| bool alwaysFree(in_addr_t /* addr */) { |
| return 1; |
| } |
| bool only2Free(in_addr_t addr) { |
| return (ntohl(addr) & 0xff) == 2; |
| } |
| bool over6Free(in_addr_t addr) { |
| return (ntohl(addr) & 0xff) >= 6; |
| } |
| bool only10Free(in_addr_t addr) { |
| return (ntohl(addr) & 0xff) == 10; |
| } |
| |
| class ClatdControllerTest : public IptablesBaseTest { |
| public: |
| ClatdControllerTest() : mClatdCtrl(nullptr) { |
| ClatdController::iptablesRestoreFunction = fakeExecIptablesRestore; |
| } |
| |
| void SetUp() { resetIpv4AddressFreeFunc(); } |
| |
| protected: |
| ClatdController mClatdCtrl; |
| void setIptablesDropRule(bool a, const char* b, const char* c, const char* d) { |
| std::lock_guard guard(mClatdCtrl.mutex); |
| return mClatdCtrl.setIptablesDropRule(a, b, c, d); |
| } |
| void setIpv4AddressFreeFunc(bool (*func)(in_addr_t)) { |
| ClatdController::isIpv4AddressFreeFunc = func; |
| } |
| void resetIpv4AddressFreeFunc() { |
| ClatdController::isIpv4AddressFreeFunc = ClatdController::isIpv4AddressFree; |
| } |
| in_addr_t selectIpv4Address(const in_addr a, int16_t b) { |
| return ClatdController::selectIpv4Address(a, b); |
| } |
| void makeChecksumNeutral(in6_addr* a, const in_addr b, const in6_addr& c) { |
| ClatdController::makeChecksumNeutral(a, b, c); |
| } |
| }; |
| |
| TEST_F(ClatdControllerTest, SelectIpv4Address) { |
| struct in_addr addr; |
| |
| inet_pton(AF_INET, kIPv4LocalAddr, &addr); |
| |
| // If no addresses are free, return INADDR_NONE. |
| setIpv4AddressFreeFunc(neverFree); |
| EXPECT_EQ(INADDR_NONE, selectIpv4Address(addr, 29)); |
| EXPECT_EQ(INADDR_NONE, selectIpv4Address(addr, 16)); |
| |
| // If the configured address is free, pick that. But a prefix that's too big is invalid. |
| setIpv4AddressFreeFunc(alwaysFree); |
| EXPECT_EQ(inet_addr(kIPv4LocalAddr), selectIpv4Address(addr, 29)); |
| EXPECT_EQ(inet_addr(kIPv4LocalAddr), selectIpv4Address(addr, 20)); |
| EXPECT_EQ(INADDR_NONE, selectIpv4Address(addr, 15)); |
| |
| // A prefix length of 32 works, but anything above it is invalid. |
| EXPECT_EQ(inet_addr(kIPv4LocalAddr), selectIpv4Address(addr, 32)); |
| EXPECT_EQ(INADDR_NONE, selectIpv4Address(addr, 33)); |
| |
| // If another address is free, pick it. |
| setIpv4AddressFreeFunc(over6Free); |
| EXPECT_EQ(inet_addr("192.0.0.6"), selectIpv4Address(addr, 29)); |
| |
| // Check that we wrap around to addresses that are lower than the first address. |
| setIpv4AddressFreeFunc(only2Free); |
| EXPECT_EQ(inet_addr("192.0.0.2"), selectIpv4Address(addr, 29)); |
| EXPECT_EQ(INADDR_NONE, selectIpv4Address(addr, 30)); |
| |
| // If a free address exists outside the prefix, we don't pick it. |
| setIpv4AddressFreeFunc(only10Free); |
| EXPECT_EQ(INADDR_NONE, selectIpv4Address(addr, 29)); |
| EXPECT_EQ(inet_addr("192.0.0.10"), selectIpv4Address(addr, 24)); |
| |
| // Now try using the real function which sees if IP addresses are free using bind(). |
| // Assume that the machine running the test has the address 127.0.0.1, but not 8.8.8.8. |
| resetIpv4AddressFreeFunc(); |
| addr.s_addr = inet_addr("8.8.8.8"); |
| EXPECT_EQ(inet_addr("8.8.8.8"), selectIpv4Address(addr, 29)); |
| |
| addr.s_addr = inet_addr("127.0.0.1"); |
| EXPECT_EQ(inet_addr("127.0.0.2"), selectIpv4Address(addr, 29)); |
| } |
| |
| TEST_F(ClatdControllerTest, MakeChecksumNeutral) { |
| // We can't test generateIPv6Address here since it requires manipulating routing, which we can't |
| // do without talking to the real netd on the system. |
| uint32_t rand = arc4random_uniform(0xffffffff); |
| uint16_t rand1 = rand & 0xffff; |
| uint16_t rand2 = (rand >> 16) & 0xffff; |
| std::string v6PrefixStr = StringPrintf("2001:db8:%x:%x", rand1, rand2); |
| std::string v6InterfaceAddrStr = StringPrintf("%s::%x:%x", v6PrefixStr.c_str(), rand2, rand1); |
| std::string nat64PrefixStr = StringPrintf("2001:db8:%x:%x::", rand2, rand1); |
| |
| in_addr v4 = {inet_addr(kIPv4LocalAddr)}; |
| in6_addr v6InterfaceAddr; |
| ASSERT_TRUE(inet_pton(AF_INET6, v6InterfaceAddrStr.c_str(), &v6InterfaceAddr)); |
| in6_addr nat64Prefix; |
| ASSERT_TRUE(inet_pton(AF_INET6, nat64PrefixStr.c_str(), &nat64Prefix)); |
| |
| // Generate a boatload of random IIDs. |
| int onebits = 0; |
| uint64_t prev_iid = 0; |
| for (int i = 0; i < 100000; i++) { |
| in6_addr v6 = v6InterfaceAddr; |
| makeChecksumNeutral(&v6, v4, nat64Prefix); |
| |
| // Check the generated IP address is in the same prefix as the interface IPv6 address. |
| EXPECT_EQ(0, memcmp(&v6, &v6InterfaceAddr, 8)); |
| |
| // Check that consecutive IIDs are not the same. |
| uint64_t iid = *(uint64_t*)(&v6.s6_addr[8]); |
| ASSERT_TRUE(iid != prev_iid) |
| << "Two consecutive random IIDs are the same: " << std::showbase << std::hex << iid |
| << "\n"; |
| prev_iid = iid; |
| |
| // Check that the IID is checksum-neutral with the NAT64 prefix and the |
| // local prefix. |
| uint16_t c1 = ip_checksum_finish(ip_checksum_add(0, &v4, sizeof(v4))); |
| uint16_t c2 = ip_checksum_finish(ip_checksum_add(0, &nat64Prefix, sizeof(nat64Prefix)) + |
| ip_checksum_add(0, &v6, sizeof(v6))); |
| |
| if (c1 != c2) { |
| char v6Str[INET6_ADDRSTRLEN]; |
| inet_ntop(AF_INET6, &v6, v6Str, sizeof(v6Str)); |
| FAIL() << "Bad IID: " << v6Str << " not checksum-neutral with " << kIPv4LocalAddr |
| << " and " << nat64PrefixStr.c_str() << std::showbase << std::hex |
| << "\n IPv4 checksum: " << c1 << "\n IPv6 checksum: " << c2 << "\n"; |
| } |
| |
| // Check that IIDs are roughly random and use all the bits by counting the |
| // total number of bits set to 1 in a random sample of 100000 generated IIDs. |
| onebits += __builtin_popcountll(*(uint64_t*)&iid); |
| } |
| EXPECT_LE(3190000, onebits); |
| EXPECT_GE(3210000, onebits); |
| } |
| |
| TEST_F(ClatdControllerTest, AddIptablesRule) { |
| setIptablesDropRule(true, "wlan0", "64:ff9b::", "2001:db8::1:2:3:4"); |
| expectIptablesRestoreCommands((ExpectedIptablesCommands){ |
| {V6, |
| "*raw\n" |
| "-A clat_raw_PREROUTING -i wlan0 -s 64:ff9b::/96 -d 2001:db8::1:2:3:4 -j DROP\n" |
| "COMMIT\n"}}); |
| } |
| |
| TEST_F(ClatdControllerTest, RemoveIptablesRule) { |
| setIptablesDropRule(false, "wlan0", "64:ff9b::", "2001:db8::a:b:c:d"); |
| expectIptablesRestoreCommands((ExpectedIptablesCommands){ |
| {V6, |
| "*raw\n" |
| "-D clat_raw_PREROUTING -i wlan0 -s 64:ff9b::/96 -d 2001:db8::a:b:c:d -j DROP\n" |
| "COMMIT\n"}}); |
| } |
| |
| } // namespace net |
| } // namespace android |