blob: 095886472acc579509b358a5f1421b9f424e263f [file] [log] [blame]
Adrien Béraud612b55b2023-05-29 10:42:04 -04001/*
2 * Copyright (C) 2004-2023 Savoir-faire Linux Inc.
3 *
Adrien Béraudcb753622023-07-17 22:32:49 -04004 * This program is free software: you can redistribute it and/or modify
Adrien Béraud612b55b2023-05-29 10:42:04 -04005 * it under the terms of the GNU General Public License as published by
Adrien Béraudcb753622023-07-17 22:32:49 -04006 * the Free Software Foundation, either version 3 of the License, or
Adrien Béraud612b55b2023-05-29 10:42:04 -04007 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Adrien Béraudcb753622023-07-17 22:32:49 -040011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Adrien Béraud612b55b2023-05-29 10:42:04 -040012 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
Adrien Béraudcb753622023-07-17 22:32:49 -040015 * along with this program. If not, see <https://www.gnu.org/licenses/>.
Adrien Béraud612b55b2023-05-29 10:42:04 -040016 */
Adrien Béraud612b55b2023-05-29 10:42:04 -040017#pragma once
18
Morteza Namvar5f639522023-07-04 17:08:58 -040019/*#include "upnp_protocol.h"
Adrien Béraud612b55b2023-05-29 10:42:04 -040020#if HAVE_LIBNATPMP
21#include "protocol/natpmp/nat_pmp.h"
22#endif
23#if HAVE_LIBUPNP
24#include "protocol/pupnp/pupnp.h"
25#endif
Morteza Namvar5f639522023-07-04 17:08:58 -040026#include "igd.h"*/
Adrien Béraud612b55b2023-05-29 10:42:04 -040027
Adrien Bérauddbb06862023-07-08 09:18:39 -040028#include "../ip_utils.h"
Adrien Béraud612b55b2023-05-29 10:42:04 -040029
Adrien Bérauddbb06862023-07-08 09:18:39 -040030#include "mapping.h"
Morteza Namvar5f639522023-07-04 17:08:58 -040031
Adrien Béraud612b55b2023-05-29 10:42:04 -040032#include <opendht/rng.h>
Adrien Béraud25c30c42023-07-05 13:46:54 -040033#include <opendht/logger.h>
Adrien Béraud612b55b2023-05-29 10:42:04 -040034#include <asio/steady_timer.hpp>
35
36#include <set>
37#include <map>
38#include <mutex>
39#include <memory>
40#include <string>
41#include <chrono>
42#include <random>
43#include <atomic>
Morteza Namvar5f639522023-07-04 17:08:58 -040044#include <condition_variable>
Adrien Béraud612b55b2023-05-29 10:42:04 -040045
Morteza Namvar5f639522023-07-04 17:08:58 -040046#include <cstdlib>
Adrien Béraud612b55b2023-05-29 10:42:04 -040047
Adrien Béraud612b55b2023-05-29 10:42:04 -040048using IgdFoundCallback = std::function<void()>;
49
Adrien Béraud1ae60aa2023-07-07 09:55:09 -040050namespace dhtnet {
Adrien Béraud612b55b2023-05-29 10:42:04 -040051class IpAddr;
52}
53
Adrien Béraud1ae60aa2023-07-07 09:55:09 -040054namespace dhtnet {
Adrien Béraud612b55b2023-05-29 10:42:04 -040055namespace upnp {
56
Morteza Namvar5f639522023-07-04 17:08:58 -040057class UPnPProtocol;
58class IGD;
59
François-Simon Fauteux-Chapleau826f0ba2024-05-29 15:22:21 -040060struct IGDInfo
61{
62 std::string uid;
63 IpAddr localIp;
64 IpAddr publicIp;
65 std::vector<MappingInfo> mappingInfoList;
66};
67
Morteza Namvar5f639522023-07-04 17:08:58 -040068enum class UpnpIgdEvent { ADDED, REMOVED, INVALID_STATE };
69
70// Interface used to report mapping event from the protocol implementations.
71// This interface is meant to be implemented only by UPnPConext class. Sincce
72// this class is a singleton, it's assumed that it out-lives the protocol
73// implementations. In other words, the observer is always assumed to point to a
74// valid instance.
75class UpnpMappingObserver
76{
77public:
78 UpnpMappingObserver() {};
79 virtual ~UpnpMappingObserver() {};
80
81 virtual void onIgdUpdated(const std::shared_ptr<IGD>& igd, UpnpIgdEvent event) = 0;
82 virtual void onMappingAdded(const std::shared_ptr<IGD>& igd, const Mapping& map) = 0;
83 virtual void onMappingRequestFailed(const Mapping& map) = 0;
84#if HAVE_LIBNATPMP
85 virtual void onMappingRenewed(const std::shared_ptr<IGD>& igd, const Mapping& map) = 0;
86#endif
87 virtual void onMappingRemoved(const std::shared_ptr<IGD>& igd, const Mapping& map) = 0;
88};
89
Adrien Béraud370257c2023-08-15 20:53:09 -040090class UPnPContext : public UpnpMappingObserver
Adrien Béraud612b55b2023-05-29 10:42:04 -040091{
92private:
93 struct MappingStatus
94 {
95 int openCount_ {0};
96 int readyCount_ {0};
97 int pendingCount_ {0};
98 int inProgressCount_ {0};
99 int failedCount_ {0};
100
101 void reset()
102 {
103 openCount_ = 0;
104 readyCount_ = 0;
105 pendingCount_ = 0;
106 inProgressCount_ = 0;
107 failedCount_ = 0;
108 };
109 int sum() { return openCount_ + pendingCount_ + inProgressCount_ + failedCount_; }
110 };
111
112public:
Sébastien Blin55abf072023-07-19 10:21:21 -0400113 UPnPContext(const std::shared_ptr<asio::io_context>& ctx, const std::shared_ptr<dht::log::Logger>& logger);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400114 ~UPnPContext();
115
Adrien Béraudb04fbd72023-08-17 19:56:11 -0400116 std::shared_ptr<asio::io_context> createIoContext(const std::shared_ptr<asio::io_context>& ctx, const std::shared_ptr<dht::log::Logger>& logger);
117
Adrien Béraud612b55b2023-05-29 10:42:04 -0400118 // Terminate the instance.
119 void shutdown();
120
121 // Set the known public address
122 void setPublicAddress(const IpAddr& addr);
123
124 // Check if there is a valid IGD in the IGD list.
125 bool isReady() const;
126
127 // Get external Ip of a chosen IGD.
128 IpAddr getExternalIP() const;
129
130 // Inform the UPnP context that the network status has changed. This clears the list of known
131 void connectivityChanged();
132
133 // Returns a shared pointer of the mapping.
134 Mapping::sharedPtr_t reserveMapping(Mapping& requestedMap);
135
136 // Release an used mapping (make it available for future use).
137 void releaseMapping(const Mapping& map);
138
139 // Register a controller
140 void registerController(void* controller);
141 // Unregister a controller
142 void unregisterController(void* controller);
143
144 // Generate random port numbers
145 static uint16_t generateRandomPort(PortType type, bool mustBeEven = false);
146
François-Simon Fauteux-Chapleau826f0ba2024-05-29 15:22:21 -0400147 // Return information about the UPnPContext's valid IGDs, including the list
148 // of all existing port mappings (for IGDs which support a protocol that allows
149 // querying that information -- UPnP does, but NAT-PMP doesn't for example)
150 std::vector<IGDInfo> getIgdsInfo() const;
151
Adrien Berauda8731ac2023-08-17 12:19:39 -0400152 template <typename T>
153 inline void dispatch(T&& f) {
154 ctx->dispatch(std::move(f));
155 }
156
Adrien Béraud612b55b2023-05-29 10:42:04 -0400157private:
158 // Initialization
159 void init();
160
161 /**
162 * @brief start the search for IGDs activate the mapping
163 * list update.
164 *
165 */
166 void startUpnp();
167
168 /**
169 * @brief Clear all IGDs and release/delete current mappings
170 *
171 * @param forceRelease If true, also delete mappings with enabled
172 * auto-update feature.
173 *
174 */
175 void stopUpnp(bool forceRelease = false);
176
177 void shutdown(std::condition_variable& cv);
178
179 // Create and register a new mapping.
180 Mapping::sharedPtr_t registerMapping(Mapping& map);
181
182 // Removes the mapping from the list.
Adrien Béraud612b55b2023-05-29 10:42:04 -0400183 void unregisterMapping(const Mapping::sharedPtr_t& map);
184
185 // Perform the request on the provided IGD.
186 void requestMapping(const Mapping::sharedPtr_t& map);
187
188 // Request a mapping remove from the IGD.
189 void requestRemoveMapping(const Mapping::sharedPtr_t& map);
190
191 // Remove all mappings of the given type.
192 void deleteAllMappings(PortType type);
193
Adrien Beraud3bd61c92023-08-17 16:57:37 -0400194 // Update the state and notify the listener
195 void updateMappingState(const Mapping::sharedPtr_t& map,
196 MappingState newState,
197 bool notify = true);
198
Adrien Béraud612b55b2023-05-29 10:42:04 -0400199 // Provision ports.
200 uint16_t getAvailablePortNumber(PortType type);
201
202 // Update preferred IGD
203 void updatePreferredIgd();
204
205 // Get preferred IGD
206 std::shared_ptr<IGD> getPreferredIgd() const;
207
208 // Check and prune the mapping list. Called periodically.
209 void updateMappingList(bool async);
210
211 // Provision (pre-allocate) the requested number of mappings.
212 bool provisionNewMappings(PortType type, int portCount);
213
214 // Close unused mappings.
215 bool deleteUnneededMappings(PortType type, int portCount);
216
217 /**
218 * Prune the mapping list.To avoid competing with allocation
219 * requests, the pruning is performed only if there are no
220 * requests in progress.
221 */
222 void pruneMappingList();
223
224 /**
225 * Check if there are allocated mappings from previous instances,
226 * and try to close them.
227 * Only done for UPNP protocol. NAT-PMP allocations will expire
228 * anyway if not renewed.
229 */
230 void pruneUnMatchedMappings(const std::shared_ptr<IGD>& igd,
231 const std::map<Mapping::key_t, Mapping>& remoteMapList);
232
233 /**
234 * Check the local mapping list against the list returned by the
235 * IGD and remove all mappings which do not have a match.
236 * Only done for UPNP protocol.
237 */
238 void pruneUnTrackedMappings(const std::shared_ptr<IGD>& igd,
239 const std::map<Mapping::key_t, Mapping>& remoteMapList);
240
241 void pruneMappingsWithInvalidIgds(const std::shared_ptr<IGD>& igd);
242
243 /**
244 * @brief Get the mapping list
245 *
246 * @param type transport type (TCP/UDP)
247 * @return a reference on the map
248 * @warning concurrency protection done by the caller
249 */
250 std::map<Mapping::key_t, Mapping::sharedPtr_t>& getMappingList(PortType type);
251
252 // Get the mapping from the key.
253 Mapping::sharedPtr_t getMappingWithKey(Mapping::key_t key);
254
255 // Get the number of mappings per state.
256 void getMappingStatus(PortType type, MappingStatus& status);
257 void getMappingStatus(MappingStatus& status);
258
259#if HAVE_LIBNATPMP
260 void renewAllocations();
261#endif
262
263 // Process requests with pending status.
264 void processPendingRequests(const std::shared_ptr<IGD>& igd);
265
266 // Process mapping with auto-update flag enabled.
267 void processMappingWithAutoUpdate();
268
269 // Implementation of UpnpMappingObserver interface.
270
271 // Callback used to report changes in IGD status.
272 void onIgdUpdated(const std::shared_ptr<IGD>& igd, UpnpIgdEvent event) override;
273 // Callback used to report add request status.
274 void onMappingAdded(const std::shared_ptr<IGD>& igd, const Mapping& map) override;
275 // Callback invoked when a request fails. Reported on failures for both
276 // new requests and renewal requests (if supported by the the protocol).
277 void onMappingRequestFailed(const Mapping& map) override;
278#if HAVE_LIBNATPMP
279 // Callback used to report renew request status.
280 void onMappingRenewed(const std::shared_ptr<IGD>& igd, const Mapping& map) override;
281#endif
282 // Callback used to report remove request status.
283 void onMappingRemoved(const std::shared_ptr<IGD>& igd, const Mapping& map) override;
284
285private:
286 UPnPContext(const UPnPContext&) = delete;
287 UPnPContext(UPnPContext&&) = delete;
288 UPnPContext& operator=(UPnPContext&&) = delete;
289 UPnPContext& operator=(const UPnPContext&) = delete;
290
Adrien Béraudc36965c2023-08-17 21:50:27 -0400291 void _connectivityChanged(const asio::error_code& ec);
292
293 // Thread (io_context), destroyed last
294 std::unique_ptr<std::thread> ioContextRunner_ {};
295
Adrien Béraud612b55b2023-05-29 10:42:04 -0400296 bool started_ {false};
297
298 // The known public address. The external addresses returned by
299 // the IGDs will be checked against this address.
300 IpAddr knownPublicAddress_ {};
301
302 // Set of registered controllers
Adrien Béraudc36965c2023-08-17 21:50:27 -0400303 std::mutex mutable controllerMutex_;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400304 std::set<void*> controllerList_;
305
306 // Map of available protocols.
307 std::map<NatProtocolType, std::shared_ptr<UPnPProtocol>> protocolList_;
308
309 // Port ranges for TCP and UDP (in that order).
310 std::map<PortType, std::pair<uint16_t, uint16_t>> portRange_ {};
311
312 // Min open ports limit
313 int minOpenPortLimit_[2] {4, 8};
314 // Max open ports limit
315 int maxOpenPortLimit_[2] {8, 12};
316
Adrien Béraud370257c2023-08-15 20:53:09 -0400317 std::shared_ptr<asio::io_context> ctx;
318 std::shared_ptr<dht::log::Logger> logger_;
Adrien Berauda8731ac2023-08-17 12:19:39 -0400319 asio::steady_timer mappingListUpdateTimer_;
Adrien Béraudc36965c2023-08-17 21:50:27 -0400320 asio::steady_timer connectivityChangedTimer_;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400321
322 // Current preferred IGD. Can be null if there is no valid IGD.
323 std::shared_ptr<IGD> preferredIgd_;
324
325 // This mutex must lock only these two members. All other
326 // members must be accessed only from the UPNP context thread.
327 std::mutex mutable mappingMutex_;
328 // List of mappings.
329 std::map<Mapping::key_t, Mapping::sharedPtr_t> mappingList_[2] {};
330 std::set<std::shared_ptr<IGD>> validIgdList_ {};
331
332 // Shutdown synchronization
333 bool shutdownComplete_ {false};
François-Simon Fauteux-Chapleau808db4f2024-04-19 11:39:47 -0400334 bool shutdownTimedOut_ {false};
Adrien Béraud612b55b2023-05-29 10:42:04 -0400335};
336
337} // namespace upnp
Sébastien Blin464bdff2023-07-19 08:02:53 -0400338} // namespace dhtnet