Line data Source code
1 : /*
2 : * Copyright (C) 2004-2026 Savoir-faire Linux Inc.
3 : *
4 : * This program is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 3 of the License, or
7 : * (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
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 : */
17 : #pragma once
18 :
19 : #include "routing_table.h"
20 : #include "swarm_protocol.h"
21 :
22 : #include <memory>
23 :
24 : namespace jami {
25 :
26 : using namespace swarm_protocol;
27 :
28 : class SwarmManager : public std::enable_shared_from_this<SwarmManager>
29 : {
30 : using ChannelCb = std::function<bool(const std::shared_ptr<dhtnet::ChannelSocketInterface>&)>;
31 : using NeedSocketCb = std::function<void(const std::string&, ChannelCb&&, bool noNewSocket)>;
32 : using ToConnectCb = std::function<bool(const NodeId&)>;
33 : using OnConnectionChanged = std::function<void(bool ok)>;
34 :
35 : public:
36 : explicit SwarmManager(const NodeId&, const std::mt19937_64& rand, ToConnectCb&& toConnectCb);
37 : ~SwarmManager();
38 :
39 : NeedSocketCb needSocketCb_;
40 :
41 6226 : std::weak_ptr<SwarmManager> weak() { return weak_from_this(); }
42 :
43 : /**
44 : * Get swarm manager id
45 : * @return NodeId
46 : */
47 2614 : const NodeId& getId() const { return id_; }
48 :
49 : /**
50 : * Set list of nodes to the routing table known_nodes
51 : * @param known_nodes
52 : * @return if some are new
53 : */
54 : bool setKnownNodes(const std::vector<NodeId>& known_nodes);
55 :
56 : /**
57 : * Set list of nodes to the routing table mobile_nodes
58 : * @param mobile_nodes
59 : */
60 : void setMobileNodes(const std::vector<NodeId>& mobile_nodes);
61 :
62 : /**
63 : * Add channel to routing table
64 : * @param channel
65 : */
66 : void addChannel(const std::shared_ptr<dhtnet::ChannelSocketInterface>& channel);
67 :
68 : /**
69 : * Remove channel from routing table
70 : * @param channel
71 : */
72 : void removeNode(const NodeId& nodeId);
73 :
74 : /**
75 : * Change mobility of specific node
76 : * @param nodeId
77 : * @param isMobile
78 : */
79 : void changeMobility(const NodeId& nodeId, bool isMobile);
80 :
81 : /**
82 : * Check if swarm manager is connected
83 : * @return true if the swarm has at least one connected node, false if not
84 : */
85 : bool isConnected() const;
86 :
87 : /**
88 : * get all nodes from the different tables in bucket
89 : */
90 : std::vector<NodeId> getAllNodes() const;
91 :
92 : std::vector<NodeId> getConnectedNodes() const;
93 :
94 : std::vector<std::map<std::string, std::string>> getRoutingTableInfo() const;
95 :
96 452 : unsigned getActiveNodesCount() const { return routing_table.getActiveNodesCount(); }
97 :
98 : /**
99 : * Delete nodes from the different tables in bucket
100 : */
101 : void deleteNode(const std::vector<NodeId>& nodes);
102 :
103 : // For tests
104 :
105 : /**
106 : * Get routing table
107 : * @return RoutingTable
108 : */
109 229 : RoutingTable& getRoutingTable() { return routing_table; };
110 :
111 : /**
112 : * Get buckets of routing table
113 : * @return buckets list
114 : */
115 : std::list<Bucket>& getBuckets() { return routing_table.getBuckets(); };
116 :
117 : /**
118 : * Shutdown swarm manager
119 : */
120 : void shutdown();
121 :
122 : /**
123 : * Restart the swarm manager.
124 : *
125 : * This function must be called in situations where we want
126 : * to use a swarm manager that was previously shut down.
127 : */
128 : void restart();
129 :
130 : /**
131 : * Display swarm manager info
132 : */
133 : void display()
134 : {
135 : JAMI_DEBUG("SwarmManager {:s} has {:d} nodes in table [P = {}]",
136 : getId().to_c_str(),
137 : routing_table.getNodeCount(),
138 : isMobile_);
139 : // print nodes of routingtable
140 : for (auto& bucket : routing_table.getBuckets()) {
141 : for (auto& node : bucket.getNodes()) {
142 : JAMI_DEBUG("Node {:s}", node.first.toString());
143 : }
144 : }
145 : }
146 :
147 : /*
148 : * Callback for connection changed
149 : * @param cb
150 : */
151 512 : void onConnectionChanged(OnConnectionChanged cb) { onConnectionChanged_ = std::move(cb); }
152 :
153 : /**
154 : * Set mobility of swarm manager
155 : * @param isMobile
156 : */
157 163 : void setMobility(bool isMobile) { isMobile_ = isMobile; }
158 :
159 : /**
160 : * Get mobility of swarm manager
161 : * @return true if mobile, false if not
162 : */
163 9 : bool isMobile() const { return isMobile_; }
164 :
165 : /**
166 : * Maintain/Update buckets
167 : * @param toConnect Nodes to connect
168 : */
169 : void maintainBuckets(const std::set<NodeId>& toConnect = {});
170 :
171 : /**
172 : * Proactively connect to a node, bypassing bucket capacity checks.
173 : * The node is registered as known and a connection is attempted with
174 : * noNewSocket=true (reuses an existing transport).
175 : * @param nodeId
176 : */
177 : void connectNode(const NodeId& nodeId);
178 :
179 : /**
180 : * Check if swarm manager is shutdown
181 : * @return true if shutdown, false if not
182 : */
183 1045 : bool isShutdown() { return isShutdown_; };
184 :
185 : private:
186 : /**
187 : * Check if we're connected with a specific device
188 : * @param deviceId
189 : * @return true if connected, false if not
190 : */
191 : bool isConnectedWith(const NodeId& deviceId);
192 :
193 : /**
194 : * Add node to the known_nodes list
195 : * @param nodeId
196 : * @return if node inserted
197 : */
198 : bool addKnownNode(const NodeId& nodeId);
199 :
200 : /**
201 : * Add node to the mobile_Nodes list
202 : * @param nodeId
203 : */
204 : void addMobileNodes(const NodeId& nodeId);
205 :
206 : /**
207 : * Send nodes request to fill known_nodes list
208 : * @param socket
209 : * @param nodeId
210 : * @param q
211 : * @param numberNodes
212 : */
213 : void sendRequest(const std::shared_ptr<dhtnet::ChannelSocketInterface>& socket,
214 : const NodeId& nodeId,
215 : Query q,
216 : int numberNodes = Bucket::BUCKET_MAX_SIZE);
217 :
218 : /**
219 : * Send answer to request
220 : * @param socket
221 : * @param msg
222 : */
223 : void sendAnswer(const std::shared_ptr<dhtnet::ChannelSocketInterface>& socket, const Message& msg_);
224 :
225 : /**
226 : * Interpret received message
227 : * @param socket
228 : */
229 : void receiveMessage(const std::shared_ptr<dhtnet::ChannelSocketInterface>& socket);
230 :
231 : /**
232 : * Reset node's timer expiry
233 : * @param ec
234 : * @param socket
235 : * @param node
236 : */
237 : void resetNodeExpiry(const asio::error_code& ec,
238 : const std::shared_ptr<dhtnet::ChannelSocketInterface>& socket,
239 : NodeId node = {});
240 :
241 : /**
242 : * Try to establish connection with specific node
243 : * @param nodeId
244 : * @param noNewSocket If true, reuse an existing transport (no new ICE negotiation)
245 : */
246 : void tryConnect(const NodeId& nodeId, bool noNewSocket = false);
247 :
248 : /**
249 : * Remove node from routing table
250 : * @param nodeId
251 : */
252 : void removeNodeInternal(const NodeId& nodeId);
253 :
254 : const NodeId id_;
255 : bool isMobile_ {false};
256 : std::mt19937_64 rd;
257 : mutable std::mutex mutex;
258 : RoutingTable routing_table;
259 :
260 : std::atomic_bool isShutdown_ {false};
261 :
262 : OnConnectionChanged onConnectionChanged_ {};
263 :
264 : ToConnectCb toConnectCb_;
265 : };
266 :
267 : } // namespace jami
|