Changeset View
Changeset View
Standalone View
Standalone View
src/mapport.cpp
| Show All 19 Lines | |||||
| #include <cassert> | #include <cassert> | ||||
| #include <chrono> | #include <chrono> | ||||
| #include <functional> | #include <functional> | ||||
| #include <string> | #include <string> | ||||
| #include <thread> | #include <thread> | ||||
| static CThreadInterrupt g_mapport_interrupt; | static CThreadInterrupt g_mapport_interrupt; | ||||
| static std::thread g_mapport_thread; | static std::thread g_mapport_thread; | ||||
| static std::atomic_uint g_mapport_enabled_protos{MapPortProtoFlag::NONE}; | |||||
| static std::atomic<MapPortProtoFlag> g_mapport_current_proto{ | |||||
| MapPortProtoFlag::NONE}; | |||||
| using namespace std::chrono_literals; | using namespace std::chrono_literals; | ||||
| static constexpr auto PORT_MAPPING_REANNOUNCE_PERIOD{20min}; | static constexpr auto PORT_MAPPING_REANNOUNCE_PERIOD{20min}; | ||||
| static constexpr auto PORT_MAPPING_RETRY_PERIOD{5min}; | static constexpr auto PORT_MAPPING_RETRY_PERIOD{5min}; | ||||
| static bool ProcessPCP() { | static void ProcessPCP() { | ||||
| // The same nonce is used for all mappings, this is allowed by the spec, and | // The same nonce is used for all mappings, this is allowed by the spec, and | ||||
| // simplifies keeping track of them. | // simplifies keeping track of them. | ||||
| PCPMappingNonce pcp_nonce; | PCPMappingNonce pcp_nonce; | ||||
| GetRandBytes(pcp_nonce); | GetRandBytes(pcp_nonce); | ||||
| bool ret = false; | bool ret = false; | ||||
| bool no_resources = false; | bool no_resources = false; | ||||
| const uint16_t private_port = GetListenPort(); | const uint16_t private_port = GetListenPort(); | ||||
| ▲ Show 20 Lines • Show All 95 Lines • ▼ Show 20 Lines | do { | ||||
| } | } | ||||
| // Sanity-check returned lifetime. | // Sanity-check returned lifetime. | ||||
| if (actual_lifetime < 30) { | if (actual_lifetime < 30) { | ||||
| LogPrintLevel(BCLog::NET, BCLog::Level::Warning, | LogPrintLevel(BCLog::NET, BCLog::Level::Warning, | ||||
| "portmap: Got impossibly short mapping lifetime of " | "portmap: Got impossibly short mapping lifetime of " | ||||
| "%d seconds\n", | "%d seconds\n", | ||||
| actual_lifetime); | actual_lifetime); | ||||
| return false; | return; | ||||
| } | } | ||||
| // RFC6887 11.2.1 recommends that clients send their first renewal | // RFC6887 11.2.1 recommends that clients send their first renewal | ||||
| // packet at a time chosen with uniform random distribution in the range | // packet at a time chosen with uniform random distribution in the range | ||||
| // 1/2 to 5/8 of expiration time. | // 1/2 to 5/8 of expiration time. | ||||
| std::chrono::seconds sleep_time_min(actual_lifetime / 2); | std::chrono::seconds sleep_time_min(actual_lifetime / 2); | ||||
| std::chrono::seconds sleep_time_max(actual_lifetime * 5 / 8); | std::chrono::seconds sleep_time_max(actual_lifetime * 5 / 8); | ||||
| sleep_time = sleep_time_min + | sleep_time = sleep_time_min + | ||||
| FastRandomContext().randrange<std::chrono::milliseconds>( | FastRandomContext().randrange<std::chrono::milliseconds>( | ||||
| sleep_time_max - sleep_time_min); | sleep_time_max - sleep_time_min); | ||||
| } while (ret && g_mapport_interrupt.sleep_for(sleep_time)); | } while (ret && g_mapport_interrupt.sleep_for(sleep_time)); | ||||
| // We don't delete the mappings when the thread is interrupted because this | // We don't delete the mappings when the thread is interrupted because this | ||||
| // would add additional complexity, so we rather just choose a fairly short | // would add additional complexity, so we rather just choose a fairly short | ||||
| // expiry time. | // expiry time. | ||||
| return ret; | |||||
| } | } | ||||
| static void ThreadMapPort() { | static void ThreadMapPort() { | ||||
| bool ok; | |||||
| do { | do { | ||||
| ok = false; | ProcessPCP(); | ||||
| } while (g_mapport_interrupt.sleep_for(PORT_MAPPING_RETRY_PERIOD)); | |||||
| // High priority protocol. | |||||
| if (g_mapport_enabled_protos & MapPortProtoFlag::PCP) { | |||||
| g_mapport_current_proto = MapPortProtoFlag::PCP; | |||||
| ok = ProcessPCP(); | |||||
| if (ok) { | |||||
| continue; | |||||
| } | |||||
| } | |||||
| g_mapport_current_proto = MapPortProtoFlag::NONE; | |||||
| if (g_mapport_enabled_protos == MapPortProtoFlag::NONE) { | |||||
| return; | |||||
| } | |||||
| } while (ok || g_mapport_interrupt.sleep_for(PORT_MAPPING_RETRY_PERIOD)); | |||||
| } | } | ||||
| void StartThreadMapPort() { | void StartThreadMapPort() { | ||||
| if (!g_mapport_thread.joinable()) { | if (!g_mapport_thread.joinable()) { | ||||
| assert(!g_mapport_interrupt); | assert(!g_mapport_interrupt); | ||||
| g_mapport_thread = | g_mapport_thread = | ||||
| std::thread(&util::TraceThread, "mapport", &ThreadMapPort); | std::thread(&util::TraceThread, "mapport", &ThreadMapPort); | ||||
| } | } | ||||
| } | } | ||||
| static void DispatchMapPort() { | void StartMapPort(bool enable) { | ||||
| if (g_mapport_current_proto == MapPortProtoFlag::NONE && | if (enable) { | ||||
| g_mapport_enabled_protos == MapPortProtoFlag::NONE) { | |||||
| return; | |||||
| } | |||||
| if (g_mapport_current_proto == MapPortProtoFlag::NONE && | |||||
| g_mapport_enabled_protos != MapPortProtoFlag::NONE) { | |||||
| StartThreadMapPort(); | StartThreadMapPort(); | ||||
| return; | } else { | ||||
| } | |||||
| if (g_mapport_current_proto != MapPortProtoFlag::NONE && | |||||
| g_mapport_enabled_protos == MapPortProtoFlag::NONE) { | |||||
| InterruptMapPort(); | InterruptMapPort(); | ||||
| StopMapPort(); | StopMapPort(); | ||||
| return; | |||||
| } | |||||
| if (g_mapport_enabled_protos & g_mapport_current_proto) { | |||||
| // Enabling another protocol does not cause switching from the currently | |||||
| // used one. | |||||
| return; | |||||
| } | } | ||||
| assert(g_mapport_thread.joinable()); | |||||
| assert(!g_mapport_interrupt); | |||||
| // Interrupt a protocol-specific loop in the ThreadPCP() to force | |||||
| // trying the next protocol in the ThreadMapPort() loop. | |||||
| g_mapport_interrupt(); | |||||
| } | |||||
| static void MapPortProtoSetEnabled(MapPortProtoFlag proto, bool enabled) { | |||||
| if (enabled) { | |||||
| g_mapport_enabled_protos |= proto; | |||||
| } else { | |||||
| g_mapport_enabled_protos &= ~proto; | |||||
| } | |||||
| } | |||||
| void StartMapPort(bool use_pcp) { | |||||
| MapPortProtoSetEnabled(MapPortProtoFlag::PCP, use_pcp); | |||||
| DispatchMapPort(); | |||||
| } | } | ||||
| void InterruptMapPort() { | void InterruptMapPort() { | ||||
| g_mapport_enabled_protos = MapPortProtoFlag::NONE; | |||||
| if (g_mapport_thread.joinable()) { | if (g_mapport_thread.joinable()) { | ||||
| g_mapport_interrupt(); | g_mapport_interrupt(); | ||||
| } | } | ||||
| } | } | ||||
| void StopMapPort() { | void StopMapPort() { | ||||
| if (g_mapport_thread.joinable()) { | if (g_mapport_thread.joinable()) { | ||||
| g_mapport_thread.join(); | g_mapport_thread.join(); | ||||
| g_mapport_interrupt.reset(); | g_mapport_interrupt.reset(); | ||||
| } | } | ||||
| } | } | ||||