Skip to content

Commit 49da80f

Browse files
committed
ice: synchronize the upnp mappings with reflexive candidates
wait for the upnp to become active, meaning it is able to make port mappings, with a timeout UPNP_ACTIVE_TIMEOUT. GitLab: #34 Change-Id: Ia4cd76e581c12f7c6b454f2557d4f20874371d53
1 parent 0d21523 commit 49da80f

File tree

3 files changed

+61
-63
lines changed

3 files changed

+61
-63
lines changed

include/connectionmanager.h

-6
Original file line numberDiff line numberDiff line change
@@ -315,12 +315,6 @@ struct ConnectionManager::Config
315315
std::shared_ptr<dhtnet::upnp::Controller> upnpCtrl;
316316
std::shared_ptr<dht::log::Logger> logger;
317317

318-
/**
319-
* returns whether or not UPnP is enabled and active
320-
* ie: if it is able to make port mappings
321-
*/
322-
bool getUPnPActive() const;
323-
324318
/** Optional pseudo random generator to be used, allowing to control the seed. */
325319
std::unique_ptr<std::mt19937_64> rng;
326320
};

src/connectionmanager.cpp

+1-29
Original file line numberDiff line numberDiff line change
@@ -363,19 +363,6 @@ class DeviceInfoSet {
363363
std::map<DeviceId, std::shared_ptr<DeviceInfo>> infos_ {};
364364
};
365365

366-
367-
/**
368-
* returns whether or not UPnP is enabled and active_
369-
* ie: if it is able to make port mappings
370-
*/
371-
bool
372-
ConnectionManager::Config::getUPnPActive() const
373-
{
374-
if (upnpCtrl)
375-
return upnpCtrl->isReady();
376-
return false;
377-
}
378-
379366
class ConnectionManager::Impl : public std::enable_shared_from_this<ConnectionManager::Impl>
380367
{
381368
public:
@@ -569,12 +556,6 @@ class ConnectionManager::Impl : public std::enable_shared_from_this<ConnectionMa
569556
std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb);
570557
bool findCertificate(const dht::InfoHash& h, std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb);
571558

572-
/**
573-
* returns whether or not UPnP is enabled and active
574-
* ie: if it is able to make port mappings
575-
*/
576-
bool getUPnPActive() const;
577-
578559
std::shared_ptr<ConnectionManager::Config> config_;
579560
std::unique_ptr<std::thread> ioContextRunner_;
580561

@@ -1543,15 +1524,6 @@ ConnectionManager::Impl::isMessageTreated(dht::Value::Id id)
15431524
return !treatedMessages_.add(id);
15441525
}
15451526

1546-
/**
1547-
* returns whether or not UPnP is enabled and active_
1548-
* ie: if it is able to make port mappings
1549-
*/
1550-
bool
1551-
ConnectionManager::Impl::getUPnPActive() const
1552-
{
1553-
return config_->getUPnPActive();
1554-
}
15551527

15561528
IpAddr
15571529
ConnectionManager::Impl::getPublishedIpAddress(uint16_t family) const
@@ -1644,7 +1616,7 @@ ConnectionManager::Impl::getIceOptions() const noexcept
16441616
{
16451617
IceTransportOptions opts;
16461618
opts.factory = config_->factory;
1647-
opts.upnpEnable = getUPnPActive();
1619+
opts.upnpEnable = config_->upnpEnabled;
16481620
opts.upnpContext = config_->upnpCtrl ? config_->upnpCtrl->upnpContext() : nullptr;
16491621

16501622
if (config_->stunEnabled)

src/ice_transport.cpp

+60-28
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ static constexpr uint16_t IPV4_HEADER_SIZE = 20; ///< Size in bytes of IPV4 pack
6767
static constexpr int MAX_CANDIDATES {32};
6868
static constexpr int MAX_DESTRUCTION_TIMEOUT {3000};
6969
static constexpr int HANDLE_EVENT_DURATION {500};
70-
70+
static constexpr std::chrono::seconds PORT_MAPPING_TIMEOUT {4};
7171
//==============================================================================
7272

7373
using namespace upnp;
@@ -210,10 +210,12 @@ class IceTransport::Impl
210210
};
211211

212212
std::shared_ptr<upnp::Controller> upnp_ {};
213-
std::mutex upnpMutex_ {};
214213
std::map<Mapping::key_t, Mapping> upnpMappings_;
215214
std::mutex upnpMappingsMutex_ {};
216215

216+
std::mutex upnpMutex_ {};
217+
std::condition_variable upnpCv_;
218+
217219
bool onlyIPv4Private_ {true};
218220

