blob: a85db51082b381dd62d27f23f4b361de1bab7f5c [file] [log] [blame]
Adrien Béraud612b55b2023-05-29 10:42:04 -04001/*
2 * Copyright (C) 2004-2023 Savoir-faire Linux Inc.
3 *
4 * Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
5 * Author: Eden Abitbol <eden.abitbol@savoirfairelinux.com>
6 * Author: Mohamed Chibani <mohamed.chibani@savoirfairelinux.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
23#pragma once
24
Morteza Namvar5f639522023-07-04 17:08:58 -040025/*#include "upnp_protocol.h"
Adrien Béraud612b55b2023-05-29 10:42:04 -040026#if HAVE_LIBNATPMP
27#include "protocol/natpmp/nat_pmp.h"
28#endif
29#if HAVE_LIBUPNP
30#include "protocol/pupnp/pupnp.h"
31#endif
Morteza Namvar5f639522023-07-04 17:08:58 -040032#include "igd.h"*/
Adrien Béraud612b55b2023-05-29 10:42:04 -040033
34#include "ip_utils.h"
35
Morteza Namvar5f639522023-07-04 17:08:58 -040036#include "upnp_thread_util.h"
37#include "upnp/mapping.h"
38
Adrien Béraud612b55b2023-05-29 10:42:04 -040039#include <opendht/rng.h>
40#include <asio/steady_timer.hpp>
41
42#include <set>
43#include <map>
44#include <mutex>
45#include <memory>
46#include <string>
47#include <chrono>
48#include <random>
49#include <atomic>
Morteza Namvar5f639522023-07-04 17:08:58 -040050#include <condition_variable>
Adrien Béraud612b55b2023-05-29 10:42:04 -040051
Morteza Namvar5f639522023-07-04 17:08:58 -040052#include <cstdlib>
Adrien Béraud612b55b2023-05-29 10:42:04 -040053
54using random_device = dht::crypto::random_device;
55
56using IgdFoundCallback = std::function<void()>;
57
58namespace jami {
59class IpAddr;
60}
61
62namespace jami {
63namespace upnp {
64
Morteza Namvar5f639522023-07-04 17:08:58 -040065class UPnPProtocol;
66class IGD;
67
68enum 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éraud33a03ba2023-06-21 11:35:54 -040090class UPnPContext : public UpnpMappingObserver, protected UpnpThreadUtil
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:
113 UPnPContext();
114 ~UPnPContext();
115
116 // Retrieve the UPnPContext singleton.
117 static std::shared_ptr<UPnPContext> getUPnPContext();
118
119 // Terminate the instance.
120 void shutdown();
121
122 // Set the known public address
123 void setPublicAddress(const IpAddr& addr);
124
125 // Check if there is a valid IGD in the IGD list.
126 bool isReady() const;
127
128 // Get external Ip of a chosen IGD.
129 IpAddr getExternalIP() const;
130
131 // Inform the UPnP context that the network status has changed. This clears the list of known
132 void connectivityChanged();
133
134 // Returns a shared pointer of the mapping.
135 Mapping::sharedPtr_t reserveMapping(Mapping& requestedMap);
136
137 // Release an used mapping (make it available for future use).
138 void releaseMapping(const Mapping& map);
139
140 // Register a controller
141 void registerController(void* controller);
142 // Unregister a controller
143 void unregisterController(void* controller);
144
145 // Generate random port numbers
146 static uint16_t generateRandomPort(PortType type, bool mustBeEven = false);
147
148private:
149 // Initialization
150 void init();
151
152 /**
153 * @brief start the search for IGDs activate the mapping
154 * list update.
155 *
156 */
157 void startUpnp();
158
159 /**
160 * @brief Clear all IGDs and release/delete current mappings
161 *
162 * @param forceRelease If true, also delete mappings with enabled
163 * auto-update feature.
164 *
165 */
166 void stopUpnp(bool forceRelease = false);
167
168 void shutdown(std::condition_variable& cv);
169
170 // Create and register a new mapping.
171 Mapping::sharedPtr_t registerMapping(Mapping& map);
172
173 // Removes the mapping from the list.
174 std::map<Mapping::key_t, Mapping::sharedPtr_t>::iterator unregisterMapping(
175 std::map<Mapping::key_t, Mapping::sharedPtr_t>::iterator it);
176 void unregisterMapping(const Mapping::sharedPtr_t& map);
177
178 // Perform the request on the provided IGD.
179 void requestMapping(const Mapping::sharedPtr_t& map);
180
181 // Request a mapping remove from the IGD.
182 void requestRemoveMapping(const Mapping::sharedPtr_t& map);
183
184 // Remove all mappings of the given type.
185 void deleteAllMappings(PortType type);
186
187 // Update the state and notify the listener
188 void updateMappingState(const Mapping::sharedPtr_t& map,
189 MappingState newState,
190 bool notify = true);
191
192 // Provision ports.
193 uint16_t getAvailablePortNumber(PortType type);
194
195 // Update preferred IGD
196 void updatePreferredIgd();
197
198 // Get preferred IGD
199 std::shared_ptr<IGD> getPreferredIgd() const;
200
201 // Check and prune the mapping list. Called periodically.
202 void updateMappingList(bool async);
203
204 // Provision (pre-allocate) the requested number of mappings.
205 bool provisionNewMappings(PortType type, int portCount);
206
207 // Close unused mappings.
208 bool deleteUnneededMappings(PortType type, int portCount);
209
210 /**
211 * Prune the mapping list.To avoid competing with allocation
212 * requests, the pruning is performed only if there are no
213 * requests in progress.
214 */
215 void pruneMappingList();
216
217 /**
218 * Check if there are allocated mappings from previous instances,
219 * and try to close them.
220 * Only done for UPNP protocol. NAT-PMP allocations will expire
221 * anyway if not renewed.
222 */
223 void pruneUnMatchedMappings(const std::shared_ptr<IGD>& igd,
224 const std::map<Mapping::key_t, Mapping>& remoteMapList);
225
226 /**
227 * Check the local mapping list against the list returned by the
228 * IGD and remove all mappings which do not have a match.
229 * Only done for UPNP protocol.
230 */
231 void pruneUnTrackedMappings(const std::shared_ptr<IGD>& igd,
232 const std::map<Mapping::key_t, Mapping>& remoteMapList);
233
234 void pruneMappingsWithInvalidIgds(const std::shared_ptr<IGD>& igd);
235
236 /**
237 * @brief Get the mapping list
238 *
239 * @param type transport type (TCP/UDP)
240 * @return a reference on the map
241 * @warning concurrency protection done by the caller
242 */
243 std::map<Mapping::key_t, Mapping::sharedPtr_t>& getMappingList(PortType type);
244
245 // Get the mapping from the key.
246 Mapping::sharedPtr_t getMappingWithKey(Mapping::key_t key);
247
248 // Get the number of mappings per state.
249 void getMappingStatus(PortType type, MappingStatus& status);
250 void getMappingStatus(MappingStatus& status);
251
252#if HAVE_LIBNATPMP
253 void renewAllocations();
254#endif
255
256 // Process requests with pending status.
257 void processPendingRequests(const std::shared_ptr<IGD>& igd);
258
259 // Process mapping with auto-update flag enabled.
260 void processMappingWithAutoUpdate();
261
262 // Implementation of UpnpMappingObserver interface.
263
264 // Callback used to report changes in IGD status.
265 void onIgdUpdated(const std::shared_ptr<IGD>& igd, UpnpIgdEvent event) override;
266 // Callback used to report add request status.
267 void onMappingAdded(const std::shared_ptr<IGD>& igd, const Mapping& map) override;
268 // Callback invoked when a request fails. Reported on failures for both
269 // new requests and renewal requests (if supported by the the protocol).
270 void onMappingRequestFailed(const Mapping& map) override;
271#if HAVE_LIBNATPMP
272 // Callback used to report renew request status.
273 void onMappingRenewed(const std::shared_ptr<IGD>& igd, const Mapping& map) override;
274#endif
275 // Callback used to report remove request status.
276 void onMappingRemoved(const std::shared_ptr<IGD>& igd, const Mapping& map) override;
277
278private:
279 UPnPContext(const UPnPContext&) = delete;
280 UPnPContext(UPnPContext&&) = delete;
281 UPnPContext& operator=(UPnPContext&&) = delete;
282 UPnPContext& operator=(const UPnPContext&) = delete;
283
284 bool started_ {false};
285
286 // The known public address. The external addresses returned by
287 // the IGDs will be checked against this address.
288 IpAddr knownPublicAddress_ {};
289
290 // Set of registered controllers
291 std::set<void*> controllerList_;
292
293 // Map of available protocols.
294 std::map<NatProtocolType, std::shared_ptr<UPnPProtocol>> protocolList_;
295
296 // Port ranges for TCP and UDP (in that order).
297 std::map<PortType, std::pair<uint16_t, uint16_t>> portRange_ {};
298
299 // Min open ports limit
300 int minOpenPortLimit_[2] {4, 8};
301 // Max open ports limit
302 int maxOpenPortLimit_[2] {8, 12};
303
304 //std::shared_ptr<Task> mappingListUpdateTimer_ {};
305 asio::steady_timer mappingListUpdateTimer_;// {};
306
307 // Current preferred IGD. Can be null if there is no valid IGD.
308 std::shared_ptr<IGD> preferredIgd_;
309
310 // This mutex must lock only these two members. All other
311 // members must be accessed only from the UPNP context thread.
312 std::mutex mutable mappingMutex_;
313 // List of mappings.
314 std::map<Mapping::key_t, Mapping::sharedPtr_t> mappingList_[2] {};
315 std::set<std::shared_ptr<IGD>> validIgdList_ {};
316
317 // Shutdown synchronization
318 bool shutdownComplete_ {false};
319};
320
321} // namespace upnp
322} // namespace jami