/ src / test / pcp_tests.cpp
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