219221
// IO/Timer events are handled by following thread
@@ -922,44 +924,74 @@ IceTransport::Impl::addStunConfig(int af)
922924
void
923925
IceTransport::Impl::requestUpnpMappings()
924926
{
925-
// Must be called once !
926-
927-
std::lock_guard lock(upnpMutex_);
928-
929927
if (not upnp_)
930928
return;
931-
932929
auto transport = isTcpEnabled() ? PJ_CAND_TCP_PASSIVE : PJ_CAND_UDP;
933930
auto portType = transport == PJ_CAND_UDP ? PortType::UDP : PortType::TCP;
934931

932+
// Use a different map instead of upnpMappings_ to store pointers to the mappings
933+
auto upnpMappings = std::make_shared<std::map<Mapping::key_t, Mapping::sharedPtr_t>>();
934+
auto isFailed = std::make_shared<bool>(false);
935+
936+
std::unique_lock lock(upnpMutex_);
937+
935938
// Request upnp mapping for each component.
936939
for (unsigned id = 1; id <= compCount_; id++) {
937940
// Set port number to 0 to get any available port.
938941
Mapping requestedMap(portType);
939942

940-
// Request the mapping
941-
Mapping::sharedPtr_t mapPtr = upnp_->reserveMapping(requestedMap);
942-
943-
// To use a mapping, it must be valid, open and has valid host address.
944-
if (mapPtr and mapPtr->getMapKey() and (mapPtr->getState() == MappingState::OPEN)
945-
and mapPtr->hasValidHostAddress()) {
946-
std::lock_guard lock(upnpMappingsMutex_);
947-
auto ret = upnpMappings_.emplace(mapPtr->getMapKey(), *mapPtr);
948-
if (ret.second) {
943+
requestedMap.setNotifyCallback([upnpMappings, isFailed, this](Mapping::sharedPtr_t mapPtr) {
944+
// Ignore intermidiate states : PENDING, IN_PROGRESS
945+
// only OPEN and FAILED are considered
946+
947+
// if the mapping is open check the validity
948+
if ((mapPtr->getState() == MappingState::OPEN)) {
949+
if (mapPtr->getMapKey() and mapPtr->hasValidHostAddress()){
950+
std::lock_guard lockMapping(upnpMappingsMutex_);
951+
upnpMappings->emplace(mapPtr->getMapKey(), mapPtr);
952+
} else {
953+
*isFailed = true;
954+
}
955+
} else if (mapPtr->getState() == MappingState::FAILED) {
956+
*isFailed = true;
949957
if (logger_)
950-
logger_->debug("[ice:{}] UPNP mapping {:s} successfully allocated",
951-
fmt::ptr(this),
952-
mapPtr->toString(true));
953-
} else {
954-
if (logger_)
955-
logger_->warn("[ice:{}] UPNP mapping {:s} already in the list!",
956-
fmt::ptr(this),
957-
mapPtr->toString());
958+
logger_->error("[ice:{}] UPNP mapping failed: {:s}",
959+
fmt::ptr(this),
960+
mapPtr->toString(true));
958961
}
959-
} else {
960-
if (logger_)
961-
logger_->warn("[ice:{}] UPNP mapping request failed!", fmt::ptr(this));
962-
upnp_->releaseMapping(requestedMap);
962+
upnpCv_.notify_all();
963+
});
964+
// Request the mapping
965+
upnp_->reserveMapping(requestedMap);
966+
}
967+
upnpCv_.wait_for(lock, PORT_MAPPING_TIMEOUT, [&] {
968+
return upnpMappings->size() == compCount_ or *isFailed;
969+
});
970+
971+
std::lock_guard lockMapping(upnpMappingsMutex_);
972+
973+
// remove the notify callback
974+
for (auto& map : *upnpMappings) {
975+
map.second->setNotifyCallback(nullptr);
976+
}
977+
// Check the number of mappings
978+
if (upnpMappings->size() != compCount_) {
979+
if (logger_)
980+
logger_->error("[ice:{}] UPNP mapping failed: expected {:d} mappings, got {:d}",
981+
fmt::ptr(this),
982+
compCount_,
983+
upnpMappings->size());
984+
// release all mappings
985+
for (auto& map : *upnpMappings) {
986+
upnp_->releaseMapping(*map.second);
987+
}
988+
} else {
989+
for (auto& map : *upnpMappings) {
990+
upnpMappings_.emplace(map.first, *map.second);
991+
if(logger_)
992+
logger_->debug("[ice:{}] UPNP mapping {:s} successfully allocated\n",
993+
fmt::ptr(this),
994+
map.second->toString(true));
963995
}
964996
}
965997
}

0 commit comments

Comments
 (0)