blob: 9f5e3817148695bd00072aa4ffeffa76a442216e [file] [log] [blame]
// Copyright 2019 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/net_util.h"
#include <arpa/inet.h>
#include <byteswap.h>
#include <net/ethernet.h>
#include <gtest/gtest.h>
namespace patchpanel {
const uint8_t ping_frame[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x86, 0xdd, 0x60, 0x0b, 0x8d, 0xb4, 0x00, 0x40, 0x3a, 0x40, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0xb9, 0x3c, 0x13, 0x8f,
0x00, 0x09, 0xde, 0x6a, 0x78, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x8e, 0x13,
0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21,
0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37};
const uint8_t rs_frame[] = {
0x33, 0x33, 0x00, 0x00, 0x00, 0x02, 0x1a, 0x9b, 0x82, 0xbd, 0xc0, 0xa0,
0x86, 0xdd, 0x60, 0x00, 0x00, 0x00, 0x00, 0x10, 0x3a, 0xff, 0xfe, 0x80,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x75, 0xb2, 0x80, 0x97, 0x83,
0x76, 0xbf, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x85, 0x00, 0x2f, 0xfc, 0x00, 0x00,
0x00, 0x00, 0x01, 0x01, 0x1a, 0x9b, 0x82, 0xbd, 0xc0, 0xa0};
const uint8_t ip_header[] = {0x45, 0x00, 0x00, 0x3d, 0x7c, 0x8e, 0x40,
0x00, 0x40, 0x11, 0x3d, 0x36, 0x64, 0x73,
0x5c, 0x02, 0x64, 0x73, 0x5c, 0x03};
const uint8_t udp_packet[] = {
0x45, 0x00, 0x00, 0x65, 0x44, 0xf7, 0x40, 0x00, 0x3f, 0x11, 0x7d, 0x62,
0x64, 0x57, 0x54, 0x5a, 0x64, 0x73, 0x5c, 0x0a, 0x9d, 0x6c, 0x09, 0xa4,
0x00, 0x51, 0x58, 0xfb, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
0x20, 0x20, 0x61, 0x73, 0x73, 0x75, 0x6d, 0x65, 0x73, 0x20, 0x20, 0x74,
0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x49, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x20, 0x20, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63,
0x6f, 0x6c, 0x20, 0x20, 0x28, 0x49, 0x50, 0x29, 0x20, 0x20, 0x5b, 0x31,
0x5d, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x61, 0x73,
0x20, 0x74, 0x68, 0x65, 0x0a};
TEST(Byteswap, 16bits) {
uint32_t test_cases[] = {
0x0000, 0x0001, 0x1000, 0xffff, 0x2244, 0xfffe,
};
for (uint32_t value : test_cases) {
EXPECT_EQ(Byteswap16(value), bswap_16(value));
EXPECT_EQ(ntohs(value), Ntohs(value));
EXPECT_EQ(htons(value), Htons(value));
}
}
TEST(Byteswap, 32bits) {
uint32_t test_cases[] = {
0x00000000, 0x00000001, 0x10000000, 0xffffffff, 0x11335577, 0xdeadbeef,
};
for (uint32_t value : test_cases) {
EXPECT_EQ(Byteswap32(value), bswap_32(value));
EXPECT_EQ(ntohl(value), Ntohl(value));
EXPECT_EQ(htonl(value), Htonl(value));
}
}
TEST(Ipv4, CreationAndStringConversion) {
struct {
std::string literal_address;
uint8_t bytes[4];
} test_cases[] = {
{"0.0.0.0", {0, 0, 0, 0}},
{"8.8.8.8", {8, 8, 8, 8}},
{"8.8.4.4", {8, 8, 4, 4}},
{"192.168.0.0", {192, 168, 0, 0}},
{"100.115.92.5", {100, 115, 92, 5}},
{"100.115.92.6", {100, 115, 92, 6}},
{"224.0.0.251", {224, 0, 0, 251}},
{"255.255.255.255", {255, 255, 255, 255}},
};
for (auto const& test_case : test_cases) {
uint32_t addr = Ipv4Addr(test_case.bytes[0], test_case.bytes[1],
test_case.bytes[2], test_case.bytes[3]);
EXPECT_EQ(test_case.literal_address, IPv4AddressToString(addr));
}
}
TEST(Ipv6, CreationAndStringConversion) {
struct {
std::string literal_address;
uint8_t bytes[16];
} test_cases[] = {
{"::", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
{"2001:da8:ff:5002:1034:56ff:fe78:9abc",
{0x20, 0x01, 0xd, 0xa8, 0, 0xff, 0x50, 0x02, 0x10, 0x34, 0x56, 0xff,
0xfe, 0x78, 0x9a, 0xbc}},
{"fe80::1122",
{0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x11, 0x22}},
};
for (auto const& test_case : test_cases) {
struct in6_addr addr = {};
memcpy(addr.s6_addr, test_case.bytes, sizeof(addr.s6_addr));
EXPECT_EQ(test_case.literal_address, IPv6AddressToString(addr));
}
}
TEST(Ipv4, CreationAndCidrStringConversion) {
struct {
std::string literal_address;
uint8_t bytes[4];
uint32_t prefix_length;
} test_cases[] = {
{"0.0.0.0/0", {0, 0, 0, 0}, 0},
{"192.168.0.0/24", {192, 168, 0, 0}, 24},
{"100.115.92.5/30", {100, 115, 92, 5}, 30},
{"100.115.92.6/30", {100, 115, 92, 6}, 30},
};
for (auto const& test_case : test_cases) {
uint32_t addr = Ipv4Addr(test_case.bytes[0], test_case.bytes[1],
test_case.bytes[2], test_case.bytes[3]);
EXPECT_EQ(test_case.literal_address,
IPv4AddressToCidrString(addr, test_case.prefix_length));
}
}
TEST(Ipv4, IpChecksum) {
alignas(4) uint8_t buffer[IP_MAXPACKET];
iphdr* ip = reinterpret_cast<iphdr*>(buffer);
memcpy(buffer, ip_header, sizeof(ip_header));
uint16_t ori_cksum = ip->check;
ip->check = 0;
EXPECT_EQ(ori_cksum, Ipv4Checksum(ip));
}
TEST(Ipv4, UdpChecksum) {
alignas(4) uint8_t buffer[IP_MAXPACKET];
udphdr* udp = reinterpret_cast<udphdr*>(buffer + sizeof(iphdr));
memcpy(buffer, udp_packet, sizeof(udp_packet));
uint16_t ori_cksum = udp->check;
udp->check = 0;
EXPECT_EQ(ori_cksum, Udpv4Checksum(buffer, sizeof(udp_packet)));
}
TEST(Ipv6, IcmpChecksum) {
alignas(4) uint8_t buffer_extended[IP_MAXPACKET + ETHER_HDR_LEN + 2];
uint8_t* buffer = buffer_extended + 2;
ip6_hdr* ip6 = reinterpret_cast<ip6_hdr*>(buffer + ETHER_HDR_LEN);
icmp6_hdr* icmp6 =
reinterpret_cast<icmp6_hdr*>(buffer + ETHER_HDR_LEN + sizeof(ip6_hdr));
memcpy(buffer, ping_frame, sizeof(ping_frame));
uint16_t ori_cksum = icmp6->icmp6_cksum;
icmp6->icmp6_cksum = 0;
ssize_t ip6_packet_len = sizeof(ping_frame) - ETHER_HDR_LEN;
EXPECT_EQ(ori_cksum, Icmpv6Checksum(reinterpret_cast<const uint8_t*>(ip6),
ip6_packet_len));
memcpy(buffer, rs_frame, sizeof(rs_frame));
ori_cksum = icmp6->icmp6_cksum;
icmp6->icmp6_cksum = 0;
ip6_packet_len = sizeof(rs_frame) - ETHER_HDR_LEN;
EXPECT_EQ(ori_cksum, Icmpv6Checksum(reinterpret_cast<const uint8_t*>(ip6),
ip6_packet_len));
}
TEST(Ipv6, EUI64Addr) {
struct {
std::string prefix;
MacAddress mac_address;
std::string eui64_address;
} test_cases[] = {{"::", {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, "::200:ff:fe00:0"},
{"2001:da8:ff:5002::",
{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc},
"2001:da8:ff:5002:1034:56ff:fe78:9abc"},
{"fe80::",
{0xf4, 0x99, 0x9f, 0xf4, 0x4f, 0xe4},
"fe80::f699:9fff:fef4:4fe4"}};
in6_addr prefix;
in6_addr addr;
for (auto const& test_case : test_cases) {
inet_pton(AF_INET6, test_case.prefix.c_str(), &prefix);
GenerateEUI64Address(&addr, prefix, test_case.mac_address);
char eui64_addr_str[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &addr, eui64_addr_str, INET6_ADDRSTRLEN);
EXPECT_EQ(test_case.eui64_address, eui64_addr_str);
}
}
TEST(IPv6, IsIPv6PrefixEqual) {
// |addr1| and |addr2| has the same prefix up to the 45th bit.
struct in6_addr addr1 = StringToIPv6Address("2001:db8:0::52:0:1");
struct in6_addr addr2 = StringToIPv6Address("2001:db8:4::52:0:1");
int idx_prefix_equal = 45;
for (int i = 0; i <= 128; i++) {
EXPECT_EQ(i <= idx_prefix_equal, IsIPv6PrefixEqual(addr1, addr2, i));
}
}
TEST(Ipv4, BroadcastAddr) {
uint32_t base = Ipv4Addr(100, 115, 92, 0);
struct {
uint32_t prefix_len;
uint32_t want;
} test_cases[] = {
{24, Ipv4Addr(100, 115, 92, 255)},
{29, Ipv4Addr(100, 115, 92, 7)},
{30, Ipv4Addr(100, 115, 92, 3)},
{31, Ipv4Addr(100, 115, 92, 1)},
};
for (const auto& t : test_cases) {
EXPECT_EQ(Ipv4BroadcastAddr(base, t.prefix_len), t.want);
}
}
TEST(IPv4, SetSockaddrIn) {
struct sockaddr_storage sockaddr = {};
std::ostringstream stream;
SetSockaddrIn((struct sockaddr*)&sockaddr, 0);
stream << sockaddr;
EXPECT_EQ("{family: AF_INET, port: 0, addr: 0.0.0.0}", stream.str());
stream.str("");
SetSockaddrIn((struct sockaddr*)&sockaddr, Ipv4Addr(192, 168, 1, 37));
stream << sockaddr;
EXPECT_EQ("{family: AF_INET, port: 0, addr: 192.168.1.37}", stream.str());
}
TEST(IpFamily, GetIpFamily) {
struct {
std::string ip_address;
sa_family_t family;
} test_cases[] = {
{"0.0.0.0", AF_INET},
{"100.115.92.6", AF_INET},
{"255.255.255.255", AF_INET},
{"::", AF_INET6},
{"fe80::", AF_INET6},
{"fe80::f699:9fff:fef4:4fe4", AF_INET6},
{"", AF_UNSPEC},
{"1234", AF_UNSPEC},
{"0.0.0.0/0", AF_UNSPEC},
{"1.1.1.256", AF_UNSPEC},
{"fe80:f699:9fff:fef4:4fe4", AF_UNSPEC},
{"fg80::", AF_UNSPEC},
};
for (const auto& t : test_cases) {
EXPECT_EQ(GetIpFamily(t.ip_address), t.family);
}
}
TEST(PrettyPrint, SocketAddrIn) {
struct sockaddr_in ipv4_sockaddr = {};
std::ostringstream stream;
stream << ipv4_sockaddr;
EXPECT_EQ("{family: AF_INET, port: 0, addr: 0.0.0.0}", stream.str());
ipv4_sockaddr.sin_family = AF_INET;
ipv4_sockaddr.sin_port = htons(1234);
ipv4_sockaddr.sin_addr.s_addr = Ipv4Addr(100, 115, 92, 10);
std::string expected_output =
"{family: AF_INET, port: 1234, addr: 100.115.92.10}";
stream.str("");
stream << ipv4_sockaddr;
EXPECT_EQ(expected_output, stream.str());
stream.str("");
stream << (const struct sockaddr&)ipv4_sockaddr;
EXPECT_EQ(expected_output, stream.str());
struct sockaddr_storage sockaddr_storage = {};
memcpy(&sockaddr_storage, &ipv4_sockaddr, sizeof(ipv4_sockaddr));
stream.str("");
stream << sockaddr_storage;
EXPECT_EQ(expected_output, stream.str());
}
TEST(PrettyPrint, SocketAddrIn6) {
struct sockaddr_in6 ipv6_sockaddr = {};
std::ostringstream stream;
stream << ipv6_sockaddr;
EXPECT_EQ("{family: AF_INET6, port: 0, addr: ::}", stream.str());
ipv6_sockaddr.sin6_family = AF_INET6;
ipv6_sockaddr.sin6_port = htons(2345);
unsigned char addr[16] = {0x20, 0x01, 0xd, 0xb1, 0, 0, 0, 0,
0xab, 0xcd, 0x12, 0x34, 0x56, 0x78, 0xfe, 0xaa};
memcpy(ipv6_sockaddr.sin6_addr.s6_addr, addr, sizeof(addr));
std::string expected_output =
"{family: AF_INET6, port: 2345, addr: 2001:db1::abcd:1234:5678:feaa}";
stream.str("");
stream << ipv6_sockaddr;
EXPECT_EQ(expected_output, stream.str());
stream.str("");
stream << (const struct sockaddr&)ipv6_sockaddr;
EXPECT_EQ(expected_output, stream.str());
struct sockaddr_storage sockaddr_storage = {};
memcpy(&sockaddr_storage, &ipv6_sockaddr, sizeof(ipv6_sockaddr));
stream.str("");
stream << sockaddr_storage;
EXPECT_EQ(expected_output, stream.str());
}
TEST(PrettyPrint, SocketAddrVsock) {
struct sockaddr_vm vm_sockaddr = {};
std::ostringstream stream;
stream << vm_sockaddr;
EXPECT_EQ("{family: AF_VSOCK, port: 0, cid: 0}", stream.str());
vm_sockaddr.svm_family = AF_VSOCK;
vm_sockaddr.svm_port = 5555;
vm_sockaddr.svm_cid = 4;
std::string expected_output = "{family: AF_VSOCK, port: 5555, cid: 4}";
stream.str("");
stream << vm_sockaddr;
EXPECT_EQ(expected_output, stream.str());
stream.str("");
stream << (const struct sockaddr&)vm_sockaddr;
EXPECT_EQ(expected_output, stream.str());
struct sockaddr_storage sockaddr_storage = {};
memcpy(&sockaddr_storage, &vm_sockaddr, sizeof(vm_sockaddr));
stream.str("");
stream << sockaddr_storage;
EXPECT_EQ(expected_output, stream.str());
}
TEST(PrettyPrint, SocketAddrUnix) {
struct sockaddr_un unix_sockaddr = {};
std::ostringstream stream;
stream << unix_sockaddr;
EXPECT_EQ("{family: AF_UNIX, path: @}", stream.str());
// Fill |sun_path| with an invalid non-null-terminated c string.
std::string bogus_output = "{family: AF_UNIX, path: ";
for (size_t i = 0; i < sizeof(unix_sockaddr.sun_path); i++) {
unix_sockaddr.sun_path[i] = 'a';
bogus_output += 'a';
}
bogus_output += '}';
stream.str("");
stream << unix_sockaddr;
EXPECT_EQ(bogus_output, stream.str());
memset(&unix_sockaddr, 0, sizeof(unix_sockaddr));
unix_sockaddr.sun_family = AF_UNIX;
std::string sun_path = "/run/arc/adb";
memcpy(&unix_sockaddr.sun_path, sun_path.c_str(), strlen(sun_path.c_str()));
std::string expected_output = "{family: AF_UNIX, path: /run/arc/adb}";
stream.str("");
stream << unix_sockaddr;
EXPECT_EQ(expected_output, stream.str());
stream.str("");
stream << (const struct sockaddr&)unix_sockaddr;
EXPECT_EQ(expected_output, stream.str());
struct sockaddr_storage sockaddr_storage = {};
memcpy(&sockaddr_storage, &unix_sockaddr, sizeof(unix_sockaddr));
stream.str("");
stream << sockaddr_storage;
EXPECT_EQ(expected_output, stream.str());
}
TEST(PrettyPrint, Rtentry) {
struct rtentry route;
memset(&route, 0, sizeof(route));
std::ostringstream stream;
stream << route;
EXPECT_EQ(
"{rt_dst: {unset}, rt_genmask: {unset}, rt_gateway: {unset}, rt_dev: "
"null, rt_flags: 0}",
stream.str());
SetSockaddrIn(&route.rt_dst, Ipv4Addr(100, 115, 92, 128));
SetSockaddrIn(&route.rt_genmask, Ipv4Addr(255, 255, 255, 252));
SetSockaddrIn(&route.rt_gateway, Ipv4Addr(192, 168, 1, 1));
std::string rt_dev = "eth0";
route.rt_dev = const_cast<char*>(rt_dev.c_str());
route.rt_flags =
RTF_UP | RTF_GATEWAY | RTF_DYNAMIC | RTF_MODIFIED | RTF_REJECT;
stream.str("");
stream << route;
EXPECT_EQ(
"{rt_dst: {family: AF_INET, port: 0, addr: 100.115.92.128}, rt_genmask: "
"{family: AF_INET, port: 0, addr: 255.255.255.252}, rt_gateway: {family: "
"AF_INET, port: 0, addr: 192.168.1.1}, rt_dev: eth0, rt_flags: RTF_UP | "
"RTF_GATEWAY | RTF_DYNAMIC | RTF_MODIFIED | RTF_REJECT}",
stream.str());
}
} // namespace patchpanel