Changeset View
Changeset View
Standalone View
Standalone View
src/common/pcp.cpp
| // Copyright (c) 2024 The Bitcoin Core developers | // Copyright (c) 2024 The Bitcoin Core developers | ||||
| // Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
| // file COPYING or https://www.opensource.org/licenses/mit-license.php. | // file COPYING or https://www.opensource.org/licenses/mit-license.php. | ||||
| #include <common/pcp.h> | #include <common/pcp.h> | ||||
| #include <atomic> | |||||
| #include <common/netif.h> | #include <common/netif.h> | ||||
| #include <crypto/common.h> | #include <crypto/common.h> | ||||
| #include <logging.h> | #include <logging.h> | ||||
| #include <netaddress.h> | #include <netaddress.h> | ||||
| #include <netbase.h> | #include <netbase.h> | ||||
| #include <random.h> | #include <random.h> | ||||
| #include <span.h> | #include <span.h> | ||||
| #include <util/check.h> | #include <util/check.h> | ||||
| ▲ Show 20 Lines • Show All 63 Lines • ▼ Show 20 Lines | |||||
| //! Created port mapping lifetime in seconds. | //! Created port mapping lifetime in seconds. | ||||
| constexpr size_t NATPMP_MAP_RESPONSE_LIFETIME_OFS = 12; | constexpr size_t NATPMP_MAP_RESPONSE_LIFETIME_OFS = 12; | ||||
| // Relevant NETPMP result codes (RFC6886 3.5). | // Relevant NETPMP result codes (RFC6886 3.5). | ||||
| //! Result code representing success status. | //! Result code representing success status. | ||||
| constexpr uint8_t NATPMP_RESULT_SUCCESS = 0; | constexpr uint8_t NATPMP_RESULT_SUCCESS = 0; | ||||
| //! Result code representing unsupported version. | //! Result code representing unsupported version. | ||||
| constexpr uint8_t NATPMP_RESULT_UNSUPP_VERSION = 1; | constexpr uint8_t NATPMP_RESULT_UNSUPP_VERSION = 1; | ||||
| //! Result code representing not authorized (router doesn't support port | |||||
| //! mapping). | |||||
| constexpr uint8_t NATPMP_RESULT_NOT_AUTHORIZED = 2; | |||||
| //! Result code representing lack of resources. | //! Result code representing lack of resources. | ||||
| constexpr uint8_t NATPMP_RESULT_NO_RESOURCES = 4; | constexpr uint8_t NATPMP_RESULT_NO_RESOURCES = 4; | ||||
| //! Mapping of NATPMP result code to string (RFC6886 3.5). Result codes <=2 | //! Mapping of NATPMP result code to string (RFC6886 3.5). Result codes <=2 | ||||
| //! match PCP. | //! match PCP. | ||||
| const std::map<uint16_t, std::string> NATPMP_RESULT_STR{ | const std::map<uint16_t, std::string> NATPMP_RESULT_STR{ | ||||
| {0, "SUCCESS"}, {1, "UNSUPP_VERSION"}, {2, "NOT_AUTHORIZED"}, | {0, "SUCCESS"}, {1, "UNSUPP_VERSION"}, {2, "NOT_AUTHORIZED"}, | ||||
| {3, "NETWORK_FAILURE"}, {4, "NO_RESOURCES"}, {5, "UNSUPP_OPCODE"}, | {3, "NETWORK_FAILURE"}, {4, "NO_RESOURCES"}, {5, "UNSUPP_OPCODE"}, | ||||
| ▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | |||||
| //! bytes). | //! bytes). | ||||
| constexpr size_t PCP_MAP_EXTERNAL_PORT_OFS = 18; | constexpr size_t PCP_MAP_EXTERNAL_PORT_OFS = 18; | ||||
| //! Suggested external IP (request), assigned external IP (response) (16 | //! Suggested external IP (request), assigned external IP (response) (16 | ||||
| //! bytes). | //! bytes). | ||||
| constexpr size_t PCP_MAP_EXTERNAL_IP_OFS = 20; | constexpr size_t PCP_MAP_EXTERNAL_IP_OFS = 20; | ||||
| //! Result code representing success (RFC6887 7.4), shared with NAT-PMP. | //! Result code representing success (RFC6887 7.4), shared with NAT-PMP. | ||||
| constexpr uint8_t PCP_RESULT_SUCCESS = NATPMP_RESULT_SUCCESS; | constexpr uint8_t PCP_RESULT_SUCCESS = NATPMP_RESULT_SUCCESS; | ||||
| //! Result code representing not authorized (RFC6887 7.4), shared with NAT-PMP. | |||||
| constexpr uint8_t PCP_RESULT_NOT_AUTHORIZED = NATPMP_RESULT_NOT_AUTHORIZED; | |||||
| //! Result code representing lack of resources (RFC6887 7.4). | //! Result code representing lack of resources (RFC6887 7.4). | ||||
| constexpr uint8_t PCP_RESULT_NO_RESOURCES = 8; | constexpr uint8_t PCP_RESULT_NO_RESOURCES = 8; | ||||
| //! Mapping of PCP result code to string (RFC6887 7.4). Result codes <=2 match | //! Mapping of PCP result code to string (RFC6887 7.4). Result codes <=2 match | ||||
| //! NAT-PMP. | //! NAT-PMP. | ||||
| const std::map<uint8_t, std::string> PCP_RESULT_STR{ | const std::map<uint8_t, std::string> PCP_RESULT_STR{ | ||||
| {0, "SUCCESS"}, {1, "UNSUPP_VERSION"}, | {0, "SUCCESS"}, {1, "UNSUPP_VERSION"}, | ||||
| {2, "NOT_AUTHORIZED"}, {3, "MALFORMED_REQUEST"}, | {2, "NOT_AUTHORIZED"}, {3, "MALFORMED_REQUEST"}, | ||||
| ▲ Show 20 Lines • Show All 289 Lines • ▼ Show 20 Lines | NATPMPRequestPortMap(const CNetAddr &gateway, uint16_t port, uint32_t lifetime, | ||||
| if (recv_res) { | if (recv_res) { | ||||
| const std::span<uint8_t> response = *recv_res; | const std::span<uint8_t> response = *recv_res; | ||||
| Assume(response.size() >= NATPMP_MAP_RESPONSE_SIZE); | Assume(response.size() >= NATPMP_MAP_RESPONSE_SIZE); | ||||
| uint16_t result_code = | uint16_t result_code = | ||||
| ReadBE16(response.data() + NATPMP_RESPONSE_HDR_RESULT_OFS); | ReadBE16(response.data() + NATPMP_RESPONSE_HDR_RESULT_OFS); | ||||
| if (result_code != NATPMP_RESULT_SUCCESS) { | if (result_code != NATPMP_RESULT_SUCCESS) { | ||||
| LogPrintLevel(BCLog::NET, BCLog::Level::Warning, | if (result_code == NATPMP_RESULT_NOT_AUTHORIZED) { | ||||
| static std::atomic<bool> warned{false}; | |||||
| if (!warned.exchange(true)) { | |||||
| LogWarning("natpmp: Port mapping failed with result %s\n", | |||||
| NATPMPResultString(result_code)); | |||||
| } else { | |||||
| LogDebug(BCLog::NET, | |||||
| "natpmp: Port mapping failed with result %s\n", | "natpmp: Port mapping failed with result %s\n", | ||||
| NATPMPResultString(result_code)); | NATPMPResultString(result_code)); | ||||
| } | |||||
| } else { | |||||
| LogWarning("natpmp: Port mapping failed with result %s\n", | |||||
| NATPMPResultString(result_code)); | |||||
| } | |||||
| if (result_code == NATPMP_RESULT_NO_RESOURCES) { | if (result_code == NATPMP_RESULT_NO_RESOURCES) { | ||||
| return MappingError::NO_RESOURCES; | return MappingError::NO_RESOURCES; | ||||
| } | } | ||||
| return MappingError::PROTOCOL_ERROR; | return MappingError::PROTOCOL_ERROR; | ||||
| } | } | ||||
| uint32_t lifetime_ret = | uint32_t lifetime_ret = | ||||
| ReadBE32(response.data() + NATPMP_MAP_RESPONSE_LIFETIME_OFS); | ReadBE32(response.data() + NATPMP_MAP_RESPONSE_LIFETIME_OFS); | ||||
| ▲ Show 20 Lines • Show All 187 Lines • ▼ Show 20 Lines | PCPRequestPortMap(const PCPMappingNonce &nonce, const CNetAddr &gateway, | ||||
| Assume(response.size() >= (PCP_HDR_SIZE + PCP_MAP_SIZE)); | Assume(response.size() >= (PCP_HDR_SIZE + PCP_MAP_SIZE)); | ||||
| uint8_t result_code = response[PCP_RESPONSE_HDR_RESULT_OFS]; | uint8_t result_code = response[PCP_RESPONSE_HDR_RESULT_OFS]; | ||||
| uint32_t lifetime_ret = ReadBE32(response.data() + PCP_HDR_LIFETIME_OFS); | uint32_t lifetime_ret = ReadBE32(response.data() + PCP_HDR_LIFETIME_OFS); | ||||
| uint16_t external_port = | uint16_t external_port = | ||||
| ReadBE16(response.data() + PCP_HDR_SIZE + PCP_MAP_EXTERNAL_PORT_OFS); | ReadBE16(response.data() + PCP_HDR_SIZE + PCP_MAP_EXTERNAL_PORT_OFS); | ||||
| CNetAddr external_addr{PCPUnwrapAddress(response.subspan( | CNetAddr external_addr{PCPUnwrapAddress(response.subspan( | ||||
| PCP_HDR_SIZE + PCP_MAP_EXTERNAL_IP_OFS, ADDR_IPV6_SIZE))}; | PCP_HDR_SIZE + PCP_MAP_EXTERNAL_IP_OFS, ADDR_IPV6_SIZE))}; | ||||
| if (result_code != PCP_RESULT_SUCCESS) { | if (result_code != PCP_RESULT_SUCCESS) { | ||||
| LogPrintLevel(BCLog::NET, BCLog::Level::Warning, | if (result_code == PCP_RESULT_NOT_AUTHORIZED) { | ||||
| "pcp: Mapping failed with result %s\n", | static std::atomic<bool> warned{false}; | ||||
| if (!warned.exchange(true)) { | |||||
| LogWarning("pcp: Mapping failed with result %s\n", | |||||
| PCPResultString(result_code)); | |||||
| } else { | |||||
| LogDebug(BCLog::NET, "pcp: Mapping failed with result %s\n", | |||||
| PCPResultString(result_code)); | PCPResultString(result_code)); | ||||
| } | |||||
| } else { | |||||
| LogWarning("pcp: Mapping failed with result %s\n", | |||||
| PCPResultString(result_code)); | |||||
| } | |||||
| if (result_code == PCP_RESULT_NO_RESOURCES) { | if (result_code == PCP_RESULT_NO_RESOURCES) { | ||||
| return MappingError::NO_RESOURCES; | return MappingError::NO_RESOURCES; | ||||
| } | } | ||||
| return MappingError::PROTOCOL_ERROR; | return MappingError::PROTOCOL_ERROR; | ||||
| } | } | ||||
| return MappingResult(PCP_VERSION, CService(internal, port), | return MappingResult(PCP_VERSION, CService(internal, port), | ||||
| CService(external_addr, external_port), lifetime_ret); | CService(external_addr, external_port), lifetime_ret); | ||||
| } | } | ||||
| std::string MappingResult::ToString() const { | std::string MappingResult::ToString() const { | ||||
| Assume(version == NATPMP_VERSION || version == PCP_VERSION); | Assume(version == NATPMP_VERSION || version == PCP_VERSION); | ||||
| return strprintf( | return strprintf( | ||||
| "%s:%s -> %s (for %ds)", version == NATPMP_VERSION ? "natpmp" : "pcp", | "%s:%s -> %s (for %ds)", version == NATPMP_VERSION ? "natpmp" : "pcp", | ||||
| external.ToStringAddrPort(), internal.ToStringAddrPort(), lifetime); | external.ToStringAddrPort(), internal.ToStringAddrPort(), lifetime); | ||||
| } | } | ||||