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 | |
Garrick Evans | 6f4fa3a | 2020-02-10 16:15:09 +0900 | [diff] [blame] | 153 | TEST(Ipv4, BroadcastAddr) { |
| 154 | uint32_t base = Ipv4Addr(100, 115, 92, 0); |
| 155 | struct { |
| 156 | uint32_t prefix_len; |
| 157 | uint32_t want; |
| 158 | } test_cases[] = { |
| 159 | {24, Ipv4Addr(100, 115, 92, 255)}, |
| 160 | {29, Ipv4Addr(100, 115, 92, 7)}, |
| 161 | {30, Ipv4Addr(100, 115, 92, 3)}, |
| 162 | {31, Ipv4Addr(100, 115, 92, 1)}, |
| 163 | }; |
| 164 | |
| 165 | for (const auto& t : test_cases) { |
| 166 | EXPECT_EQ(Ipv4BroadcastAddr(base, t.prefix_len), t.want); |
| 167 | } |
| 168 | } |
| 169 | |
Hugo Benichi | e8758b5 | 2020-04-03 14:49:01 +0900 | [diff] [blame] | 170 | TEST(IPv4, SetSockaddrIn) { |
| 171 | struct sockaddr_storage sockaddr = {}; |
| 172 | std::ostringstream stream; |
| 173 | |
| 174 | SetSockaddrIn((struct sockaddr*)&sockaddr, 0); |
| 175 | stream << sockaddr; |
| 176 | EXPECT_EQ("{family: AF_INET, port: 0, addr: 0.0.0.0}", stream.str()); |
| 177 | |
| 178 | stream.str(""); |
| 179 | SetSockaddrIn((struct sockaddr*)&sockaddr, Ipv4Addr(192, 168, 1, 37)); |
| 180 | stream << sockaddr; |
| 181 | EXPECT_EQ("{family: AF_INET, port: 0, addr: 192.168.1.37}", stream.str()); |
| 182 | } |
| 183 | |
Hugo Benichi | dcc3239 | 2020-02-27 09:14:40 +0900 | [diff] [blame] | 184 | TEST(PrettyPrint, SocketAddrIn) { |
| 185 | struct sockaddr_in ipv4_sockaddr = {}; |
| 186 | std::ostringstream stream; |
| 187 | |
| 188 | stream << ipv4_sockaddr; |
| 189 | EXPECT_EQ("{family: AF_INET, port: 0, addr: 0.0.0.0}", stream.str()); |
| 190 | |
| 191 | ipv4_sockaddr.sin_family = AF_INET; |
| 192 | ipv4_sockaddr.sin_port = htons(1234); |
| 193 | ipv4_sockaddr.sin_addr.s_addr = Ipv4Addr(100, 115, 92, 10); |
| 194 | std::string expected_output = |
| 195 | "{family: AF_INET, port: 1234, addr: 100.115.92.10}"; |
| 196 | |
| 197 | stream.str(""); |
| 198 | stream << ipv4_sockaddr; |
| 199 | EXPECT_EQ(expected_output, stream.str()); |
| 200 | |
| 201 | stream.str(""); |
| 202 | stream << (const struct sockaddr&)ipv4_sockaddr; |
| 203 | EXPECT_EQ(expected_output, stream.str()); |
| 204 | |
| 205 | struct sockaddr_storage sockaddr_storage = {}; |
| 206 | memcpy(&sockaddr_storage, &ipv4_sockaddr, sizeof(ipv4_sockaddr)); |
| 207 | |
| 208 | stream.str(""); |
| 209 | stream << sockaddr_storage; |
| 210 | EXPECT_EQ(expected_output, stream.str()); |
| 211 | } |
| 212 | |
| 213 | TEST(PrettyPrint, SocketAddrIn6) { |
| 214 | struct sockaddr_in6 ipv6_sockaddr = {}; |
| 215 | std::ostringstream stream; |
| 216 | |
| 217 | stream << ipv6_sockaddr; |
| 218 | EXPECT_EQ("{family: AF_INET6, port: 0, addr: ::}", stream.str()); |
| 219 | |
| 220 | ipv6_sockaddr.sin6_family = AF_INET6; |
| 221 | ipv6_sockaddr.sin6_port = htons(2345); |
| 222 | unsigned char addr[16] = {0x20, 0x01, 0xd, 0xb1, 0, 0, 0, 0, |
| 223 | 0xab, 0xcd, 0x12, 0x34, 0x56, 0x78, 0xfe, 0xaa}; |
| 224 | memcpy(ipv6_sockaddr.sin6_addr.s6_addr, addr, sizeof(addr)); |
| 225 | std::string expected_output = |
| 226 | "{family: AF_INET6, port: 2345, addr: 2001:db1::abcd:1234:5678:feaa}"; |
| 227 | |
| 228 | stream.str(""); |
| 229 | stream << ipv6_sockaddr; |
| 230 | EXPECT_EQ(expected_output, stream.str()); |
| 231 | |
| 232 | stream.str(""); |
| 233 | stream << (const struct sockaddr&)ipv6_sockaddr; |
| 234 | EXPECT_EQ(expected_output, stream.str()); |
| 235 | |
| 236 | struct sockaddr_storage sockaddr_storage = {}; |
| 237 | memcpy(&sockaddr_storage, &ipv6_sockaddr, sizeof(ipv6_sockaddr)); |
| 238 | |
| 239 | stream.str(""); |
| 240 | stream << sockaddr_storage; |
| 241 | EXPECT_EQ(expected_output, stream.str()); |
| 242 | } |
| 243 | |
| 244 | TEST(PrettyPrint, SocketAddrVsock) { |
| 245 | struct sockaddr_vm vm_sockaddr = {}; |
| 246 | std::ostringstream stream; |
| 247 | |
| 248 | stream << vm_sockaddr; |
| 249 | EXPECT_EQ("{family: AF_VSOCK, port: 0, cid: 0}", stream.str()); |
| 250 | |
| 251 | vm_sockaddr.svm_family = AF_VSOCK; |
| 252 | vm_sockaddr.svm_port = 5555; |
| 253 | vm_sockaddr.svm_cid = 4; |
| 254 | std::string expected_output = "{family: AF_VSOCK, port: 5555, cid: 4}"; |
| 255 | |
| 256 | stream.str(""); |
| 257 | stream << vm_sockaddr; |
| 258 | EXPECT_EQ(expected_output, stream.str()); |
| 259 | |
| 260 | stream.str(""); |
| 261 | stream << (const struct sockaddr&)vm_sockaddr; |
| 262 | EXPECT_EQ(expected_output, stream.str()); |
| 263 | |
| 264 | struct sockaddr_storage sockaddr_storage = {}; |
| 265 | memcpy(&sockaddr_storage, &vm_sockaddr, sizeof(vm_sockaddr)); |
| 266 | |
| 267 | stream.str(""); |
| 268 | stream << sockaddr_storage; |
| 269 | EXPECT_EQ(expected_output, stream.str()); |
| 270 | } |
| 271 | |
| 272 | TEST(PrettyPrint, SocketAddrUnix) { |
| 273 | struct sockaddr_un unix_sockaddr = {}; |
| 274 | std::ostringstream stream; |
| 275 | |
| 276 | stream << unix_sockaddr; |
| 277 | EXPECT_EQ("{family: AF_UNIX, path: @}", stream.str()); |
| 278 | |
| 279 | // Fill |sun_path| with an invalid non-null-terminated c string. |
| 280 | std::string bogus_output = "{family: AF_UNIX, path: "; |
| 281 | for (size_t i = 0; i < sizeof(unix_sockaddr.sun_path); i++) { |
| 282 | unix_sockaddr.sun_path[i] = 'a'; |
| 283 | bogus_output += 'a'; |
| 284 | } |
| 285 | bogus_output += '}'; |
| 286 | stream.str(""); |
| 287 | stream << unix_sockaddr; |
| 288 | EXPECT_EQ(bogus_output, stream.str()); |
| 289 | |
| 290 | memset(&unix_sockaddr, 0, sizeof(unix_sockaddr)); |
| 291 | unix_sockaddr.sun_family = AF_UNIX; |
| 292 | std::string sun_path = "/run/arc/adb"; |
| 293 | memcpy(&unix_sockaddr.sun_path, sun_path.c_str(), strlen(sun_path.c_str())); |
| 294 | std::string expected_output = "{family: AF_UNIX, path: /run/arc/adb}"; |
| 295 | |
| 296 | stream.str(""); |
| 297 | stream << unix_sockaddr; |
| 298 | EXPECT_EQ(expected_output, stream.str()); |
| 299 | |
| 300 | stream.str(""); |
| 301 | stream << (const struct sockaddr&)unix_sockaddr; |
| 302 | EXPECT_EQ(expected_output, stream.str()); |
| 303 | |
| 304 | struct sockaddr_storage sockaddr_storage = {}; |
| 305 | memcpy(&sockaddr_storage, &unix_sockaddr, sizeof(unix_sockaddr)); |
| 306 | |
| 307 | stream.str(""); |
| 308 | stream << sockaddr_storage; |
| 309 | EXPECT_EQ(expected_output, stream.str()); |
| 310 | } |
| 311 | |
Hugo Benichi | e8758b5 | 2020-04-03 14:49:01 +0900 | [diff] [blame] | 312 | TEST(PrettyPrint, Rtentry) { |
| 313 | struct rtentry route; |
| 314 | memset(&route, 0, sizeof(route)); |
| 315 | std::ostringstream stream; |
| 316 | |
| 317 | stream << route; |
| 318 | EXPECT_EQ( |
| 319 | "{rt_dst: {unset}, rt_genmask: {unset}, rt_gateway: {unset}, rt_dev: " |
| 320 | "null, rt_flags: 0}", |
| 321 | stream.str()); |
| 322 | |
| 323 | SetSockaddrIn(&route.rt_dst, Ipv4Addr(100, 115, 92, 128)); |
| 324 | SetSockaddrIn(&route.rt_genmask, Ipv4Addr(255, 255, 255, 252)); |
| 325 | SetSockaddrIn(&route.rt_gateway, Ipv4Addr(192, 168, 1, 1)); |
| 326 | std::string rt_dev = "eth0"; |
| 327 | route.rt_dev = (char*)rt_dev.c_str(); |
| 328 | route.rt_flags = |
| 329 | RTF_UP | RTF_GATEWAY | RTF_DYNAMIC | RTF_MODIFIED | RTF_REJECT; |
| 330 | stream.str(""); |
| 331 | stream << route; |
| 332 | EXPECT_EQ( |
| 333 | "{rt_dst: {family: AF_INET, port: 0, addr: 100.115.92.128}, rt_genmask: " |
| 334 | "{family: AF_INET, port: 0, addr: 255.255.255.252}, rt_gateway: {family: " |
| 335 | "AF_INET, port: 0, addr: 192.168.1.1}, rt_dev: eth0, rt_flags: RTF_UP | " |
| 336 | "RTF_GATEWAY | RTF_DYNAMIC | RTF_MODIFIED | RTF_REJECT}", |
| 337 | stream.str()); |
| 338 | } |
| 339 | |
Garrick Evans | 3388a03 | 2020-03-24 11:25:55 +0900 | [diff] [blame^] | 340 | } // namespace patchpanel |