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