| // Copyright 2018 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "patchpanel/subnet.h" |
| |
| #include <arpa/inet.h> |
| #include <stdint.h> |
| |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include <base/bind.h> |
| #include <base/bind_helpers.h> |
| #include <base/strings/string_util.h> |
| #include <gtest/gtest.h> |
| |
| #include "patchpanel/net_util.h" |
| |
| namespace patchpanel { |
| namespace { |
| |
| constexpr size_t kContainerBaseAddress = Ipv4Addr(100, 115, 92, 192); |
| constexpr size_t kVmBaseAddress = Ipv4Addr(100, 115, 92, 24); |
| constexpr size_t kPluginBaseAddress = Ipv4Addr(100, 115, 92, 128); |
| |
| constexpr size_t kContainerSubnetPrefixLength = 28; |
| constexpr size_t kVmSubnetPrefixLength = 30; |
| constexpr size_t kPluginSubnetPrefixLength = 28; |
| |
| uint32_t AddOffset(uint32_t base_addr_no, uint32_t offset_ho) { |
| return htonl(ntohl(base_addr_no) + offset_ho); |
| } |
| |
| // kExpectedAvailableCount[i] == AvailableCount() for subnet with prefix_length |
| // i. |
| constexpr size_t kExpectedAvailableCount[] = { |
| 0xfffffffe, 0x7ffffffe, 0x3ffffffe, 0x1ffffffe, 0xffffffe, 0x7fffffe, |
| 0x3fffffe, 0x1fffffe, 0xfffffe, 0x7ffffe, 0x3ffffe, 0x1ffffe, |
| 0xffffe, 0x7fffe, 0x3fffe, 0x1fffe, 0xfffe, 0x7ffe, |
| 0x3ffe, 0x1ffe, 0xffe, 0x7fe, 0x3fe, 0x1fe, |
| 0xfe, 0x7e, 0x3e, 0x1e, 0xe, 0x6, |
| 0x2, 0x0, |
| }; |
| |
| // kExpectedNetmask[i] == Netmask() for subnet with prefix_length i. |
| constexpr uint32_t kExpectedNetmask[] = { |
| Ipv4Addr(0, 0, 0, 0), Ipv4Addr(128, 0, 0, 0), |
| Ipv4Addr(192, 0, 0, 0), Ipv4Addr(224, 0, 0, 0), |
| Ipv4Addr(240, 0, 0, 0), Ipv4Addr(248, 0, 0, 0), |
| Ipv4Addr(252, 0, 0, 0), Ipv4Addr(254, 0, 0, 0), |
| Ipv4Addr(255, 0, 0, 0), Ipv4Addr(255, 128, 0, 0), |
| Ipv4Addr(255, 192, 0, 0), Ipv4Addr(255, 224, 0, 0), |
| Ipv4Addr(255, 240, 0, 0), Ipv4Addr(255, 248, 0, 0), |
| Ipv4Addr(255, 252, 0, 0), Ipv4Addr(255, 254, 0, 0), |
| Ipv4Addr(255, 255, 0, 0), Ipv4Addr(255, 255, 128, 0), |
| Ipv4Addr(255, 255, 192, 0), Ipv4Addr(255, 255, 224, 0), |
| Ipv4Addr(255, 255, 240, 0), Ipv4Addr(255, 255, 248, 0), |
| Ipv4Addr(255, 255, 252, 0), Ipv4Addr(255, 255, 254, 0), |
| Ipv4Addr(255, 255, 255, 0), Ipv4Addr(255, 255, 255, 128), |
| Ipv4Addr(255, 255, 255, 192), Ipv4Addr(255, 255, 255, 224), |
| Ipv4Addr(255, 255, 255, 240), Ipv4Addr(255, 255, 255, 248), |
| Ipv4Addr(255, 255, 255, 252), Ipv4Addr(255, 255, 255, 254), |
| }; |
| |
| // kExpectedPrefix[i] == Prefix() for subnet with 4 * i offset to |
| // |kVmBaseAddress|. |
| constexpr uint32_t kExpectedPrefix[] = { |
| Ipv4Addr(100, 115, 92, 24), Ipv4Addr(100, 115, 92, 28), |
| Ipv4Addr(100, 115, 92, 32), Ipv4Addr(100, 115, 92, 36), |
| Ipv4Addr(100, 115, 92, 40), Ipv4Addr(100, 115, 92, 44), |
| Ipv4Addr(100, 115, 92, 48), Ipv4Addr(100, 115, 92, 52), |
| Ipv4Addr(100, 115, 92, 56), Ipv4Addr(100, 115, 92, 60), |
| Ipv4Addr(100, 115, 92, 64), Ipv4Addr(100, 115, 92, 68), |
| Ipv4Addr(100, 115, 92, 72), Ipv4Addr(100, 115, 92, 76), |
| Ipv4Addr(100, 115, 92, 80), Ipv4Addr(100, 115, 92, 84), |
| Ipv4Addr(100, 115, 92, 88), Ipv4Addr(100, 115, 92, 92), |
| Ipv4Addr(100, 115, 92, 96), Ipv4Addr(100, 115, 92, 100), |
| Ipv4Addr(100, 115, 92, 104), Ipv4Addr(100, 115, 92, 108), |
| Ipv4Addr(100, 115, 92, 112), Ipv4Addr(100, 115, 92, 116), |
| Ipv4Addr(100, 115, 92, 120), Ipv4Addr(100, 115, 92, 124), |
| Ipv4Addr(100, 115, 92, 128), Ipv4Addr(100, 115, 92, 132), |
| Ipv4Addr(100, 115, 92, 136), Ipv4Addr(100, 115, 92, 140), |
| Ipv4Addr(100, 115, 92, 144), Ipv4Addr(100, 115, 92, 148), |
| }; |
| |
| // kExpectedCidrString[i] == ToCidrString() for subnet with 4 * i offset to |
| // |kVmBaseAddress|. |
| const char* kExpectedCidrString[] = { |
| "100.115.92.24/30", "100.115.92.28/30", "100.115.92.32/30", |
| "100.115.92.36/30", "100.115.92.40/30", "100.115.92.44/30", |
| "100.115.92.48/30", "100.115.92.52/30", "100.115.92.56/30", |
| "100.115.92.60/30", "100.115.92.64/30", "100.115.92.68/30", |
| "100.115.92.72/30", "100.115.92.76/30", "100.115.92.80/30", |
| "100.115.92.84/30", "100.115.92.88/30", "100.115.92.92/30", |
| "100.115.92.96/30", "100.115.92.100/30", "100.115.92.104/30", |
| "100.115.92.108/30", "100.115.92.112/30", "100.115.92.116/30", |
| "100.115.92.120/30", "100.115.92.124/30", "100.115.92.128/30", |
| "100.115.92.132/30", "100.115.92.136/30", "100.115.92.140/30", |
| "100.115.92.144/30", "100.115.92.148/30", |
| }; |
| |
| class VmSubnetTest : public ::testing::TestWithParam<size_t> {}; |
| class ContainerSubnetTest : public ::testing::TestWithParam<size_t> {}; |
| class PrefixTest : public ::testing::TestWithParam<size_t> {}; |
| |
| void DoNothing() {} |
| |
| void SetTrue(bool* value) { |
| *value = true; |
| } |
| |
| } // namespace |
| |
| TEST_P(VmSubnetTest, Prefix) { |
| size_t index = GetParam(); |
| Subnet subnet(AddOffset(kVmBaseAddress, index * 4), kVmSubnetPrefixLength, |
| base::Bind(&DoNothing)); |
| |
| EXPECT_EQ(kExpectedPrefix[index], subnet.Prefix()); |
| } |
| |
| TEST_P(VmSubnetTest, CidrString) { |
| size_t index = GetParam(); |
| Subnet subnet(AddOffset(kVmBaseAddress, index * 4), kVmSubnetPrefixLength, |
| base::Bind(&DoNothing)); |
| |
| EXPECT_EQ(std::string(kExpectedCidrString[index]), subnet.ToCidrString()); |
| EXPECT_EQ(kExpectedCidrString[index], subnet.ToCidrString()); |
| } |
| |
| TEST_P(VmSubnetTest, AddressAtOffset) { |
| size_t index = GetParam(); |
| Subnet subnet(AddOffset(kVmBaseAddress, index * 4), kVmSubnetPrefixLength, |
| base::Bind(&DoNothing)); |
| |
| for (uint32_t offset = 0; offset < subnet.AvailableCount(); ++offset) { |
| uint32_t address = AddOffset(kVmBaseAddress, index * 4 + offset + 1); |
| EXPECT_EQ(address, subnet.AddressAtOffset(offset)); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(AllValues, |
| VmSubnetTest, |
| ::testing::Range(size_t{0}, size_t{26})); |
| |
| TEST_P(ContainerSubnetTest, AddressAtOffset) { |
| size_t index = GetParam(); |
| Subnet subnet(AddOffset(kContainerBaseAddress, index * 16), |
| kContainerSubnetPrefixLength, base::Bind(&DoNothing)); |
| |
| for (uint32_t offset = 0; offset < subnet.AvailableCount(); ++offset) { |
| uint32_t address = |
| AddOffset(kContainerBaseAddress, index * 16 + offset + 1); |
| EXPECT_EQ(address, subnet.AddressAtOffset(offset)); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(AllValues, |
| ContainerSubnetTest, |
| ::testing::Range(size_t{1}, size_t{4})); |
| |
| TEST_P(PrefixTest, AvailableCount) { |
| size_t prefix_length = GetParam(); |
| |
| Subnet subnet(0, prefix_length, base::Bind(&DoNothing)); |
| EXPECT_EQ(kExpectedAvailableCount[prefix_length], subnet.AvailableCount()); |
| } |
| |
| TEST_P(PrefixTest, Netmask) { |
| size_t prefix_length = GetParam(); |
| |
| Subnet subnet(0, prefix_length, base::Bind(&DoNothing)); |
| EXPECT_EQ(kExpectedNetmask[prefix_length], subnet.Netmask()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(AllValues, |
| PrefixTest, |
| ::testing::Range(size_t{8}, size_t{32})); |
| |
| TEST(SubtnetAddress, StringConversion) { |
| Subnet container_subnet(kContainerBaseAddress, kContainerSubnetPrefixLength, |
| base::Bind(&DoNothing)); |
| EXPECT_EQ("100.115.92.192/28", container_subnet.ToCidrString()); |
| { |
| EXPECT_EQ("100.115.92.193", |
| container_subnet.AllocateAtOffset(0)->ToIPv4String()); |
| EXPECT_EQ("100.115.92.194", |
| container_subnet.AllocateAtOffset(1)->ToIPv4String()); |
| EXPECT_EQ("100.115.92.205", |
| container_subnet.AllocateAtOffset(12)->ToIPv4String()); |
| EXPECT_EQ("100.115.92.206", |
| container_subnet.AllocateAtOffset(13)->ToIPv4String()); |
| } |
| { |
| EXPECT_EQ("100.115.92.193/28", |
| container_subnet.AllocateAtOffset(0)->ToCidrString()); |
| EXPECT_EQ("100.115.92.194/28", |
| container_subnet.AllocateAtOffset(1)->ToCidrString()); |
| EXPECT_EQ("100.115.92.205/28", |
| container_subnet.AllocateAtOffset(12)->ToCidrString()); |
| EXPECT_EQ("100.115.92.206/28", |
| container_subnet.AllocateAtOffset(13)->ToCidrString()); |
| } |
| |
| Subnet vm_subnet(kVmBaseAddress, kVmSubnetPrefixLength, |
| base::Bind(&DoNothing)); |
| EXPECT_EQ("100.115.92.24/30", vm_subnet.ToCidrString()); |
| { |
| EXPECT_EQ("100.115.92.25", vm_subnet.AllocateAtOffset(0)->ToIPv4String()); |
| EXPECT_EQ("100.115.92.26", vm_subnet.AllocateAtOffset(1)->ToIPv4String()); |
| } |
| { |
| EXPECT_EQ("100.115.92.25/30", |
| vm_subnet.AllocateAtOffset(0)->ToCidrString()); |
| EXPECT_EQ("100.115.92.26/30", |
| vm_subnet.AllocateAtOffset(1)->ToCidrString()); |
| } |
| |
| Subnet plugin_subnet(kPluginBaseAddress, kPluginSubnetPrefixLength, |
| base::Bind(&DoNothing)); |
| EXPECT_EQ("100.115.92.128/28", plugin_subnet.ToCidrString()); |
| { |
| EXPECT_EQ("100.115.92.129", |
| plugin_subnet.AllocateAtOffset(0)->ToIPv4String()); |
| EXPECT_EQ("100.115.92.130", |
| plugin_subnet.AllocateAtOffset(1)->ToIPv4String()); |
| EXPECT_EQ("100.115.92.141", |
| plugin_subnet.AllocateAtOffset(12)->ToIPv4String()); |
| EXPECT_EQ("100.115.92.142", |
| plugin_subnet.AllocateAtOffset(13)->ToIPv4String()); |
| } |
| { |
| EXPECT_EQ("100.115.92.129/28", |
| plugin_subnet.AllocateAtOffset(0)->ToCidrString()); |
| EXPECT_EQ("100.115.92.130/28", |
| plugin_subnet.AllocateAtOffset(1)->ToCidrString()); |
| EXPECT_EQ("100.115.92.141/28", |
| plugin_subnet.AllocateAtOffset(12)->ToCidrString()); |
| EXPECT_EQ("100.115.92.142/28", |
| plugin_subnet.AllocateAtOffset(13)->ToCidrString()); |
| } |
| } |
| |
| // Tests that the Subnet runs the provided cleanup callback when it gets |
| // destroyed. |
| TEST(Subnet, Cleanup) { |
| bool called = false; |
| |
| { Subnet subnet(0, 24, base::Bind(&SetTrue, &called)); } |
| |
| EXPECT_TRUE(called); |
| } |
| |
| // Tests that the subnet rejects attempts to allocate addresses outside its |
| // range. |
| TEST(PluginSubnet, OutOfBounds) { |
| Subnet subnet(kPluginBaseAddress, kPluginSubnetPrefixLength, |
| base::DoNothing()); |
| |
| EXPECT_FALSE(subnet.Allocate(htonl(ntohl(kPluginBaseAddress) - 1))); |
| EXPECT_FALSE(subnet.Allocate(kPluginBaseAddress)); |
| EXPECT_FALSE(subnet.Allocate(AddOffset( |
| kPluginBaseAddress, (1ull << (32 - kPluginSubnetPrefixLength)) - 1))); |
| EXPECT_FALSE(subnet.Allocate(AddOffset( |
| kPluginBaseAddress, (1ull << (32 - kPluginSubnetPrefixLength))))); |
| } |
| |
| // Tests that the subnet rejects attempts to allocate the same address twice. |
| TEST(PluginSubnet, DuplicateAddress) { |
| Subnet subnet(kPluginBaseAddress, kPluginSubnetPrefixLength, |
| base::DoNothing()); |
| |
| auto addr = subnet.Allocate(AddOffset(kPluginBaseAddress, 1)); |
| EXPECT_TRUE(addr); |
| EXPECT_FALSE(subnet.Allocate(AddOffset(kPluginBaseAddress, 1))); |
| } |
| |
| // Tests that the subnet allows allocating all addresses in the subnet's range. |
| TEST(PluginSubnet, Allocate) { |
| Subnet subnet(kPluginBaseAddress, kPluginSubnetPrefixLength, |
| base::DoNothing()); |
| |
| std::vector<std::unique_ptr<SubnetAddress>> addrs; |
| addrs.reserve(subnet.AvailableCount()); |
| |
| for (size_t offset = 0; offset < subnet.AvailableCount(); ++offset) { |
| // Offset by one since the network id is not allocatable. |
| auto addr = subnet.Allocate(AddOffset(kPluginBaseAddress, offset + 1)); |
| EXPECT_TRUE(addr); |
| EXPECT_EQ(AddOffset(kPluginBaseAddress, offset + 1), addr->Address()); |
| addrs.emplace_back(std::move(addr)); |
| } |
| } |
| // Tests that the subnet allows allocating all addresses in the subnet's range |
| // using an offset. |
| TEST(PluginSubnet, AllocateAtOffset) { |
| Subnet subnet(kPluginBaseAddress, kPluginSubnetPrefixLength, |
| base::DoNothing()); |
| |
| std::vector<std::unique_ptr<SubnetAddress>> addrs; |
| addrs.reserve(subnet.AvailableCount()); |
| |
| for (size_t offset = 0; offset < subnet.AvailableCount(); ++offset) { |
| auto addr = subnet.AllocateAtOffset(offset); |
| EXPECT_TRUE(addr); |
| EXPECT_EQ(AddOffset(kPluginBaseAddress, offset + 1), addr->Address()); |
| addrs.emplace_back(std::move(addr)); |
| } |
| } |
| |
| // Tests that the subnet frees addresses when they are destroyed. |
| TEST(PluginSubnet, Free) { |
| Subnet subnet(kPluginBaseAddress, kPluginSubnetPrefixLength, |
| base::DoNothing()); |
| |
| { |
| auto addr = subnet.Allocate(AddOffset(kPluginBaseAddress, 1)); |
| EXPECT_TRUE(addr); |
| } |
| |
| EXPECT_TRUE(subnet.Allocate(AddOffset(kPluginBaseAddress, 1))); |
| } |
| |
| } // namespace patchpanel |