Bitcoin ABC 0.33.3
P2P Digital Currency
pcp.cpp
Go to the documentation of this file.
1// Copyright (c) 2024 The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or https://www.opensource.org/licenses/mit-license.php.
4
5#include <common/pcp.h>
6
7#include <atomic>
8#include <common/netif.h>
9#include <crypto/common.h>
10#include <logging.h>
11#include <netaddress.h>
12#include <netbase.h>
13#include <random.h>
14#include <span.h>
15#include <util/check.h>
16#include <util/readwritefile.h>
17#include <util/sock.h>
18#include <util/strencodings.h>
20
21namespace {
22
23// RFC6886 NAT-PMP and RFC6887 Port Control Protocol (PCP) implementation.
24// NAT-PMP and PCP use network byte order (big-endian).
25
26// NAT-PMP (v0) protocol constants.
28constexpr uint16_t NATPMP_SERVER_PORT = 5351;
30constexpr uint8_t NATPMP_VERSION = 0;
32constexpr uint8_t NATPMP_REQUEST = 0x00;
34constexpr uint8_t NATPMP_RESPONSE = 0x80;
36constexpr uint8_t NATPMP_OP_GETEXTERNAL = 0x00;
38constexpr uint8_t NATPMP_OP_MAP_TCP = 0x02;
40constexpr size_t NATPMP_REQUEST_HDR_SIZE = 2;
42constexpr size_t NATPMP_RESPONSE_HDR_SIZE = 8;
44constexpr size_t NATPMP_GETEXTERNAL_REQUEST_SIZE = NATPMP_REQUEST_HDR_SIZE + 0;
46constexpr size_t NATPMP_GETEXTERNAL_RESPONSE_SIZE =
47 NATPMP_RESPONSE_HDR_SIZE + 4;
49constexpr size_t NATPMP_MAP_REQUEST_SIZE = NATPMP_REQUEST_HDR_SIZE + 10;
51constexpr size_t NATPMP_MAP_RESPONSE_SIZE = NATPMP_RESPONSE_HDR_SIZE + 8;
52
53// Shared header offsets (RFC6886 3.2, 3.3), relative to start of packet.
55constexpr size_t NATPMP_HDR_VERSION_OFS = 0;
57constexpr size_t NATPMP_HDR_OP_OFS = 1;
60constexpr size_t NATPMP_RESPONSE_HDR_RESULT_OFS = 2;
61
62// GETEXTERNAL response offsets (RFC6886 3.2), relative to start of packet.
64constexpr size_t NATPMP_GETEXTERNAL_RESPONSE_IP_OFS = 8;
65
66// MAP request offsets (RFC6886 3.3), relative to start of packet.
68constexpr size_t NATPMP_MAP_REQUEST_INTERNAL_PORT_OFS = 4;
70constexpr size_t NATPMP_MAP_REQUEST_EXTERNAL_PORT_OFS = 6;
72constexpr size_t NATPMP_MAP_REQUEST_LIFETIME_OFS = 8;
73
74// MAP response offsets (RFC6886 3.3), relative to start of packet.
76constexpr size_t NATPMP_MAP_RESPONSE_INTERNAL_PORT_OFS = 8;
78constexpr size_t NATPMP_MAP_RESPONSE_EXTERNAL_PORT_OFS = 10;
80constexpr size_t NATPMP_MAP_RESPONSE_LIFETIME_OFS = 12;
81
82// Relevant NETPMP result codes (RFC6886 3.5).
84constexpr uint8_t NATPMP_RESULT_SUCCESS = 0;
86constexpr uint8_t NATPMP_RESULT_UNSUPP_VERSION = 1;
89constexpr uint8_t NATPMP_RESULT_NOT_AUTHORIZED = 2;
91constexpr uint8_t NATPMP_RESULT_NO_RESOURCES = 4;
92
95const std::map<uint16_t, std::string> NATPMP_RESULT_STR{
96 {0, "SUCCESS"}, {1, "UNSUPP_VERSION"}, {2, "NOT_AUTHORIZED"},
97 {3, "NETWORK_FAILURE"}, {4, "NO_RESOURCES"}, {5, "UNSUPP_OPCODE"},
98};
99
100// PCP (v2) protocol constants.
102constexpr size_t PCP_MAX_SIZE = 1100;
105constexpr uint16_t PCP_SERVER_PORT = NATPMP_SERVER_PORT;
107constexpr uint8_t PCP_VERSION = 2;
109constexpr uint8_t PCP_REQUEST = NATPMP_REQUEST; // R = 0
111constexpr uint8_t PCP_RESPONSE = NATPMP_RESPONSE; // R = 1
113constexpr uint8_t PCP_OP_MAP = 0x01;
115constexpr uint16_t PCP_PROTOCOL_TCP = 6;
117constexpr size_t PCP_HDR_SIZE = 24;
119constexpr size_t PCP_MAP_SIZE = 36;
120
121// Header offsets shared between request and responses (RFC6887 7.1, 7.2),
122// relative to start of packet.
124constexpr size_t PCP_HDR_VERSION_OFS = NATPMP_HDR_VERSION_OFS;
126constexpr size_t PCP_HDR_OP_OFS = NATPMP_HDR_OP_OFS;
128constexpr size_t PCP_HDR_LIFETIME_OFS = 4;
129
130// Request header offsets (RFC6887 7.1), relative to start of packet.
132constexpr size_t PCP_REQUEST_HDR_IP_OFS = 8;
133
134// Response header offsets (RFC6887 7.2), relative to start of packet.
136constexpr size_t PCP_RESPONSE_HDR_RESULT_OFS = 3;
137
138// MAP request/response offsets (RFC6887 11.1), relative to start of
139// opcode-specific data.
141constexpr size_t PCP_MAP_NONCE_OFS = 0;
143constexpr size_t PCP_MAP_PROTOCOL_OFS = 12;
145constexpr size_t PCP_MAP_INTERNAL_PORT_OFS = 16;
148constexpr size_t PCP_MAP_EXTERNAL_PORT_OFS = 18;
151constexpr size_t PCP_MAP_EXTERNAL_IP_OFS = 20;
152
154constexpr uint8_t PCP_RESULT_SUCCESS = NATPMP_RESULT_SUCCESS;
156constexpr uint8_t PCP_RESULT_NOT_AUTHORIZED = NATPMP_RESULT_NOT_AUTHORIZED;
158constexpr uint8_t PCP_RESULT_NO_RESOURCES = 8;
159
162const std::map<uint8_t, std::string> PCP_RESULT_STR{
163 {0, "SUCCESS"}, {1, "UNSUPP_VERSION"},
164 {2, "NOT_AUTHORIZED"}, {3, "MALFORMED_REQUEST"},
165 {4, "UNSUPP_OPCODE"}, {5, "UNSUPP_OPTION"},
166 {6, "MALFORMED_OPTION"}, {7, "NETWORK_FAILURE"},
167 {8, "NO_RESOURCES"}, {9, "UNSUPP_PROTOCOL"},
168 {10, "USER_EX_QUOTA"}, {11, "CANNOT_PROVIDE_EXTERNAL"},
169 {12, "ADDRESS_MISMATCH"}, {13, "EXCESSIVE_REMOTE_PEER"},
170};
171
173std::string NATPMPResultString(uint16_t result_code) {
174 auto result_i = NATPMP_RESULT_STR.find(result_code);
175 return strprintf("%s (code %d)",
176 result_i == NATPMP_RESULT_STR.end() ? "(unknown)"
177 : result_i->second,
178 result_code);
179}
180
182std::string PCPResultString(uint8_t result_code) {
183 auto result_i = PCP_RESULT_STR.find(result_code);
184 return strprintf("%s (code %d)",
185 result_i == PCP_RESULT_STR.end() ? "(unknown)"
186 : result_i->second,
187 result_code);
188}
189
192[[nodiscard]] bool PCPWrapAddress(Span<uint8_t> wrapped_addr,
193 const CNetAddr &addr) {
194 Assume(wrapped_addr.size() == ADDR_IPV6_SIZE);
195 if (addr.IsIPv4()) {
196 struct in_addr addr4;
197 if (!addr.GetInAddr(&addr4)) {
198 return false;
199 }
200 // Section 5: "When the address field holds an IPv4 address, an
201 // IPv4-mapped IPv6 address [RFC4291] is used (::ffff:0:0/96)."
202 std::memcpy(wrapped_addr.data(), IPV4_IN_IPV6_PREFIX.data(),
203 IPV4_IN_IPV6_PREFIX.size());
204 std::memcpy(wrapped_addr.data() + IPV4_IN_IPV6_PREFIX.size(), &addr4,
206 return true;
207 }
208 if (addr.IsIPv6()) {
209 struct in6_addr addr6;
210 if (!addr.GetIn6Addr(&addr6)) {
211 return false;
212 }
213 std::memcpy(wrapped_addr.data(), &addr6, ADDR_IPV6_SIZE);
214 return true;
215 }
216 return false;
217}
218
220CNetAddr PCPUnwrapAddress(Span<const uint8_t> wrapped_addr) {
221 Assume(wrapped_addr.size() == ADDR_IPV6_SIZE);
222 if (util::HasPrefix(wrapped_addr, IPV4_IN_IPV6_PREFIX)) {
223 struct in_addr addr4;
224 std::memcpy(&addr4, wrapped_addr.data() + IPV4_IN_IPV6_PREFIX.size(),
226 return CNetAddr(addr4);
227 }
228 struct in6_addr addr6;
229 std::memcpy(&addr6, wrapped_addr.data(), ADDR_IPV6_SIZE);
230 return CNetAddr(addr6);
231}
232
234std::optional<std::vector<uint8_t>>
235PCPSendRecv(Sock &sock, const std::string &protocol,
236 Span<const uint8_t> request, int num_tries,
237 std::chrono::milliseconds timeout_per_try,
238 std::function<bool(Span<const uint8_t>)> check_packet,
239
240 CThreadInterrupt &interrupt) {
241 using namespace std::chrono;
242 // UDP is a potentially lossy protocol, so we try to send again a few times.
243 uint8_t response[PCP_MAX_SIZE];
244 bool got_response = false;
245 int recvsz = 0;
246 for (int ntry = 0; !got_response && ntry < num_tries; ++ntry) {
247 if (ntry > 0) {
249 "%s: Retrying (%d)\n", protocol, ntry);
250 }
251 // Dispatch packet to gateway.
252 if (sock.Send(request.data(), request.size(), 0) !=
253 static_cast<ssize_t>(request.size())) {
255 "%s: Could not send request: %s\n", protocol,
257 // Network-level error, probably no use retrying.
258 return std::nullopt;
259 }
260
261 // Wait for response(s) until we get a valid response, a network error,
262 // or time out.
263 auto cur_time =
264 time_point_cast<milliseconds>(MockableSteadyClock::now());
265 auto deadline = cur_time + timeout_per_try;
266 while ((cur_time = time_point_cast<milliseconds>(
267 MockableSteadyClock::now())) < deadline) {
268 if (interrupt) {
269 return std::nullopt;
270 }
271 Sock::Event occurred = 0;
272 if (!sock.Wait(deadline - cur_time, Sock::RECV, &occurred)) {
274 "%s: Could not wait on socket: %s\n", protocol,
276 // Network-level error, probably no use retrying.
277 return std::nullopt;
278 }
279 if (!occurred) {
281 protocol);
282 // Retry.
283 break;
284 }
285
286 // Receive response.
287 recvsz = sock.Recv(response, sizeof(response), MSG_DONTWAIT);
288 if (recvsz < 0) {
290 "%s: Could not receive response: %s\n", protocol,
292 // Network-level error, probably no use retrying.
293 return std::nullopt;
294 }
296 "%s: Received response of %d bytes: %s\n", protocol,
297 recvsz, HexStr(Span(response, recvsz)));
298
299 if (check_packet(Span<uint8_t>(response, recvsz))) {
300 // Got expected response, break from receive loop as well as
301 // from retry loop.
302 got_response = true;
303 break;
304 }
305 }
306 }
307 if (!got_response) {
309 "%s: Giving up after %d tries\n", protocol, num_tries);
310 return std::nullopt;
311 }
312 return std::vector<uint8_t>(response, response + recvsz);
313}
314
315} // namespace
316
317std::variant<MappingResult, MappingError>
318NATPMPRequestPortMap(const CNetAddr &gateway, uint16_t port, uint32_t lifetime,
319 CThreadInterrupt &interrupt, int num_tries,
320 std::chrono::milliseconds timeout_per_try) {
321 struct sockaddr_storage dest_addr;
322 socklen_t dest_addrlen = sizeof(struct sockaddr_storage);
323
325 "natpmp: Requesting port mapping port %d from gateway %s\n",
326 port, gateway.ToStringAddr());
327
328 // Validate gateway, make sure it's IPv4. NAT-PMP does not support IPv6.
329 if (!CService(gateway, PCP_SERVER_PORT)
330 .GetSockAddr((struct sockaddr *)&dest_addr, &dest_addrlen)) {
332 }
333 if (dest_addr.ss_family != AF_INET) {
335 }
336
337 // Create IPv4 UDP socket
338 auto sock{CreateSock(AF_INET, SOCK_DGRAM, IPPROTO_UDP)};
339 if (!sock) {
341 "natpmp: Could not create UDP socket: %s\n",
344 }
345
346 // Associate UDP socket to gateway.
347 if (sock->Connect((struct sockaddr *)&dest_addr, dest_addrlen) != 0) {
349 "natpmp: Could not connect to gateway: %s\n",
352 }
353
354 // Use getsockname to get the address toward the default gateway (the
355 // internal address).
356 struct sockaddr_in internal;
357 socklen_t internal_addrlen = sizeof(struct sockaddr_in);
358 if (sock->GetSockName((struct sockaddr *)&internal, &internal_addrlen) !=
359 0) {
361 "natpmp: Could not get sock name: %s\n",
364 }
365
366 // Request external IP address (RFC6886 section 3.2).
367 std::vector<uint8_t> request(NATPMP_GETEXTERNAL_REQUEST_SIZE);
368 request[NATPMP_HDR_VERSION_OFS] = NATPMP_VERSION;
369 request[NATPMP_HDR_OP_OFS] = NATPMP_REQUEST | NATPMP_OP_GETEXTERNAL;
370
371 auto recv_res = PCPSendRecv(
372 *sock, "natpmp", request, num_tries, timeout_per_try,
373 [&](const Span<const uint8_t> response) -> bool {
374 if (response.size() < NATPMP_GETEXTERNAL_RESPONSE_SIZE) {
375 LogPrintLevel(BCLog::NET, BCLog::Level::Warning,
376 "natpmp: Response too small\n");
377 // Wasn't response to what we expected, try receiving next
378 // packet.
379 return false;
380 }
381 if (response[NATPMP_HDR_VERSION_OFS] != NATPMP_VERSION ||
382 response[NATPMP_HDR_OP_OFS] !=
383 (NATPMP_RESPONSE | NATPMP_OP_GETEXTERNAL)) {
385 "natpmp: Response to wrong command\n");
386 // Wasn't response to what we expected, try receiving next
387 // packet.
388 return false;
389 }
390 return true;
391 },
392 interrupt);
393
394 struct in_addr external_addr;
395 if (recv_res) {
396 const std::span<const uint8_t> response = *recv_res;
397
398 Assume(response.size() >= NATPMP_GETEXTERNAL_RESPONSE_SIZE);
399 uint16_t result_code =
400 ReadBE16(response.data() + NATPMP_RESPONSE_HDR_RESULT_OFS);
401 if (result_code != NATPMP_RESULT_SUCCESS) {
404 "natpmp: Getting external address failed with result %s\n",
405 NATPMPResultString(result_code));
407 }
408
409 std::memcpy(&external_addr,
410 response.data() + NATPMP_GETEXTERNAL_RESPONSE_IP_OFS,
412 } else {
414 }
415
416 // Create TCP mapping request (RFC6886 section 3.3).
417 request = std::vector<uint8_t>(NATPMP_MAP_REQUEST_SIZE);
418 request[NATPMP_HDR_VERSION_OFS] = NATPMP_VERSION;
419 request[NATPMP_HDR_OP_OFS] = NATPMP_REQUEST | NATPMP_OP_MAP_TCP;
420 WriteBE16(request.data() + NATPMP_MAP_REQUEST_INTERNAL_PORT_OFS, port);
421 WriteBE16(request.data() + NATPMP_MAP_REQUEST_EXTERNAL_PORT_OFS, port);
422 WriteBE32(request.data() + NATPMP_MAP_REQUEST_LIFETIME_OFS, lifetime);
423
424 recv_res = PCPSendRecv(
425 *sock, "natpmp", request, num_tries, timeout_per_try,
426 [&](const Span<const uint8_t> response) -> bool {
427 if (response.size() < NATPMP_MAP_RESPONSE_SIZE) {
428 LogPrintLevel(BCLog::NET, BCLog::Level::Warning,
429 "natpmp: Response too small\n");
430 // Wasn't response to what we expected, try receiving next
431 // packet.
432 return false;
433 }
434 if (response[0] != NATPMP_VERSION ||
435 response[1] != (NATPMP_RESPONSE | NATPMP_OP_MAP_TCP)) {
437 "natpmp: Response to wrong command\n");
438 // Wasn't response to what we expected, try receiving next
439 // packet.
440 return false;
441 }
442 uint16_t internal_port = ReadBE16(
443 response.data() + NATPMP_MAP_RESPONSE_INTERNAL_PORT_OFS);
444 if (internal_port != port) {
446 "natpmp: Response port doesn't match request\n");
447 // Wasn't response to what we expected, try receiving next
448 // packet.
449 return false;
450 }
451 return true;
452 },
453 interrupt);
454
455 if (recv_res) {
456 const std::span<uint8_t> response = *recv_res;
457
458 Assume(response.size() >= NATPMP_MAP_RESPONSE_SIZE);
459 uint16_t result_code =
460 ReadBE16(response.data() + NATPMP_RESPONSE_HDR_RESULT_OFS);
461 if (result_code != NATPMP_RESULT_SUCCESS) {
462 if (result_code == NATPMP_RESULT_NOT_AUTHORIZED) {
463 static std::atomic<bool> warned{false};
464 if (!warned.exchange(true)) {
465 LogWarning("natpmp: Port mapping failed with result %s\n",
466 NATPMPResultString(result_code));
467 } else {
469 "natpmp: Port mapping failed with result %s\n",
470 NATPMPResultString(result_code));
471 }
472 } else {
473 LogWarning("natpmp: Port mapping failed with result %s\n",
474 NATPMPResultString(result_code));
475 }
476 if (result_code == NATPMP_RESULT_NO_RESOURCES) {
478 }
480 }
481
482 uint32_t lifetime_ret =
483 ReadBE32(response.data() + NATPMP_MAP_RESPONSE_LIFETIME_OFS);
484 uint16_t external_port =
485 ReadBE16(response.data() + NATPMP_MAP_RESPONSE_EXTERNAL_PORT_OFS);
486 return MappingResult(NATPMP_VERSION, CService(internal.sin_addr, port),
487 CService(external_addr, external_port),
488 lifetime_ret);
489 }
491}
492
493std::variant<MappingResult, MappingError>
494PCPRequestPortMap(const PCPMappingNonce &nonce, const CNetAddr &gateway,
495 const CNetAddr &bind, uint16_t port, uint32_t lifetime,
496 CThreadInterrupt &interrupt, int num_tries,
497 std::chrono::milliseconds timeout_per_try) {
498 struct sockaddr_storage dest_addr, bind_addr;
499 socklen_t dest_addrlen = sizeof(struct sockaddr_storage),
500 bind_addrlen = sizeof(struct sockaddr_storage);
501
504 "pcp: Requesting port mapping for addr %s port %d from gateway %s\n",
505 bind.ToStringAddr(), port, gateway.ToStringAddr());
506
507 // Validate addresses, make sure they're the same network family.
508 if (!CService(gateway, PCP_SERVER_PORT)
509 .GetSockAddr((struct sockaddr *)&dest_addr, &dest_addrlen)) {
511 }
512 if (!CService(bind, 0).GetSockAddr((struct sockaddr *)&bind_addr,
513 &bind_addrlen)) {
515 }
516 if (dest_addr.ss_family != bind_addr.ss_family) {
518 }
519
520 // Create UDP socket (IPv4 or IPv6 based on provided gateway).
521 auto sock{CreateSock(dest_addr.ss_family, SOCK_DGRAM, IPPROTO_UDP)};
522 if (!sock) {
524 "pcp: Could not create UDP socket: %s\n",
527 }
528
529 // Make sure that we send from requested destination address, anything else
530 // will be rejected by a security-conscious router.
531 if (sock->Bind((struct sockaddr *)&bind_addr, bind_addrlen) != 0) {
533 "pcp: Could not bind to address: %s\n",
536 }
537
538 // Associate UDP socket to gateway.
539 if (sock->Connect((struct sockaddr *)&dest_addr, dest_addrlen) != 0) {
541 "pcp: Could not connect to gateway: %s\n",
544 }
545
546 // Use getsockname to get the address toward the default gateway (the
547 // internal address), in case we don't know what address to map (this is
548 // only needed if bind is INADDR_ANY, but it doesn't hurt as an extra
549 // check).
550 struct sockaddr_storage internal_addr;
551 socklen_t internal_addrlen = sizeof(struct sockaddr_storage);
552 if (sock->GetSockName((struct sockaddr *)&internal_addr,
553 &internal_addrlen) != 0) {
555 "pcp: Could not get sock name: %s\n",
558 }
559 CService internal;
560 if (!internal.SetSockAddr((struct sockaddr *)&internal_addr,
561 internal_addrlen)) {
563 }
565 "pcp: Internal address after connect: %s\n",
566 internal.ToStringAddr());
567
568 // Build request packet. Make sure the packet is zeroed so that reserved
569 // fields are zero as required by the spec (and not potentially leak data).
570 // Make sure there's space for the request header and MAP specific request
571 // data.
572 std::vector<uint8_t> request(PCP_HDR_SIZE + PCP_MAP_SIZE);
573 // Fill in request header, See RFC6887 Figure 2.
574 size_t ofs = 0;
575 request[ofs + PCP_HDR_VERSION_OFS] = PCP_VERSION;
576 request[ofs + PCP_HDR_OP_OFS] = PCP_REQUEST | PCP_OP_MAP;
577 WriteBE32(request.data() + ofs + PCP_HDR_LIFETIME_OFS, lifetime);
578 if (!PCPWrapAddress(
579 Span(request).subspan(ofs + PCP_REQUEST_HDR_IP_OFS, ADDR_IPV6_SIZE),
580 internal)) {
582 }
583
584 ofs += PCP_HDR_SIZE;
585
586 // Fill in MAP request packet, See RFC6887 Figure 9.
587 // Randomize mapping nonce (this is repeated in the response, to be able to
588 // correlate requests and responses, and used to authenticate changes to the
589 // mapping).
590 std::memcpy(request.data() + ofs + PCP_MAP_NONCE_OFS, nonce.data(),
592 request[ofs + PCP_MAP_PROTOCOL_OFS] = PCP_PROTOCOL_TCP;
593 WriteBE16(request.data() + ofs + PCP_MAP_INTERNAL_PORT_OFS, port);
594 WriteBE16(request.data() + ofs + PCP_MAP_EXTERNAL_PORT_OFS, port);
595 if (!PCPWrapAddress(Span(request).subspan(ofs + PCP_MAP_EXTERNAL_IP_OFS,
597 bind)) {
599 }
600
601 ofs += PCP_MAP_SIZE;
602 Assume(ofs == request.size());
603
604 // Receive loop.
605 bool is_natpmp = false;
606 auto recv_res = PCPSendRecv(
607 *sock, "pcp", request, num_tries, timeout_per_try,
608 [&](const Span<const uint8_t> response) -> bool {
609 // Unsupported version according to RFC6887 appendix A and RFC6886
610 // section 3.5, can fall back to NAT-PMP.
611 if (response.size() == NATPMP_RESPONSE_HDR_SIZE &&
612 response[PCP_HDR_VERSION_OFS] == NATPMP_VERSION &&
613 response[PCP_RESPONSE_HDR_RESULT_OFS] ==
614 NATPMP_RESULT_UNSUPP_VERSION) {
615 is_natpmp = true;
616 // Let it through to caller.
617 return true;
618 }
619 if (response.size() < (PCP_HDR_SIZE + PCP_MAP_SIZE)) {
620 LogPrintLevel(BCLog::NET, BCLog::Level::Warning,
621 "pcp: Response too small\n");
622 // Wasn't response to what we expected, try receiving next
623 // packet.
624 return false;
625 }
626 if (response[PCP_HDR_VERSION_OFS] != PCP_VERSION ||
627 response[PCP_HDR_OP_OFS] != (PCP_RESPONSE | PCP_OP_MAP)) {
629 "pcp: Response to wrong command\n");
630 // Wasn't response to what we expected, try receiving next
631 // packet.
632 return false;
633 }
634 // Handle MAP opcode response. See RFC6887 Figure 10.
635 // Check that returned mapping nonce matches our request.
636 if (!std::ranges::equal(
637 response.subspan(PCP_HDR_SIZE + PCP_MAP_NONCE_OFS,
639 nonce)) {
641 "pcp: Mapping nonce mismatch\n");
642 // Wasn't response to what we expected, try receiving next
643 // packet.
644 return false;
645 }
646 uint8_t protocol = response[PCP_HDR_SIZE + 12];
647 uint16_t internal_port =
648 ReadBE16(response.data() + PCP_HDR_SIZE + 16);
649 if (protocol != PCP_PROTOCOL_TCP || internal_port != port) {
652 "pcp: Response protocol or port doesn't match request\n");
653 // Wasn't response to what we expected, try receiving next
654 // packet.
655 return false;
656 }
657 return true;
658 },
659 interrupt);
660
661 if (!recv_res) {
663 }
664 if (is_natpmp) {
666 }
667
668 const std::span<const uint8_t> response = *recv_res;
669 // If we get here, we got a valid MAP response to our request.
670 // Check to see if we got the result we expected.
671 Assume(response.size() >= (PCP_HDR_SIZE + PCP_MAP_SIZE));
672 uint8_t result_code = response[PCP_RESPONSE_HDR_RESULT_OFS];
673 uint32_t lifetime_ret = ReadBE32(response.data() + PCP_HDR_LIFETIME_OFS);
674 uint16_t external_port =
675 ReadBE16(response.data() + PCP_HDR_SIZE + PCP_MAP_EXTERNAL_PORT_OFS);
676 CNetAddr external_addr{PCPUnwrapAddress(response.subspan(
677 PCP_HDR_SIZE + PCP_MAP_EXTERNAL_IP_OFS, ADDR_IPV6_SIZE))};
678 if (result_code != PCP_RESULT_SUCCESS) {
679 if (result_code == PCP_RESULT_NOT_AUTHORIZED) {
680 static std::atomic<bool> warned{false};
681 if (!warned.exchange(true)) {
682 LogWarning("pcp: Mapping failed with result %s\n",
683 PCPResultString(result_code));
684 } else {
685 LogDebug(BCLog::NET, "pcp: Mapping failed with result %s\n",
686 PCPResultString(result_code));
687 }
688 } else {
689 LogWarning("pcp: Mapping failed with result %s\n",
690 PCPResultString(result_code));
691 }
692 if (result_code == PCP_RESULT_NO_RESOURCES) {
694 }
696 }
697
698 return MappingResult(PCP_VERSION, CService(internal, port),
699 CService(external_addr, external_port), lifetime_ret);
700}
701
702std::string MappingResult::ToString() const {
703 Assume(version == NATPMP_VERSION || version == PCP_VERSION);
704 return strprintf(
705 "%s:%s -> %s (for %ds)", version == NATPMP_VERSION ? "natpmp" : "pcp",
707}
#define Assume(val)
Assume is the identity function.
Definition: check.h:97
Network address.
Definition: netaddress.h:114
std::string ToStringAddr() const
Definition: netaddress.cpp:630
bool GetIn6Addr(struct in6_addr *pipv6Addr) const
Try to get our IPv6 address.
Definition: netaddress.cpp:712
bool GetInAddr(struct in_addr *pipv4Addr) const
Try to get our IPv4 address.
Definition: netaddress.cpp:693
bool IsIPv4() const
Definition: netaddress.cpp:340
bool IsIPv6() const
Definition: netaddress.cpp:344
A combination of a network address (CNetAddr) and a (TCP) port.
Definition: netaddress.h:573
bool SetSockAddr(const struct sockaddr *paddr, socklen_t addrlen)
Set CService from a network sockaddr.
Definition: netaddress.cpp:993
std::string ToStringAddrPort() const
A helper class for interruptible sleeps.
RAII helper class that manages a socket and closes it automatically when it goes out of scope.
Definition: sock.h:27
virtual ssize_t Send(const void *data, size_t len, int flags) const
send(2) wrapper.
Definition: sock.cpp:49
virtual int Bind(const sockaddr *addr, socklen_t addr_len) const
bind(2) wrapper.
Definition: sock.cpp:61
virtual bool Wait(std::chrono::milliseconds timeout, Event requested, Event *occurred=nullptr) const
Wait for readiness for input (recv) or output (send).
Definition: sock.cpp:136
uint8_t Event
Definition: sock.h:153
virtual int GetSockName(sockaddr *name, socklen_t *name_len) const
getsockname(2) wrapper.
Definition: sock.cpp:106
static constexpr Event RECV
If passed to Wait(), then it will wait for readiness to read from the socket.
Definition: sock.h:159
virtual int Connect(const sockaddr *addr, socklen_t addr_len) const
connect(2) wrapper.
Definition: sock.cpp:57
virtual ssize_t Recv(void *buf, size_t len, int flags) const
recv(2) wrapper.
Definition: sock.cpp:53
A Span is an object that can refer to a contiguous sequence of objects.
Definition: span.h:94
constexpr std::size_t size() const noexcept
Definition: span.h:210
constexpr C * data() const noexcept
Definition: span.h:199
#define WSAGetLastError()
Definition: compat.h:57
#define MSG_DONTWAIT
Definition: compat.h:128
static void WriteBE32(uint8_t *ptr, uint32_t x)
Definition: common.h:69
static uint16_t ReadBE16(const uint8_t *ptr)
Definition: common.h:46
static uint32_t ReadBE32(const uint8_t *ptr)
Definition: common.h:52
static void WriteBE16(uint8_t *ptr, uint16_t x)
Definition: common.h:64
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
Definition: hex_base.cpp:30
#define LogPrintLevel(category, level,...)
Definition: logging.h:437
#define LogWarning(...)
Definition: logging.h:416
#define LogDebug(category,...)
Definition: logging.h:446
@ NET
Definition: logging.h:69
bool HasPrefix(const T1 &obj, const std::array< uint8_t, PREFIX_LEN > &prefix)
Check whether a container begins with the given prefix.
Definition: string.h:162
static constexpr size_t ADDR_IPV4_SIZE
Size of IPv4 address (in bytes).
Definition: netaddress.h:87
static const std::array< uint8_t, 12 > IPV4_IN_IPV6_PREFIX
Prefix of an IPv6 address when it contains an embedded IPv4 address.
Definition: netaddress.h:67
static constexpr size_t ADDR_IPV6_SIZE
Size of IPv6 address (in bytes).
Definition: netaddress.h:90
std::function< std::unique_ptr< Sock >(int, int, int)> CreateSock
Socket factory.
Definition: netbase.cpp:660
std::variant< MappingResult, MappingError > NATPMPRequestPortMap(const CNetAddr &gateway, uint16_t port, uint32_t lifetime, CThreadInterrupt &interrupt, int num_tries, std::chrono::milliseconds timeout_per_try)
Try to open a port using RFC 6886 NAT-PMP.
Definition: pcp.cpp:318
std::variant< MappingResult, MappingError > PCPRequestPortMap(const PCPMappingNonce &nonce, const CNetAddr &gateway, const CNetAddr &bind, uint16_t port, uint32_t lifetime, CThreadInterrupt &interrupt, int num_tries, std::chrono::milliseconds timeout_per_try)
Try to open a port using RFC 6887 Port Control Protocol (PCP).
Definition: pcp.cpp:494
std::array< uint8_t, PCP_MAP_NONCE_SIZE > PCPMappingNonce
PCP mapping nonce.
Definition: pcp.h:24
constexpr size_t PCP_MAP_NONCE_SIZE
Mapping nonce size in bytes (see RFC6887 section 11.1).
Definition: pcp.h:20
@ PROTOCOL_ERROR
Any kind of protocol-level error, except unsupported version or no resources.
@ NO_RESOURCES
No resources available (port probably already mapped).
@ UNSUPP_VERSION
Unsupported protocol version.
@ NETWORK_ERROR
Any kind of network-level error.
Response response
Definition: processor.cpp:536
std::string NetworkErrorString(int err)
Return readable error string for a network error code.
Definition: sock.cpp:438
Span(T *, EndOrSize) -> Span< T >
Successful response to a port mapping.
Definition: pcp.h:40
CService external
External host:port.
Definition: pcp.h:50
uint32_t lifetime
Granted lifetime of binding (seconds).
Definition: pcp.h:52
CService internal
Internal host:port.
Definition: pcp.h:48
std::string ToString() const
Format mapping as string for logging.
Definition: pcp.cpp:702
uint8_t version
Protocol version, one of NATPMP_VERSION or PCP_VERSION.
Definition: pcp.h:46
static time_point now() noexcept
Return current system time or mocked time, if set.
Definition: time.cpp:47
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1202