pcp_tests.cpp
1 // Copyright (c) 2024-present The Bitcoin Core developers 2 // Distributed under the MIT software license, see the accompanying 3 // file COPYING or http://www.opensource.org/licenses/mit-license.php. 4 5 #include <common/pcp.h> 6 #include <netbase.h> 7 #include <test/util/logging.h> 8 #include <test/util/common.h> 9 #include <test/util/setup_common.h> 10 #include <util/time.h> 11 12 #include <boost/test/unit_test.hpp> 13 14 #include <algorithm> 15 #include <deque> 16 17 using namespace std::literals; 18 19 static CThreadInterrupt g_interrupt; 20 21 /// UDP test server operation. 22 struct TestOp { 23 std::chrono::milliseconds delay; 24 enum Op { 25 SEND, // Expect send (with optional data) 26 RECV, // Expect receive (with data) 27 NOP, // Just delay 28 } op; 29 std::vector<uint8_t> data; 30 31 //! Injected error. 32 //! Set this field to a non-zero value to return the errno code from send or receive operation. 33 int error; 34 35 TestOp(std::chrono::milliseconds delay_in, Op op_in, const std::vector<uint8_t> &data_in, int error_in): 36 delay(delay_in), op(op_in), data(data_in), error(error_in) {} 37 }; 38 39 /// Save the value of CreateSock and restore when the test ends. 40 class PCPTestingSetup : public BasicTestingSetup 41 { 42 public: 43 explicit PCPTestingSetup(const ChainType chainType = ChainType::MAIN, 44 TestOpts opts = {}) 45 : BasicTestingSetup{chainType, opts}, 46 m_create_sock_orig{CreateSock} 47 { 48 const std::optional<CService> local_ipv4{Lookup("192.168.0.6", 1, false)}; 49 const std::optional<CService> local_ipv6{Lookup("2a10:1234:5678:9abc:def0:1234:5678:9abc", 1, false)}; 50 const std::optional<CService> gateway_ipv4{Lookup("192.168.0.1", 1, false)}; 51 const std::optional<CService> gateway_ipv6{Lookup("2a10:1234:5678:9abc:def0:0000:0000:0000", 1, false)}; 52 BOOST_REQUIRE(local_ipv4 && local_ipv6 && gateway_ipv4 && gateway_ipv6); 53 default_local_ipv4 = *local_ipv4; 54 default_local_ipv6 = *local_ipv6; 55 default_gateway_ipv4 = *gateway_ipv4; 56 default_gateway_ipv6 = *gateway_ipv6; 57 58 struct in_addr inaddr_any; 59 inaddr_any.s_addr = htonl(INADDR_ANY); 60 bind_any_ipv4 = CNetAddr(inaddr_any); 61 } 62 63 ~PCPTestingSetup() 64 { 65 CreateSock = m_create_sock_orig; 66 MockableSteadyClock::ClearMockTime(); 67 } 68 69 // Default testing nonce. 70 static constexpr PCPMappingNonce TEST_NONCE{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc}; 71 // Default network addresses. 72 CNetAddr default_local_ipv4; 73 CNetAddr default_local_ipv6; 74 CNetAddr default_gateway_ipv4; 75 CNetAddr default_gateway_ipv6; 76 // IPv4 bind 77 CNetAddr bind_any_ipv4; 78 private: 79 const decltype(CreateSock) m_create_sock_orig; 80 }; 81 82 /** Simple scripted UDP server emulation for testing. 83 */ 84 class PCPTestSock final : public Sock 85 { 86 public: 87 // Note: we awkwardly mark all methods as const, and properties as mutable, 88 // because Sock expects all networking calls to be const. 89 explicit PCPTestSock(const CNetAddr &local_ip, const CNetAddr &gateway_ip, const std::vector<TestOp> &script) 90 : Sock{INVALID_SOCKET}, 91 m_script(script), 92 m_local_ip(local_ip), 93 m_gateway_ip(gateway_ip) 94 { 95 ElapseTime(std::chrono::seconds(0)); // start mocking steady time 96 PrepareOp(); 97 } 98 99 PCPTestSock& operator=(Sock&& other) override 100 { 101 assert(false && "Move of Sock into PCPTestSock not allowed."); 102 return *this; 103 } 104 105 ssize_t Send(const void* data, size_t len, int) const override { 106 if (!m_connected) return -1; 107 std::span in_pkt = std::span(static_cast<const uint8_t*>(data), len); 108 if (AtEndOfScript() || CurOp().op != TestOp::SEND) { 109 // Ignore sends after end of script, or sends when we expect a receive. 110 FailScript(); 111 return len; 112 } 113 if (CurOp().error) return -1; // Inject failure 114 if (CurOp().data.empty() || std::ranges::equal(CurOp().data, in_pkt)) { 115 AdvanceOp(); 116 } else { 117 // Wrong send, fail script 118 FailScript(); 119 } 120 return len; 121 } 122 123 ssize_t Recv(void* buf, size_t len, int flags) const override 124 { 125 if (!m_connected || AtEndOfScript() || CurOp().op != TestOp::RECV || m_time_left != 0s) { 126 return -1; 127 } 128 if (CurOp().error) return -1; // Inject failure 129 const auto &recv_pkt = CurOp().data; 130 const size_t consume_bytes{std::min(len, recv_pkt.size())}; 131 std::memcpy(buf, recv_pkt.data(), consume_bytes); 132 if ((flags & MSG_PEEK) == 0) { 133 AdvanceOp(); 134 } 135 return consume_bytes; 136 } 137 138 int Connect(const sockaddr* sa, socklen_t sa_len) const override { 139 CService service; 140 if (service.SetSockAddr(sa, sa_len) && service == CService(m_gateway_ip, 5351)) { 141 if (m_bound.IsBindAny()) { // If bind-any, bind to local ip. 142 m_bound = CService(m_local_ip, 0); 143 } 144 if (m_bound.GetPort() == 0) { // If no port assigned, assign port 1. 145 m_bound = CService(m_bound, 1); 146 } 147 m_connected = true; 148 return 0; 149 } 150 return -1; 151 } 152 153 int Bind(const sockaddr* sa, socklen_t sa_len) const override { 154 CService service; 155 if (service.SetSockAddr(sa, sa_len)) { 156 // Can only bind to one of our local ips 157 if (!service.IsBindAny() && service != m_local_ip) { 158 return -1; 159 } 160 m_bound = service; 161 return 0; 162 } 163 return -1; 164 } 165 166 int Listen(int) const override { return -1; } 167 168 std::unique_ptr<Sock> Accept(sockaddr* addr, socklen_t* addr_len) const override 169 { 170 return nullptr; 171 }; 172 173 int GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const override 174 { 175 return -1; 176 } 177 178 int SetSockOpt(int, int, const void*, socklen_t) const override { return 0; } 179 180 int GetSockName(sockaddr* name, socklen_t* name_len) const override 181 { 182 // Return the address we've been bound to. 183 return m_bound.GetSockAddr(name, name_len) ? 0 : -1; 184 } 185 186 bool SetNonBlocking() const override { return true; } 187 188 bool IsSelectable() const override { return true; } 189 190 bool Wait(std::chrono::milliseconds timeout, 191 Event requested, 192 Event* occurred = nullptr) const override 193 { 194 // Only handles receive events. 195 if (AtEndOfScript() || requested != Sock::RECV) { 196 ElapseTime(timeout); 197 } else { 198 std::chrono::milliseconds delay = std::min(m_time_left, timeout); 199 ElapseTime(delay); 200 m_time_left -= delay; 201 if (CurOp().op == TestOp::RECV && m_time_left == 0s && occurred != nullptr) { 202 *occurred = Sock::RECV; 203 } 204 if (CurOp().op == TestOp::NOP) { 205 // This was a pure delay operation, move to the next op. 206 AdvanceOp(); 207 } 208 } 209 return true; 210 } 211 212 bool WaitMany(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) const override 213 { 214 return false; 215 } 216 217 bool IsConnected(std::string&) const override 218 { 219 return true; 220 } 221 222 private: 223 const std::vector<TestOp> m_script; 224 mutable size_t m_script_ptr = 0; 225 mutable std::chrono::milliseconds m_time_left; 226 mutable std::chrono::milliseconds m_time{MockableSteadyClock::INITIAL_MOCK_TIME}; 227 mutable bool m_connected{false}; 228 mutable CService m_bound; 229 mutable CNetAddr m_local_ip; 230 mutable CNetAddr m_gateway_ip; 231 232 void ElapseTime(std::chrono::milliseconds duration) const 233 { 234 m_time += duration; 235 MockableSteadyClock::SetMockTime(m_time); 236 } 237 238 bool AtEndOfScript() const { return m_script_ptr == m_script.size(); } 239 const TestOp &CurOp() const { 240 BOOST_REQUIRE(m_script_ptr < m_script.size()); 241 return m_script[m_script_ptr]; 242 } 243 244 void PrepareOp() const { 245 if (AtEndOfScript()) return; 246 m_time_left = CurOp().delay; 247 } 248 249 void AdvanceOp() const 250 { 251 m_script_ptr += 1; 252 PrepareOp(); 253 } 254 255 void FailScript() const { m_script_ptr = m_script.size(); } 256 }; 257 258 BOOST_FIXTURE_TEST_SUITE(pcp_tests, PCPTestingSetup) 259 260 // NAT-PMP IPv4 good-weather scenario. 261 BOOST_AUTO_TEST_CASE(natpmp_ipv4) 262 { 263 const std::vector<TestOp> script{ 264 { 265 0ms, TestOp::SEND, 266 { 267 0x00, 0x00, // version, opcode (request external IP) 268 }, 0 269 }, 270 { 271 2ms, TestOp::RECV, 272 { 273 0x00, 0x80, 0x00, 0x00, // version, opcode (external IP), result code (success) 274 0x66, 0xfd, 0xa1, 0xee, // seconds sinds start of epoch 275 0x01, 0x02, 0x03, 0x04, // external IP address 276 }, 0 277 }, 278 { 279 0ms, TestOp::SEND, 280 { 281 0x00, 0x02, 0x00, 0x00, // version, opcode (request map TCP) 282 0x04, 0xd2, 0x04, 0xd2, // internal port, suggested external port 283 0x00, 0x00, 0x03, 0xe8, // requested mapping lifetime in seconds 284 }, 0 285 }, 286 { 287 2ms, TestOp::RECV, 288 { 289 0x00, 0x82, 0x00, 0x00, // version, opcode (mapped TCP) 290 0x66, 0xfd, 0xa1, 0xee, // seconds sinds start of epoch 291 0x04, 0xd2, 0x04, 0xd2, // internal port, mapped external port 292 0x00, 0x00, 0x01, 0xf4, // mapping lifetime in seconds 293 }, 0 294 }, 295 }; 296 CreateSock = [this, &script](int domain, int type, int protocol) { 297 if (domain == AF_INET && type == SOCK_DGRAM && protocol == IPPROTO_UDP) return std::make_unique<PCPTestSock>(default_local_ipv4, default_gateway_ipv4, script); 298 return std::unique_ptr<PCPTestSock>(); 299 }; 300 301 auto res = NATPMPRequestPortMap(default_gateway_ipv4, 1234, 1000, g_interrupt, 1, 200ms); 302 303 MappingResult* mapping = std::get_if<MappingResult>(&res); 304 BOOST_REQUIRE(mapping); 305 BOOST_CHECK_EQUAL(mapping->version, 0); 306 BOOST_CHECK_EQUAL(mapping->internal.ToStringAddrPort(), "192.168.0.6:1234"); 307 BOOST_CHECK_EQUAL(mapping->external.ToStringAddrPort(), "1.2.3.4:1234"); 308 BOOST_CHECK_EQUAL(mapping->lifetime, 500); 309 } 310 311 // PCP IPv4 good-weather scenario. 312 BOOST_AUTO_TEST_CASE(pcp_ipv4) 313 { 314 const std::vector<TestOp> script{ 315 { 316 0ms, TestOp::SEND, 317 { 318 0x02, 0x01, 0x00, 0x00, // version, opcode 319 0x00, 0x00, 0x03, 0xe8, // lifetime 320 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc0, 0xa8, 0x00, 0x06, // internal IP 321 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, // nonce 322 0x06, 0x00, 0x00, 0x00, // protocol (TCP), reserved 323 0x04, 0xd2, 0x04, 0xd2, // internal port, suggested external port 324 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, // suggested external IP 325 }, 0 326 }, 327 { 328 250ms, TestOp::RECV, // 250ms delay before answer 329 { 330 0x02, 0x81, 0x00, 0x00, // version, opcode, result success 331 0x00, 0x00, 0x01, 0xf4, // granted lifetime 332 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved 333 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, // nonce 334 0x06, 0x00, 0x00, 0x00, // protocol (TCP), reserved 335 0x04, 0xd2, 0x04, 0xd2, // internal port, assigned external port 336 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x02, 0x03, 0x04, // assigned external IP 337 }, 0 338 }, 339 }; 340 CreateSock = [this, &script](int domain, int type, int protocol) { 341 if (domain == AF_INET && type == SOCK_DGRAM && protocol == IPPROTO_UDP) return std::make_unique<PCPTestSock>(default_local_ipv4, default_gateway_ipv4, script); 342 return std::unique_ptr<PCPTestSock>(); 343 }; 344 345 auto res = PCPRequestPortMap(TEST_NONCE, default_gateway_ipv4, bind_any_ipv4, 1234, 1000, g_interrupt, 1, 1000ms); 346 347 MappingResult* mapping = std::get_if<MappingResult>(&res); 348 BOOST_REQUIRE(mapping); 349 BOOST_CHECK_EQUAL(mapping->version, 2); 350 BOOST_CHECK_EQUAL(mapping->internal.ToStringAddrPort(), "192.168.0.6:1234"); 351 BOOST_CHECK_EQUAL(mapping->external.ToStringAddrPort(), "1.2.3.4:1234"); 352 BOOST_CHECK_EQUAL(mapping->lifetime, 500); 353 } 354 355 // PCP IPv6 good-weather scenario. 356 BOOST_AUTO_TEST_CASE(pcp_ipv6) 357 { 358 const std::vector<TestOp> script{ 359 { 360 0ms, TestOp::SEND, 361 { 362 0x02, 0x01, 0x00, 0x00, // version, opcode 363 0x00, 0x00, 0x03, 0xe8, // lifetime 364 0x2a, 0x10, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, // internal IP 365 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, // nonce 366 0x06, 0x00, 0x00, 0x00, // protocol (TCP), reserved 367 0x04, 0xd2, 0x04, 0xd2, // internal port, suggested external port 368 0x2a, 0x10, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, // suggested external IP 369 }, 0 370 }, 371 { 372 500ms, TestOp::RECV, // 500ms delay before answer 373 { 374 0x02, 0x81, 0x00, 0x00, // version, opcode, result success 375 0x00, 0x00, 0x01, 0xf4, // granted lifetime 376 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved 377 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, // nonce 378 0x06, 0x00, 0x00, 0x00, // protocol (TCP), reserved 379 0x04, 0xd2, 0x04, 0xd2, // internal port, assigned external port 380 0x2a, 0x10, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, // suggested external IP 381 }, 0 382 }, 383 }; 384 CreateSock = [this, &script](int domain, int type, int protocol) { 385 if (domain == AF_INET6 && type == SOCK_DGRAM && protocol == IPPROTO_UDP) return std::make_unique<PCPTestSock>(default_local_ipv6, default_gateway_ipv6, script); 386 return std::unique_ptr<PCPTestSock>(); 387 }; 388 389 auto res = PCPRequestPortMap(TEST_NONCE, default_gateway_ipv6, default_local_ipv6, 1234, 1000, g_interrupt, 1, 1000ms); 390 391 MappingResult* mapping = std::get_if<MappingResult>(&res); 392 BOOST_REQUIRE(mapping); 393 BOOST_CHECK_EQUAL(mapping->version, 2); 394 BOOST_CHECK_EQUAL(mapping->internal.ToStringAddrPort(), "[2a10:1234:5678:9abc:def0:1234:5678:9abc]:1234"); 395 BOOST_CHECK_EQUAL(mapping->external.ToStringAddrPort(), "[2a10:1234:5678:9abc:def0:1234:5678:9abc]:1234"); 396 BOOST_CHECK_EQUAL(mapping->lifetime, 500); 397 } 398 399 // PCP timeout. 400 BOOST_AUTO_TEST_CASE(pcp_timeout) 401 { 402 const std::vector<TestOp> script{}; 403 CreateSock = [this, &script](int domain, int type, int protocol) { 404 if (domain == AF_INET && type == SOCK_DGRAM && protocol == IPPROTO_UDP) return std::make_unique<PCPTestSock>(default_local_ipv4, default_gateway_ipv4, script); 405 return std::unique_ptr<PCPTestSock>(); 406 }; 407 408 ASSERT_DEBUG_LOG("pcp: Retrying (1)"); 409 ASSERT_DEBUG_LOG("pcp: Retrying (2)"); 410 ASSERT_DEBUG_LOG("pcp: Timeout"); 411 412 auto res = PCPRequestPortMap(TEST_NONCE, default_gateway_ipv4, bind_any_ipv4, 1234, 1000, g_interrupt, 3, 2000ms); 413 414 MappingError* err = std::get_if<MappingError>(&res); 415 BOOST_REQUIRE(err); 416 BOOST_CHECK_EQUAL(*err, MappingError::NETWORK_ERROR); 417 } 418 419 // PCP failure receiving (router sends ICMP port closed). 420 BOOST_AUTO_TEST_CASE(pcp_connrefused) 421 { 422 const std::vector<TestOp> script{ 423 { 424 0ms, TestOp::SEND, 425 { // May send anything. 426 }, 0 427 }, 428 { 429 0ms, TestOp::RECV, 430 { 431 }, ECONNREFUSED 432 }, 433 }; 434 CreateSock = [this, &script](int domain, int type, int protocol) { 435 if (domain == AF_INET && type == SOCK_DGRAM && protocol == IPPROTO_UDP) return std::make_unique<PCPTestSock>(default_local_ipv4, default_gateway_ipv4, script); 436 return std::unique_ptr<PCPTestSock>(); 437 }; 438 439 ASSERT_DEBUG_LOG("pcp: Could not receive response"); 440 441 auto res = PCPRequestPortMap(TEST_NONCE, default_gateway_ipv4, bind_any_ipv4, 1234, 1000, g_interrupt, 3, 2000ms); 442 443 MappingError* err = std::get_if<MappingError>(&res); 444 BOOST_REQUIRE(err); 445 BOOST_CHECK_EQUAL(*err, MappingError::NETWORK_ERROR); 446 } 447 448 // PCP IPv6 success after one timeout. 449 BOOST_AUTO_TEST_CASE(pcp_ipv6_timeout_success) 450 { 451 const std::vector<TestOp> script{ 452 { 453 0ms, TestOp::SEND, 454 { 455 0x02, 0x01, 0x00, 0x00, // version, opcode 456 0x00, 0x00, 0x03, 0xe8, // lifetime 457 0x2a, 0x10, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, // internal IP 458 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, // nonce 459 0x06, 0x00, 0x00, 0x00, // protocol (TCP), reserved 460 0x04, 0xd2, 0x04, 0xd2, // internal port, suggested external port 461 0x2a, 0x10, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, // suggested external IP 462 }, 0 463 }, 464 { 465 2001ms, TestOp::NOP, // Takes longer to respond than timeout of 2000ms 466 {}, 0 467 }, 468 { 469 0ms, TestOp::SEND, // Repeated send (try 2) 470 { 471 0x02, 0x01, 0x00, 0x00, // version, opcode 472 0x00, 0x00, 0x03, 0xe8, // lifetime 473 0x2a, 0x10, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, // internal IP 474 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, // nonce 475 0x06, 0x00, 0x00, 0x00, // protocol (TCP), reserved 476 0x04, 0xd2, 0x04, 0xd2, // internal port, suggested external port 477 0x2a, 0x10, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, // suggested external IP 478 }, 0 479 }, 480 { 481 200ms, TestOp::RECV, // This time we're in time 482 { 483 0x02, 0x81, 0x00, 0x00, // version, opcode, result success 484 0x00, 0x00, 0x01, 0xf4, // granted lifetime 485 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved 486 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, // nonce 487 0x06, 0x00, 0x00, 0x00, // protocol (TCP), reserved 488 0x04, 0xd2, 0x04, 0xd2, // internal port, assigned external port 489 0x2a, 0x10, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, // suggested external IP 490 }, 0 491 }, 492 }; 493 CreateSock = [this, &script](int domain, int type, int protocol) { 494 if (domain == AF_INET6 && type == SOCK_DGRAM && protocol == IPPROTO_UDP) return std::make_unique<PCPTestSock>(default_local_ipv6, default_gateway_ipv6, script); 495 return std::unique_ptr<PCPTestSock>(); 496 }; 497 498 ASSERT_DEBUG_LOG("pcp: Retrying (1)"); 499 ASSERT_DEBUG_LOG("pcp: Timeout"); 500 501 auto res = PCPRequestPortMap(TEST_NONCE, default_gateway_ipv6, default_local_ipv6, 1234, 1000, g_interrupt, 2, 2000ms); 502 503 BOOST_CHECK(std::get_if<MappingResult>(&res)); 504 } 505 506 // PCP IPv4 failure (no resources). 507 BOOST_AUTO_TEST_CASE(pcp_ipv4_fail_no_resources) 508 { 509 const std::vector<TestOp> script{ 510 { 511 0ms, TestOp::SEND, 512 { 513 0x02, 0x01, 0x00, 0x00, // version, opcode 514 0x00, 0x00, 0x03, 0xe8, // lifetime 515 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc0, 0xa8, 0x00, 0x06, // internal IP 516 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, // nonce 517 0x06, 0x00, 0x00, 0x00, // protocol (TCP), reserved 518 0x04, 0xd2, 0x04, 0xd2, // internal port, suggested external port 519 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, // suggested external IP 520 }, 0 521 }, 522 { 523 500ms, TestOp::RECV, 524 { 525 0x02, 0x81, 0x00, 0x08, // version, opcode, result 0x08: no resources 526 0x00, 0x00, 0x00, 0x00, // granted lifetime 527 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved 528 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, // nonce 529 0x06, 0x00, 0x00, 0x00, // protocol (TCP), reserved 530 0x04, 0xd2, 0x00, 0x00, // internal port, assigned external port 531 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // assigned external IP 532 }, 0 533 }, 534 }; 535 CreateSock = [this, &script](int domain, int type, int protocol) { 536 if (domain == AF_INET && type == SOCK_DGRAM && protocol == IPPROTO_UDP) return std::make_unique<PCPTestSock>(default_local_ipv4, default_gateway_ipv4, script); 537 return std::unique_ptr<PCPTestSock>(); 538 }; 539 540 auto res = PCPRequestPortMap(TEST_NONCE, default_gateway_ipv4, bind_any_ipv4, 1234, 1000, g_interrupt, 3, 1000ms); 541 542 MappingError* err = std::get_if<MappingError>(&res); 543 BOOST_REQUIRE(err); 544 BOOST_CHECK_EQUAL(*err, MappingError::NO_RESOURCES); 545 } 546 547 // PCP IPv4 failure (test NATPMP downgrade scenario). 548 BOOST_AUTO_TEST_CASE(pcp_ipv4_fail_unsupported_version) 549 { 550 const std::vector<TestOp> script{ 551 { 552 0ms, TestOp::SEND, 553 { 554 0x02, 0x01, 0x00, 0x00, // version, opcode 555 0x00, 0x00, 0x03, 0xe8, // lifetime 556 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc0, 0xa8, 0x00, 0x06, // internal IP 557 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, // nonce 558 0x06, 0x00, 0x00, 0x00, // protocol (TCP), reserved 559 0x04, 0xd2, 0x04, 0xd2, // internal port, suggested external port 560 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, // suggested external IP 561 }, 0 562 }, 563 { 564 500ms, TestOp::RECV, 565 { 566 0x00, 0x81, 0x00, 0x01, // version, opcode, result 0x01: unsupported version 567 0x00, 0x00, 0x00, 0x00, 568 }, 0 569 }, 570 }; 571 CreateSock = [this, &script](int domain, int type, int protocol) { 572 if (domain == AF_INET && type == SOCK_DGRAM && protocol == IPPROTO_UDP) return std::make_unique<PCPTestSock>(default_local_ipv4, default_gateway_ipv4, script); 573 return std::unique_ptr<PCPTestSock>(); 574 }; 575 576 auto res = PCPRequestPortMap(TEST_NONCE, default_gateway_ipv4, bind_any_ipv4, 1234, 1000, g_interrupt, 3, 1000ms); 577 578 MappingError* err = std::get_if<MappingError>(&res); 579 BOOST_REQUIRE(err); 580 BOOST_CHECK_EQUAL(*err, MappingError::UNSUPP_VERSION); 581 } 582 583 // NAT-PMP IPv4 protocol error scenarii. 584 BOOST_AUTO_TEST_CASE(natpmp_protocol_error) 585 { 586 // First scenario: non-0 result code when requesting external IP. 587 std::vector<TestOp> script{ 588 { 589 0ms, TestOp::SEND, 590 { 591 0x00, 0x00, // version, opcode (request external IP) 592 }, 0 593 }, 594 { 595 2ms, TestOp::RECV, 596 { 597 0x00, 0x80, 0x00, 0x42, // version, opcode (external IP), result code (*NOT* success) 598 0x66, 0xfd, 0xa1, 0xee, // seconds sinds start of epoch 599 0x01, 0x02, 0x03, 0x04, // external IP address 600 }, 0 601 }, 602 }; 603 CreateSock = [this, &script](int domain, int type, int protocol) { 604 if (domain == AF_INET && type == SOCK_DGRAM && protocol == IPPROTO_UDP) return std::make_unique<PCPTestSock>(default_local_ipv4, default_gateway_ipv4, script); 605 return std::unique_ptr<PCPTestSock>(); 606 }; 607 608 auto res = NATPMPRequestPortMap(default_gateway_ipv4, 1234, 1000, g_interrupt, 1, 200ms); 609 610 MappingError* err = std::get_if<MappingError>(&res); 611 BOOST_REQUIRE(err); 612 BOOST_CHECK_EQUAL(*err, MappingError::PROTOCOL_ERROR); 613 614 // First scenario: non-0 result code when requesting port mapping. 615 script = { 616 { 617 0ms, TestOp::SEND, 618 { 619 0x00, 0x00, // version, opcode (request external IP) 620 }, 0 621 }, 622 { 623 2ms, TestOp::RECV, 624 { 625 0x00, 0x80, 0x00, 0x00, // version, opcode (external IP), result code (success) 626 0x66, 0xfd, 0xa1, 0xee, // seconds sinds start of epoch 627 0x01, 0x02, 0x03, 0x04, // external IP address 628 }, 0 629 }, 630 { 631 0ms, TestOp::SEND, 632 { 633 0x00, 0x02, 0x00, 0x00, // version, opcode (request map TCP) 634 0x04, 0xd2, 0x04, 0xd2, // internal port, suggested external port 635 0x00, 0x00, 0x03, 0xe8, // requested mapping lifetime in seconds 636 }, 0 637 }, 638 { 639 2ms, TestOp::RECV, 640 { 641 0x00, 0x82, 0x00, 0x43, // version, opcode (mapped TCP) 642 0x66, 0xfd, 0xa1, 0xee, // seconds sinds start of epoch 643 0x04, 0xd2, 0x04, 0xd2, // internal port, mapped external port 644 0x00, 0x00, 0x01, 0xf4, // mapping lifetime in seconds 645 }, 0 646 }, 647 }; 648 CreateSock = [this, &script](int domain, int type, int protocol) { 649 if (domain == AF_INET && type == SOCK_DGRAM && protocol == IPPROTO_UDP) return std::make_unique<PCPTestSock>(default_local_ipv4, default_gateway_ipv4, script); 650 return std::unique_ptr<PCPTestSock>(); 651 }; 652 653 res = NATPMPRequestPortMap(default_gateway_ipv4, 1234, 1000, g_interrupt, 1, 200ms); 654 655 err = std::get_if<MappingError>(&res); 656 BOOST_REQUIRE(err); 657 BOOST_CHECK_EQUAL(*err, MappingError::PROTOCOL_ERROR); 658 } 659 660 // PCP IPv4 protocol error scenario. 661 BOOST_AUTO_TEST_CASE(pcp_protocol_error) 662 { 663 const std::vector<TestOp> script{ 664 { 665 0ms, TestOp::SEND, 666 { 667 0x02, 0x01, 0x00, 0x00, // version, opcode 668 0x00, 0x00, 0x03, 0xe8, // lifetime 669 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc0, 0xa8, 0x00, 0x06, // internal IP 670 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, // nonce 671 0x06, 0x00, 0x00, 0x00, // protocol (TCP), reserved 672 0x04, 0xd2, 0x04, 0xd2, // internal port, suggested external port 673 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, // suggested external IP 674 }, 0 675 }, 676 { 677 250ms, TestOp::RECV, // 250ms delay before answer 678 { 679 0x02, 0x81, 0x00, 0x42, // version, opcode, result error 680 0x00, 0x00, 0x01, 0xf4, // granted lifetime 681 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved 682 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, // nonce 683 0x06, 0x00, 0x00, 0x00, // protocol (TCP), reserved 684 0x04, 0xd2, 0x04, 0xd2, // internal port, assigned external port 685 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x02, 0x03, 0x04, // assigned external IP 686 }, 0 687 }, 688 }; 689 CreateSock = [this, &script](int domain, int type, int protocol) { 690 if (domain == AF_INET && type == SOCK_DGRAM && protocol == IPPROTO_UDP) return std::make_unique<PCPTestSock>(default_local_ipv4, default_gateway_ipv4, script); 691 return std::unique_ptr<PCPTestSock>(); 692 }; 693 694 auto res = PCPRequestPortMap(TEST_NONCE, default_gateway_ipv4, bind_any_ipv4, 1234, 1000, g_interrupt, 1, 1000ms); 695 696 MappingError* err = std::get_if<MappingError>(&res); 697 BOOST_REQUIRE(err); 698 BOOST_CHECK_EQUAL(*err, MappingError::PROTOCOL_ERROR); 699 } 700 701 BOOST_AUTO_TEST_SUITE_END() 702