blob: 30d50c034661a01b0c914aaeb0c504eff62c8112 [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
25#include "protocol/upnp_protocol.h"
26#if HAVE_LIBNATPMP
27#include "protocol/natpmp/nat_pmp.h"
28#endif
29#if HAVE_LIBUPNP
30#include "protocol/pupnp/pupnp.h"
31#endif
32#include "protocol/igd.h"
33
34#include "ip_utils.h"
35
36#include <opendht/rng.h>
37#include <asio/steady_timer.hpp>
38
39#include <set>
40#include <map>
41#include <mutex>
42#include <memory>
43#include <string>
44#include <chrono>
45#include <random>
46#include <atomic>
47#include <cstdlib>
48
49//#include "upnp_thread_util.h"
50
51using random_device = dht::crypto::random_device;
52
53using IgdFoundCallback = std::function<void()>;
54
55namespace jami {
56class IpAddr;
57}
58
59namespace jami {
60namespace upnp {
61
62class UPnPContext : public UpnpMappingObserver//, protected UpnpThreadUtil
63{
64private:
65 struct MappingStatus
66 {
67 int openCount_ {0};
68 int readyCount_ {0};
69 int pendingCount_ {0};
70 int inProgressCount_ {0};
71 int failedCount_ {0};
72
73 void reset()
74 {
75 openCount_ = 0;
76 readyCount_ = 0;
77 pendingCount_ = 0;
78 inProgressCount_ = 0;
79 failedCount_ = 0;
80 };
81 int sum() { return openCount_ + pendingCount_ + inProgressCount_ + failedCount_; }
82 };
83
84public:
85 UPnPContext();
86 ~UPnPContext();
87
88 // Retrieve the UPnPContext singleton.
89 static std::shared_ptr<UPnPContext> getUPnPContext();
90
91 // Terminate the instance.
92 void shutdown();
93
94 // Set the known public address
95 void setPublicAddress(const IpAddr& addr);
96
97 // Check if there is a valid IGD in the IGD list.
98 bool isReady() const;
99
100 // Get external Ip of a chosen IGD.
101 IpAddr getExternalIP() const;
102
103 // Inform the UPnP context that the network status has changed. This clears the list of known
104 void connectivityChanged();
105
106 // Returns a shared pointer of the mapping.
107 Mapping::sharedPtr_t reserveMapping(Mapping& requestedMap);
108
109 // Release an used mapping (make it available for future use).
110 void releaseMapping(const Mapping& map);
111
112 // Register a controller
113 void registerController(void* controller);
114 // Unregister a controller
115 void unregisterController(void* controller);
116
117 // Generate random port numbers
118 static uint16_t generateRandomPort(PortType type, bool mustBeEven = false);
119
120private:
121 // Initialization
122 void init();
123
124 /**
125 * @brief start the search for IGDs activate the mapping
126 * list update.
127 *
128 */
129 void startUpnp();
130
131 /**
132 * @brief Clear all IGDs and release/delete current mappings
133 *
134 * @param forceRelease If true, also delete mappings with enabled
135 * auto-update feature.
136 *
137 */
138 void stopUpnp(bool forceRelease = false);
139
140 void shutdown(std::condition_variable& cv);
141
142 // Create and register a new mapping.
143 Mapping::sharedPtr_t registerMapping(Mapping& map);
144
145 // Removes the mapping from the list.
146 std::map<Mapping::key_t, Mapping::sharedPtr_t>::iterator unregisterMapping(
147 std::map<Mapping::key_t, Mapping::sharedPtr_t>::iterator it);
148 void unregisterMapping(const Mapping::sharedPtr_t& map);
149
150 // Perform the request on the provided IGD.
151 void requestMapping(const Mapping::sharedPtr_t& map);
152
153 // Request a mapping remove from the IGD.
154 void requestRemoveMapping(const Mapping::sharedPtr_t& map);
155
156 // Remove all mappings of the given type.
157 void deleteAllMappings(PortType type);
158
159 // Update the state and notify the listener
160 void updateMappingState(const Mapping::sharedPtr_t& map,
161 MappingState newState,
162 bool notify = true);
163
164 // Provision ports.
165 uint16_t getAvailablePortNumber(PortType type);
166
167 // Update preferred IGD
168 void updatePreferredIgd();
169
170 // Get preferred IGD
171 std::shared_ptr<IGD> getPreferredIgd() const;
172
173 // Check and prune the mapping list. Called periodically.
174 void updateMappingList(bool async);
175
176 // Provision (pre-allocate) the requested number of mappings.
177 bool provisionNewMappings(PortType type, int portCount);
178
179 // Close unused mappings.
180 bool deleteUnneededMappings(PortType type, int portCount);
181
182 /**
183 * Prune the mapping list.To avoid competing with allocation
184 * requests, the pruning is performed only if there are no
185 * requests in progress.
186 */
187 void pruneMappingList();
188
189 /**
190 * Check if there are allocated mappings from previous instances,
191 * and try to close them.
192 * Only done for UPNP protocol. NAT-PMP allocations will expire
193 * anyway if not renewed.
194 */
195 void pruneUnMatchedMappings(const std::shared_ptr<IGD>& igd,
196 const std::map<Mapping::key_t, Mapping>& remoteMapList);
197
198 /**
199 * Check the local mapping list against the list returned by the
200 * IGD and remove all mappings which do not have a match.
201 * Only done for UPNP protocol.
202 */
203 void pruneUnTrackedMappings(const std::shared_ptr<IGD>& igd,
204 const std::map<Mapping::key_t, Mapping>& remoteMapList);
205
206 void pruneMappingsWithInvalidIgds(const std::shared_ptr<IGD>& igd);
207
208 /**
209 * @brief Get the mapping list
210 *
211 * @param type transport type (TCP/UDP)
212 * @return a reference on the map
213 * @warning concurrency protection done by the caller
214 */
215 std::map<Mapping::key_t, Mapping::sharedPtr_t>& getMappingList(PortType type);
216
217 // Get the mapping from the key.
218 Mapping::sharedPtr_t getMappingWithKey(Mapping::key_t key);
219
220 // Get the number of mappings per state.
221 void getMappingStatus(PortType type, MappingStatus& status);
222 void getMappingStatus(MappingStatus& status);
223
224#if HAVE_LIBNATPMP
225 void renewAllocations();
226#endif
227
228 // Process requests with pending status.
229 void processPendingRequests(const std::shared_ptr<IGD>& igd);
230
231 // Process mapping with auto-update flag enabled.
232 void processMappingWithAutoUpdate();
233
234 // Implementation of UpnpMappingObserver interface.
235
236 // Callback used to report changes in IGD status.
237 void onIgdUpdated(const std::shared_ptr<IGD>& igd, UpnpIgdEvent event) override;
238 // Callback used to report add request status.
239 void onMappingAdded(const std::shared_ptr<IGD>& igd, const Mapping& map) override;
240 // Callback invoked when a request fails. Reported on failures for both
241 // new requests and renewal requests (if supported by the the protocol).
242 void onMappingRequestFailed(const Mapping& map) override;
243#if HAVE_LIBNATPMP
244 // Callback used to report renew request status.
245 void onMappingRenewed(const std::shared_ptr<IGD>& igd, const Mapping& map) override;
246#endif
247 // Callback used to report remove request status.
248 void onMappingRemoved(const std::shared_ptr<IGD>& igd, const Mapping& map) override;
249
250private:
251 UPnPContext(const UPnPContext&) = delete;
252 UPnPContext(UPnPContext&&) = delete;
253 UPnPContext& operator=(UPnPContext&&) = delete;
254 UPnPContext& operator=(const UPnPContext&) = delete;
255
256 bool started_ {false};
257
258 // The known public address. The external addresses returned by
259 // the IGDs will be checked against this address.
260 IpAddr knownPublicAddress_ {};
261
262 // Set of registered controllers
263 std::set<void*> controllerList_;
264
265 // Map of available protocols.
266 std::map<NatProtocolType, std::shared_ptr<UPnPProtocol>> protocolList_;
267
268 // Port ranges for TCP and UDP (in that order).
269 std::map<PortType, std::pair<uint16_t, uint16_t>> portRange_ {};
270
271 // Min open ports limit
272 int minOpenPortLimit_[2] {4, 8};
273 // Max open ports limit
274 int maxOpenPortLimit_[2] {8, 12};
275
276 //std::shared_ptr<Task> mappingListUpdateTimer_ {};
277 asio::steady_timer mappingListUpdateTimer_;// {};
278
279 // Current preferred IGD. Can be null if there is no valid IGD.
280 std::shared_ptr<IGD> preferredIgd_;
281
282 // This mutex must lock only these two members. All other
283 // members must be accessed only from the UPNP context thread.
284 std::mutex mutable mappingMutex_;
285 // List of mappings.
286 std::map<Mapping::key_t, Mapping::sharedPtr_t> mappingList_[2] {};
287 std::set<std::shared_ptr<IGD>> validIgdList_ {};
288
289 // Shutdown synchronization
290 bool shutdownComplete_ {false};
291};
292
293} // namespace upnp
294} // namespace jami