blob: b62b2f59a6db5744bda09b5b044f4ed41246e0a9 [file] [log] [blame]
/*
* Copyright (C) 2004-2023 Savoir-faire Linux Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "connectivity/upnp/protocol/upnp_protocol.h"
#include "connectivity/upnp/protocol/igd.h"
#include "pmp_igd.h"
#include "logger.h"
#include "connectivity/ip_utils.h"
#include "noncopyable.h"
#include "compiler_intrinsics.h"
// uncomment to enable native natpmp error messages
//#define ENABLE_STRNATPMPERR 1
#include <natpmp.h>
#include <atomic>
#include <thread>
namespace dhtnet {
class IpAddr;
}
namespace dhtnet {
namespace upnp {
// Requested lifetime in seconds. The actual lifetime might be different.
constexpr static unsigned int MAPPING_ALLOCATION_LIFETIME {60 * 60};
// Max number of IGD search attempts before failure.
constexpr static unsigned int MAX_RESTART_SEARCH_RETRIES {3};
// Time-out between two successive read response.
constexpr static auto TIMEOUT_BEFORE_READ_RETRY {std::chrono::milliseconds(300)};
// Max number of read attempts before failure.
constexpr static unsigned int MAX_READ_RETRIES {3};
// Base unit for the timeout between two successive IGD search.
constexpr static auto NATPMP_SEARCH_RETRY_UNIT {std::chrono::seconds(10)};
class NatPmp : public UPnPProtocol
{
public:
NatPmp();
~NatPmp();
// Set the observer.
void setObserver(UpnpMappingObserver* obs) override;
// Returns the protocol type.
NatProtocolType getProtocol() const override { return NatProtocolType::NAT_PMP; }
// Get protocol type as string.
char const* getProtocolName() const override { return "NAT-PMP"; }
// Notifies a change in network.
void clearIgds() override;
// Renew pmp_igd.
void searchForIgd() override;
// Get the IGD list.
std::list<std::shared_ptr<IGD>> getIgdList() const override;
// Return true if it has at least one valid IGD.
bool isReady() const override;
// Request a new mapping.
void requestMappingAdd(const Mapping& mapping) override;
// Renew an allocated mapping.
void requestMappingRenew(const Mapping& mapping) override;
// Removes a mapping.
void requestMappingRemove(const Mapping& mapping) override;
// Get the host (local) address.
const IpAddr getHostAddress() const override;
// Terminate. Nothing to do here, the clean-up is done when
// the IGD is cleared.
void terminate() override;
private:
NON_COPYABLE(NatPmp);
std::weak_ptr<NatPmp> weak() { return std::static_pointer_cast<NatPmp>(shared_from_this()); }
// Helpers to run tasks on NAT-PMP internal execution queue.
ScheduledExecutor* getNatpmpScheduler() { return &natpmpScheduler_; }
template<typename Callback>
void runOnNatPmpQueue(Callback&& cb)
{
natpmpScheduler_.run([cb = std::forward<Callback>(cb)]() mutable { cb(); });
}
// Helpers to run tasks on UPNP context execution queue.
ScheduledExecutor* getUpnContextScheduler() { return UpnpThreadUtil::getScheduler(); }
void terminate(std::condition_variable& cv);
void initNatPmp();
void getIgdPublicAddress();
void removeAllMappings();
int readResponse(natpmp_t& handle, natpmpresp_t& response);
int sendMappingRequest(const Mapping& mapping, uint32_t& lifetime);
// Adds a port mapping.
int addPortMapping(Mapping& mapping);
// Removes a port mapping.
void removePortMapping(Mapping& mapping);
// True if the error is fatal.
bool isErrorFatal(int error);
// Gets NAT-PMP error code string.
const char* getNatPmpErrorStr(int errorCode) const;
// Get local getaway.
std::unique_ptr<IpAddr> getLocalGateway() const;
// Helpers to process user's callbacks
void processIgdUpdate(UpnpIgdEvent event);
void processMappingAdded(const Mapping& map);
void processMappingRequestFailed(const Mapping& map);
void processMappingRenewed(const Mapping& map);
void processMappingRemoved(const Mapping& map);
// Check if the IGD has a local match
bool validIgdInstance(const std::shared_ptr<IGD>& igdIn);
// Increment errors counter.
void incrementErrorsCounter(const std::shared_ptr<IGD>& igd);
std::atomic_bool initialized_ {false};
// Data members
std::shared_ptr<PMPIGD> igd_;
natpmp_t natpmpHdl_;
ScheduledExecutor natpmpScheduler_ {"natpmp"};
std::shared_ptr<Task> searchForIgdTimer_ {};
unsigned int igdSearchCounter_ {0};
UpnpMappingObserver* observer_ {nullptr};
IpAddr hostAddress_ {};
// Calls from other threads that does not need synchronous access are
// rescheduled on the NatPmp private queue. This will avoid the need to
// protect most of the data members of this class.
// For some internal members (such as the igd instance and the host
// address) that need to be synchronously accessed, are protected by
// this mutex.
mutable std::mutex natpmpMutex_;
// Shutdown synchronization
bool shutdownComplete_ {false};
};
} // namespace upnp
} // namespace jami