Hugo Benichi | 2ac4d07 | 2019-05-28 14:51:23 +0900 | [diff] [blame] | 1 | // Copyright 2019 The Chromium OS Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Garrick Evans | 3388a03 | 2020-03-24 11:25:55 +0900 | [diff] [blame] | 5 | #include "patchpanel/net_util.h" |
Hugo Benichi | 2ac4d07 | 2019-05-28 14:51:23 +0900 | [diff] [blame] | 6 | |
| 7 | #include <arpa/inet.h> |
| 8 | #include <byteswap.h> |
Jason Jeremy Iman | 16f9172 | 2020-01-14 09:53:28 +0900 | [diff] [blame] | 9 | #include <net/ethernet.h> |
Hugo Benichi | 2ac4d07 | 2019-05-28 14:51:23 +0900 | [diff] [blame] | 10 | |
| 11 | #include <gtest/gtest.h> |
| 12 | |
Garrick Evans | 3388a03 | 2020-03-24 11:25:55 +0900 | [diff] [blame] | 13 | namespace patchpanel { |
Hugo Benichi | 2ac4d07 | 2019-05-28 14:51:23 +0900 | [diff] [blame] | 14 | |
Jason Jeremy Iman | 16f9172 | 2020-01-14 09:53:28 +0900 | [diff] [blame] | 15 | const uint8_t ping_frame[] = |
| 16 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x86\xdd\x60\x0b" |
| 17 | "\x8d\xb4\x00\x40\x3a\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" |
| 18 | "\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" |
| 19 | "\x00\x00\x00\x00\x00\x01\x80\x00\xb9\x3c\x13\x8f\x00\x09\xde\x6a" |
| 20 | "\x78\x5d\x00\x00\x00\x00\x8e\x13\x0f\x00\x00\x00\x00\x00\x10\x11" |
| 21 | "\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21" |
| 22 | "\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31" |
| 23 | "\x32\x33\x34\x35\x36\x37"; |
| 24 | |
| 25 | const uint8_t rs_frame[] = |
| 26 | "\x33\x33\x00\x00\x00\x02\x1a\x9b\x82\xbd\xc0\xa0\x86\xdd\x60\x00" |
| 27 | "\x00\x00\x00\x10\x3a\xff\xfe\x80\x00\x00\x00\x00\x00\x00\x2d\x75" |
| 28 | "\xb2\x80\x97\x83\x76\xbf\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00" |
| 29 | "\x00\x00\x00\x00\x00\x02\x85\x00\x2f\xfc\x00\x00\x00\x00\x01\x01" |
| 30 | "\x1a\x9b\x82\xbd\xc0\xa0"; |
| 31 | |
| 32 | const uint8_t ip_header[] = |
| 33 | "\x45\x00\x00\x3d\x7c\x8e\x40\x00\x40\x11\x3d\x36\x64\x73\x5c\x02" |
| 34 | "\x64\x73\x5c\x03"; |
| 35 | |
| 36 | const uint8_t udp_packet[] = |
| 37 | "\x45\x00\x00\x65\x44\xf7\x40\x00\x3f\x11\x7d\x62\x64\x57\x54\x5a" |
| 38 | "\x64\x73\x5c\x0a\x9d\x6c\x09\xa4\x00\x51\x58\xfb\x70\x72\x6f\x74" |
| 39 | "\x6f\x63\x6f\x6c\x20\x20\x61\x73\x73\x75\x6d\x65\x73\x20\x20\x74" |
| 40 | "\x68\x61\x74\x20\x74\x68\x65\x20\x49\x6e\x74\x65\x72\x6e\x65\x74" |
| 41 | "\x20\x20\x50\x72\x6f\x74\x6f\x63\x6f\x6c\x20\x20\x28\x49\x50\x29" |
| 42 | "\x20\x20\x5b\x31\x5d\x20\x69\x73\x20\x75\x73\x65\x64\x20\x61\x73" |
| 43 | "\x20\x74\x68\x65\x0a"; |
| 44 | |
Hugo Benichi | 2ac4d07 | 2019-05-28 14:51:23 +0900 | [diff] [blame] | 45 | TEST(Byteswap, 16bits) { |
| 46 | uint32_t test_cases[] = { |
| 47 | 0x0000, 0x0001, 0x1000, 0xffff, 0x2244, 0xfffe, |
| 48 | }; |
| 49 | |
| 50 | for (uint32_t value : test_cases) { |
| 51 | EXPECT_EQ(Byteswap16(value), bswap_16(value)); |
| 52 | EXPECT_EQ(ntohs(value), Ntohs(value)); |
| 53 | EXPECT_EQ(htons(value), Htons(value)); |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | TEST(Byteswap, 32bits) { |
| 58 | uint32_t test_cases[] = { |
| 59 | 0x00000000, 0x00000001, 0x10000000, 0xffffffff, 0x11335577, 0xdeadbeef, |
| 60 | }; |
| 61 | |
| 62 | for (uint32_t value : test_cases) { |
| 63 | EXPECT_EQ(Byteswap32(value), bswap_32(value)); |
| 64 | EXPECT_EQ(ntohl(value), Ntohl(value)); |
| 65 | EXPECT_EQ(htonl(value), Htonl(value)); |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | TEST(Ipv4, CreationAndStringConversion) { |
| 70 | struct { |
| 71 | std::string literal_address; |
| 72 | uint8_t bytes[4]; |
| 73 | } test_cases[] = { |
| 74 | {"0.0.0.0", {0, 0, 0, 0}}, |
| 75 | {"8.8.8.8", {8, 8, 8, 8}}, |
| 76 | {"8.8.4.4", {8, 8, 4, 4}}, |
| 77 | {"192.168.0.0", {192, 168, 0, 0}}, |
| 78 | {"100.115.92.5", {100, 115, 92, 5}}, |
| 79 | {"100.115.92.6", {100, 115, 92, 6}}, |
| 80 | {"224.0.0.251", {224, 0, 0, 251}}, |
| 81 | {"255.255.255.255", {255, 255, 255, 255}}, |
| 82 | }; |
| 83 | |
| 84 | for (auto const& test_case : test_cases) { |
| 85 | uint32_t addr = Ipv4Addr(test_case.bytes[0], test_case.bytes[1], |
| 86 | test_case.bytes[2], test_case.bytes[3]); |
| 87 | EXPECT_EQ(test_case.literal_address, IPv4AddressToString(addr)); |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | TEST(Ipv4, CreationAndCidrStringConversion) { |
| 92 | struct { |
| 93 | std::string literal_address; |
| 94 | uint8_t bytes[4]; |
| 95 | uint32_t prefix_length; |
| 96 | } test_cases[] = { |
| 97 | {"0.0.0.0/0", {0, 0, 0, 0}, 0}, |
| 98 | {"192.168.0.0/24", {192, 168, 0, 0}, 24}, |
| 99 | {"100.115.92.5/30", {100, 115, 92, 5}, 30}, |
| 100 | {"100.115.92.6/30", {100, 115, 92, 6}, 30}, |
| 101 | }; |
| 102 | |
| 103 | for (auto const& test_case : test_cases) { |
| 104 | uint32_t addr = Ipv4Addr(test_case.bytes[0], test_case.bytes[1], |
| 105 | test_case.bytes[2], test_case.bytes[3]); |
| 106 | EXPECT_EQ(test_case.literal_address, |
| 107 | IPv4AddressToCidrString(addr, test_case.prefix_length)); |
| 108 | } |
| 109 | } |
| 110 | |
Jason Jeremy Iman | 16f9172 | 2020-01-14 09:53:28 +0900 | [diff] [blame] | 111 | TEST(Ipv4, IpChecksum) { |
| 112 | alignas(4) uint8_t buffer[IP_MAXPACKET]; |
| 113 | |
| 114 | iphdr* ip = reinterpret_cast<iphdr*>(buffer); |
| 115 | |
| 116 | memcpy(buffer, ip_header, sizeof(ip_header)); |
| 117 | uint16_t ori_cksum = ip->check; |
| 118 | ip->check = 0; |
| 119 | EXPECT_EQ(ori_cksum, Ipv4Checksum(ip)); |
| 120 | } |
| 121 | |
| 122 | TEST(Ipv4, UdpChecksum) { |
| 123 | alignas(4) uint8_t buffer[IP_MAXPACKET]; |
| 124 | |
| 125 | iphdr* ip = reinterpret_cast<iphdr*>(buffer); |
| 126 | udphdr* udp = reinterpret_cast<udphdr*>(buffer + sizeof(iphdr)); |
| 127 | |
| 128 | memcpy(buffer, udp_packet, sizeof(udp_packet)); |
| 129 | uint16_t ori_cksum = udp->check; |
| 130 | udp->check = 0; |
| 131 | EXPECT_EQ(ori_cksum, Udpv4Checksum(ip, udp)); |
| 132 | } |
| 133 | |
| 134 | TEST(Ipv6, IcmpChecksum) { |
| 135 | alignas(4) uint8_t buffer_extended[IP_MAXPACKET + ETHER_HDR_LEN + 2]; |
| 136 | uint8_t* buffer = buffer_extended + 2; |
| 137 | |
| 138 | ip6_hdr* ip6 = reinterpret_cast<ip6_hdr*>(buffer + ETHER_HDR_LEN); |
| 139 | icmp6_hdr* icmp6 = |
| 140 | reinterpret_cast<icmp6_hdr*>(buffer + ETHER_HDR_LEN + sizeof(ip6_hdr)); |
| 141 | |
| 142 | memcpy(buffer, ping_frame, sizeof(ping_frame)); |
| 143 | uint16_t ori_cksum = icmp6->icmp6_cksum; |
| 144 | icmp6->icmp6_cksum = 0; |
| 145 | EXPECT_EQ(ori_cksum, Icmpv6Checksum(ip6, icmp6)); |
| 146 | |
| 147 | memcpy(buffer, rs_frame, sizeof(rs_frame)); |
| 148 | ori_cksum = icmp6->icmp6_cksum; |
| 149 | icmp6->icmp6_cksum = 0; |
| 150 | EXPECT_EQ(ori_cksum, Icmpv6Checksum(ip6, icmp6)); |
| 151 | } |
| 152 | |
Taoyu Li | a0727dc | 2020-09-24 19:54:59 +0900 | [diff] [blame^] | 153 | TEST(Ipv6, EUI64Addr) { |
| 154 | struct { |
| 155 | std::string prefix; |
| 156 | MacAddress mac_address; |
| 157 | std::string eui64_address; |
| 158 | } test_cases[] = {{"::", {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, "::200:ff:fe00:0"}, |
| 159 | {"2001:da8:ff:5002::", |
| 160 | {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc}, |
| 161 | "2001:da8:ff:5002:1034:56ff:fe78:9abc"}, |
| 162 | {"fe80::", |
| 163 | {0xf4, 0x99, 0x9f, 0xf4, 0x4f, 0xe4}, |
| 164 | "fe80::f699:9fff:fef4:4fe4"}}; |
| 165 | in6_addr prefix; |
| 166 | in6_addr addr; |
| 167 | for (auto const& test_case : test_cases) { |
| 168 | inet_pton(AF_INET6, test_case.prefix.c_str(), &prefix); |
| 169 | GenerateEUI64Address(&addr, prefix, test_case.mac_address); |
| 170 | char eui64_addr_str[INET6_ADDRSTRLEN]; |
| 171 | inet_ntop(AF_INET6, &addr, eui64_addr_str, INET6_ADDRSTRLEN); |
| 172 | EXPECT_EQ(test_case.eui64_address, eui64_addr_str); |
| 173 | } |
| 174 | } |
| 175 | |
Garrick Evans | 6f4fa3a | 2020-02-10 16:15:09 +0900 | [diff] [blame] | 176 | TEST(Ipv4, BroadcastAddr) { |
| 177 | uint32_t base = Ipv4Addr(100, 115, 92, 0); |
| 178 | struct { |
| 179 | uint32_t prefix_len; |
| 180 | uint32_t want; |
| 181 | } test_cases[] = { |
| 182 | {24, Ipv4Addr(100, 115, 92, 255)}, |
| 183 | {29, Ipv4Addr(100, 115, 92, 7)}, |
| 184 | {30, Ipv4Addr(100, 115, 92, 3)}, |
| 185 | {31, Ipv4Addr(100, 115, 92, 1)}, |
| 186 | }; |
| 187 | |
| 188 | for (const auto& t : test_cases) { |
| 189 | EXPECT_EQ(Ipv4BroadcastAddr(base, t.prefix_len), t.want); |
| 190 | } |
| 191 | } |
| 192 | |
Hugo Benichi | e8758b5 | 2020-04-03 14:49:01 +0900 | [diff] [blame] | 193 | TEST(IPv4, SetSockaddrIn) { |
| 194 | struct sockaddr_storage sockaddr = {}; |
| 195 | std::ostringstream stream; |
| 196 | |
| 197 | SetSockaddrIn((struct sockaddr*)&sockaddr, 0); |
| 198 | stream << sockaddr; |
| 199 | EXPECT_EQ("{family: AF_INET, port: 0, addr: 0.0.0.0}", stream.str()); |
| 200 | |
| 201 | stream.str(""); |
| 202 | SetSockaddrIn((struct sockaddr*)&sockaddr, Ipv4Addr(192, 168, 1, 37)); |
| 203 | stream << sockaddr; |
| 204 | EXPECT_EQ("{family: AF_INET, port: 0, addr: 192.168.1.37}", stream.str()); |
| 205 | } |
| 206 | |
Hugo Benichi | dcc3239 | 2020-02-27 09:14:40 +0900 | [diff] [blame] | 207 | TEST(PrettyPrint, SocketAddrIn) { |
| 208 | struct sockaddr_in ipv4_sockaddr = {}; |
| 209 | std::ostringstream stream; |
| 210 | |
| 211 | stream << ipv4_sockaddr; |
| 212 | EXPECT_EQ("{family: AF_INET, port: 0, addr: 0.0.0.0}", stream.str()); |
| 213 | |
| 214 | ipv4_sockaddr.sin_family = AF_INET; |
| 215 | ipv4_sockaddr.sin_port = htons(1234); |
| 216 | ipv4_sockaddr.sin_addr.s_addr = Ipv4Addr(100, 115, 92, 10); |
| 217 | std::string expected_output = |
| 218 | "{family: AF_INET, port: 1234, addr: 100.115.92.10}"; |
| 219 | |
| 220 | stream.str(""); |
| 221 | stream << ipv4_sockaddr; |
| 222 | EXPECT_EQ(expected_output, stream.str()); |
| 223 | |
| 224 | stream.str(""); |
| 225 | stream << (const struct sockaddr&)ipv4_sockaddr; |
| 226 | EXPECT_EQ(expected_output, stream.str()); |
| 227 | |
| 228 | struct sockaddr_storage sockaddr_storage = {}; |
| 229 | memcpy(&sockaddr_storage, &ipv4_sockaddr, sizeof(ipv4_sockaddr)); |
| 230 | |
| 231 | stream.str(""); |
| 232 | stream << sockaddr_storage; |
| 233 | EXPECT_EQ(expected_output, stream.str()); |
| 234 | } |
| 235 | |
| 236 | TEST(PrettyPrint, SocketAddrIn6) { |
| 237 | struct sockaddr_in6 ipv6_sockaddr = {}; |
| 238 | std::ostringstream stream; |
| 239 | |
| 240 | stream << ipv6_sockaddr; |
| 241 | EXPECT_EQ("{family: AF_INET6, port: 0, addr: ::}", stream.str()); |
| 242 | |
| 243 | ipv6_sockaddr.sin6_family = AF_INET6; |
| 244 | ipv6_sockaddr.sin6_port = htons(2345); |
| 245 | unsigned char addr[16] = {0x20, 0x01, 0xd, 0xb1, 0, 0, 0, 0, |
| 246 | 0xab, 0xcd, 0x12, 0x34, 0x56, 0x78, 0xfe, 0xaa}; |
| 247 | memcpy(ipv6_sockaddr.sin6_addr.s6_addr, addr, sizeof(addr)); |
| 248 | std::string expected_output = |
| 249 | "{family: AF_INET6, port: 2345, addr: 2001:db1::abcd:1234:5678:feaa}"; |
| 250 | |
| 251 | stream.str(""); |
| 252 | stream << ipv6_sockaddr; |
| 253 | EXPECT_EQ(expected_output, stream.str()); |
| 254 | |
| 255 | stream.str(""); |
| 256 | stream << (const struct sockaddr&)ipv6_sockaddr; |
| 257 | EXPECT_EQ(expected_output, stream.str()); |
| 258 | |
| 259 | struct sockaddr_storage sockaddr_storage = {}; |
| 260 | memcpy(&sockaddr_storage, &ipv6_sockaddr, sizeof(ipv6_sockaddr)); |
| 261 | |
| 262 | stream.str(""); |
| 263 | stream << sockaddr_storage; |
| 264 | EXPECT_EQ(expected_output, stream.str()); |
| 265 | } |
| 266 | |
| 267 | TEST(PrettyPrint, SocketAddrVsock) { |
| 268 | struct sockaddr_vm vm_sockaddr = {}; |
| 269 | std::ostringstream stream; |
| 270 | |
| 271 | stream << vm_sockaddr; |
| 272 | EXPECT_EQ("{family: AF_VSOCK, port: 0, cid: 0}", stream.str()); |
| 273 | |
| 274 | vm_sockaddr.svm_family = AF_VSOCK; |
| 275 | vm_sockaddr.svm_port = 5555; |
| 276 | vm_sockaddr.svm_cid = 4; |
| 277 | std::string expected_output = "{family: AF_VSOCK, port: 5555, cid: 4}"; |
| 278 | |
| 279 | stream.str(""); |
| 280 | stream << vm_sockaddr; |
| 281 | EXPECT_EQ(expected_output, stream.str()); |
| 282 | |
| 283 | stream.str(""); |
| 284 | stream << (const struct sockaddr&)vm_sockaddr; |
| 285 | EXPECT_EQ(expected_output, stream.str()); |
| 286 | |
| 287 | struct sockaddr_storage sockaddr_storage = {}; |
| 288 | memcpy(&sockaddr_storage, &vm_sockaddr, sizeof(vm_sockaddr)); |
| 289 | |
| 290 | stream.str(""); |
| 291 | stream << sockaddr_storage; |
| 292 | EXPECT_EQ(expected_output, stream.str()); |
| 293 | } |
| 294 | |
| 295 | TEST(PrettyPrint, SocketAddrUnix) { |
| 296 | struct sockaddr_un unix_sockaddr = {}; |
| 297 | std::ostringstream stream; |
| 298 | |
| 299 | stream << unix_sockaddr; |
| 300 | EXPECT_EQ("{family: AF_UNIX, path: @}", stream.str()); |
| 301 | |
| 302 | // Fill |sun_path| with an invalid non-null-terminated c string. |
| 303 | std::string bogus_output = "{family: AF_UNIX, path: "; |
| 304 | for (size_t i = 0; i < sizeof(unix_sockaddr.sun_path); i++) { |
| 305 | unix_sockaddr.sun_path[i] = 'a'; |
| 306 | bogus_output += 'a'; |
| 307 | } |
| 308 | bogus_output += '}'; |
| 309 | stream.str(""); |
| 310 | stream << unix_sockaddr; |
| 311 | EXPECT_EQ(bogus_output, stream.str()); |
| 312 | |
| 313 | memset(&unix_sockaddr, 0, sizeof(unix_sockaddr)); |
| 314 | unix_sockaddr.sun_family = AF_UNIX; |
| 315 | std::string sun_path = "/run/arc/adb"; |
| 316 | memcpy(&unix_sockaddr.sun_path, sun_path.c_str(), strlen(sun_path.c_str())); |
| 317 | std::string expected_output = "{family: AF_UNIX, path: /run/arc/adb}"; |
| 318 | |
| 319 | stream.str(""); |
| 320 | stream << unix_sockaddr; |
| 321 | EXPECT_EQ(expected_output, stream.str()); |
| 322 | |
| 323 | stream.str(""); |
| 324 | stream << (const struct sockaddr&)unix_sockaddr; |
| 325 | EXPECT_EQ(expected_output, stream.str()); |
| 326 | |
| 327 | struct sockaddr_storage sockaddr_storage = {}; |
| 328 | memcpy(&sockaddr_storage, &unix_sockaddr, sizeof(unix_sockaddr)); |
| 329 | |
| 330 | stream.str(""); |
| 331 | stream << sockaddr_storage; |
| 332 | EXPECT_EQ(expected_output, stream.str()); |
| 333 | } |
| 334 | |
Hugo Benichi | e8758b5 | 2020-04-03 14:49:01 +0900 | [diff] [blame] | 335 | TEST(PrettyPrint, Rtentry) { |
| 336 | struct rtentry route; |
| 337 | memset(&route, 0, sizeof(route)); |
| 338 | std::ostringstream stream; |
| 339 | |
| 340 | stream << route; |
| 341 | EXPECT_EQ( |
| 342 | "{rt_dst: {unset}, rt_genmask: {unset}, rt_gateway: {unset}, rt_dev: " |
| 343 | "null, rt_flags: 0}", |
| 344 | stream.str()); |
| 345 | |
| 346 | SetSockaddrIn(&route.rt_dst, Ipv4Addr(100, 115, 92, 128)); |
| 347 | SetSockaddrIn(&route.rt_genmask, Ipv4Addr(255, 255, 255, 252)); |
| 348 | SetSockaddrIn(&route.rt_gateway, Ipv4Addr(192, 168, 1, 1)); |
| 349 | std::string rt_dev = "eth0"; |
| 350 | route.rt_dev = (char*)rt_dev.c_str(); |
| 351 | route.rt_flags = |
| 352 | RTF_UP | RTF_GATEWAY | RTF_DYNAMIC | RTF_MODIFIED | RTF_REJECT; |
| 353 | stream.str(""); |
| 354 | stream << route; |
| 355 | EXPECT_EQ( |
| 356 | "{rt_dst: {family: AF_INET, port: 0, addr: 100.115.92.128}, rt_genmask: " |
| 357 | "{family: AF_INET, port: 0, addr: 255.255.255.252}, rt_gateway: {family: " |
| 358 | "AF_INET, port: 0, addr: 192.168.1.1}, rt_dev: eth0, rt_flags: RTF_UP | " |
| 359 | "RTF_GATEWAY | RTF_DYNAMIC | RTF_MODIFIED | RTF_REJECT}", |
| 360 | stream.str()); |
| 361 | } |
| 362 | |
Garrick Evans | 3388a03 | 2020-03-24 11:25:55 +0900 | [diff] [blame] | 363 | } // namespace patchpanel |