blob: 1ecf90ebd360777306790745c965521d395f9282 [file] [log] [blame]
Adrien Béraud612b55b2023-05-29 10:42:04 -04001/*
2 * Copyright (C) 2004-2023 Savoir-faire Linux Inc.
3 *
4 * Author: Eden Abitbol <eden.abitbol@savoirfairelinux.com>
5 * Author: Mohamed Chibani <mohamed.chibani@savoirfairelinux.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22#pragma once
23
24#include "connectivity/upnp/protocol/upnp_protocol.h"
25#include "connectivity/upnp/protocol/igd.h"
26#include "pmp_igd.h"
27
28#include "logger.h"
29#include "connectivity/ip_utils.h"
30#include "noncopyable.h"
31#include "compiler_intrinsics.h"
32
33// uncomment to enable native natpmp error messages
34//#define ENABLE_STRNATPMPERR 1
35#include <natpmp.h>
36
37#include <atomic>
38#include <thread>
39
Adrien Béraud1ae60aa2023-07-07 09:55:09 -040040namespace dhtnet {
Adrien Béraud612b55b2023-05-29 10:42:04 -040041class IpAddr;
42}
43
Adrien Béraud1ae60aa2023-07-07 09:55:09 -040044namespace dhtnet {
Adrien Béraud612b55b2023-05-29 10:42:04 -040045namespace upnp {
46
47// Requested lifetime in seconds. The actual lifetime might be different.
48constexpr static unsigned int MAPPING_ALLOCATION_LIFETIME {60 * 60};
49// Max number of IGD search attempts before failure.
50constexpr static unsigned int MAX_RESTART_SEARCH_RETRIES {3};
51// Time-out between two successive read response.
52constexpr static auto TIMEOUT_BEFORE_READ_RETRY {std::chrono::milliseconds(300)};
53// Max number of read attempts before failure.
54constexpr static unsigned int MAX_READ_RETRIES {3};
55// Base unit for the timeout between two successive IGD search.
56constexpr static auto NATPMP_SEARCH_RETRY_UNIT {std::chrono::seconds(10)};
57
58class NatPmp : public UPnPProtocol
59{
60public:
61 NatPmp();
62 ~NatPmp();
63
64 // Set the observer.
65 void setObserver(UpnpMappingObserver* obs) override;
66
67 // Returns the protocol type.
68 NatProtocolType getProtocol() const override { return NatProtocolType::NAT_PMP; }
69
70 // Get protocol type as string.
71 char const* getProtocolName() const override { return "NAT-PMP"; }
72
73 // Notifies a change in network.
74 void clearIgds() override;
75
76 // Renew pmp_igd.
77 void searchForIgd() override;
78
79 // Get the IGD list.
80 std::list<std::shared_ptr<IGD>> getIgdList() const override;
81
82 // Return true if it has at least one valid IGD.
83 bool isReady() const override;
84
85 // Request a new mapping.
86 void requestMappingAdd(const Mapping& mapping) override;
87
88 // Renew an allocated mapping.
89 void requestMappingRenew(const Mapping& mapping) override;
90
91 // Removes a mapping.
92 void requestMappingRemove(const Mapping& mapping) override;
93
94 // Get the host (local) address.
95 const IpAddr getHostAddress() const override;
96
97 // Terminate. Nothing to do here, the clean-up is done when
98 // the IGD is cleared.
99 void terminate() override;
100
101private:
102 NON_COPYABLE(NatPmp);
103
104 std::weak_ptr<NatPmp> weak() { return std::static_pointer_cast<NatPmp>(shared_from_this()); }
105
106 // Helpers to run tasks on NAT-PMP internal execution queue.
107 ScheduledExecutor* getNatpmpScheduler() { return &natpmpScheduler_; }
108 template<typename Callback>
109 void runOnNatPmpQueue(Callback&& cb)
110 {
111 natpmpScheduler_.run([cb = std::forward<Callback>(cb)]() mutable { cb(); });
112 }
113
114 // Helpers to run tasks on UPNP context execution queue.
115 ScheduledExecutor* getUpnContextScheduler() { return UpnpThreadUtil::getScheduler(); }
116
117 void terminate(std::condition_variable& cv);
118
119 void initNatPmp();
120 void getIgdPublicAddress();
121 void removeAllMappings();
122 int readResponse(natpmp_t& handle, natpmpresp_t& response);
123 int sendMappingRequest(const Mapping& mapping, uint32_t& lifetime);
124
125 // Adds a port mapping.
126 int addPortMapping(Mapping& mapping);
127 // Removes a port mapping.
128 void removePortMapping(Mapping& mapping);
129
130 // True if the error is fatal.
131 bool isErrorFatal(int error);
132 // Gets NAT-PMP error code string.
133 const char* getNatPmpErrorStr(int errorCode) const;
134 // Get local getaway.
135 std::unique_ptr<IpAddr> getLocalGateway() const;
136
137 // Helpers to process user's callbacks
138 void processIgdUpdate(UpnpIgdEvent event);
139 void processMappingAdded(const Mapping& map);
140 void processMappingRequestFailed(const Mapping& map);
141 void processMappingRenewed(const Mapping& map);
142 void processMappingRemoved(const Mapping& map);
143
144 // Check if the IGD has a local match
145 bool validIgdInstance(const std::shared_ptr<IGD>& igdIn);
146
147 // Increment errors counter.
148 void incrementErrorsCounter(const std::shared_ptr<IGD>& igd);
149
150 std::atomic_bool initialized_ {false};
151
152 // Data members
153 std::shared_ptr<PMPIGD> igd_;
154 natpmp_t natpmpHdl_;
155 ScheduledExecutor natpmpScheduler_ {"natpmp"};
156 std::shared_ptr<Task> searchForIgdTimer_ {};
157 unsigned int igdSearchCounter_ {0};
158 UpnpMappingObserver* observer_ {nullptr};
159 IpAddr hostAddress_ {};
160
161 // Calls from other threads that does not need synchronous access are
162 // rescheduled on the NatPmp private queue. This will avoid the need to
163 // protect most of the data members of this class.
164 // For some internal members (such as the igd instance and the host
165 // address) that need to be synchronously accessed, are protected by
166 // this mutex.
167 mutable std::mutex natpmpMutex_;
168
169 // Shutdown synchronization
170 bool shutdownComplete_ {false};
171};
172
173} // namespace upnp
174} // namespace jami