mavtables  0.2.1
MAVLink router and firewall.
Connection.cpp
Go to the documentation of this file.
1 // MAVLink router and firewall.
2 // Copyright (C) 2018 Michael R. Shannon <mrshannon.aerospace@gmail.com>
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 2 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 <http://www.gnu.org/licenses/>.
16 
17 
18 #include <algorithm>
19 #include <memory>
20 #include <sstream>
21 #include <stdexcept>
22 #include <utility>
23 
24 #include "AddressPool.hpp"
25 #include "Connection.hpp"
26 #include "Filter.hpp"
27 #include "Logger.hpp"
28 #include "MAVAddress.hpp"
29 #include "Packet.hpp"
30 #include "PacketQueue.hpp"
31 #include "utility.hpp"
32 
33 
34 /** Log an accepted/rejected packet to the \ref Logger.
35  *
36  * \param accept Set to true if the packet is accepted, false if the packet is
37  * rejected.
38  * \param packet The packet that is to be accepted/rejected.
39  */
40 void Connection::log_(bool accept, const Packet &packet)
41 {
42  if (Logger::level() >= 3)
43  {
44  std::stringstream ss;
45  ss << (accept ? "accepted " : "rejected ")
46  << packet << " source ";
47  auto connection = packet.connection();
48 
49  if (connection == nullptr)
50  {
51  ss << "unknown";
52  }
53  else
54  {
55  ss << *connection;
56  }
57 
58  ss << " dest " << name_;
59  Logger::log(3, ss.str());
60  }
61 }
62 
63 
64 /** Send a packet to a particular address.
65  *
66  * If the particular address cannot be found it will be sent to every component
67  * of the system it was sent to.
68  *
69  * Packets are ran through the contained \ref Filter before being placed into
70  * the \ref PacketQueue given in the constructor. Packets are read from the
71  * queue (for sending) by using the \ref next_packet method.
72  *
73  * \note This disregards the destination address of the packet.
74  *
75  * \param packet The packet to send.
76  * \param dest The address to send the packet to, if this address is not
77  * handled by this connection the packet will be silently dropped.
78  */
79 void Connection::send_to_address_(
80  std::shared_ptr<const Packet> packet, const MAVAddress &dest)
81 {
82  // Address reachable on this connection.
83  if (pool_->contains(dest))
84  {
85  // Run packet/address combination through the filter.
86  auto [accept, priority] = filter_->will_accept(*packet, dest);
87 
88  // Add packet to the queue.
89  if (accept)
90  {
91  log_(true, *packet);
92  queue_->push(std::move(packet), priority);
93  }
94  else
95  {
96  log_(false, *packet);
97  }
98  }
99  // If the component is not reachable, send it to all components on the
100  // system.
101  else
102  {
103  bool system_found = false;
104 
105  // Loop over addresses.
106  for (const auto &addr : pool_->addresses())
107  {
108  // System can be reached on connection.
109  if (addr.system() == dest.system())
110  {
111  system_found = true;
112  auto [accept, priority] = filter_->will_accept(*packet, dest);
113 
114  if (accept)
115  {
116  log_(true, *packet);
117  queue_->push(std::move(packet), priority);
118  return;
119  }
120  }
121  }
122 
123  if (system_found)
124  {
125  log_(false, *packet);
126  }
127  }
128 }
129 
130 
131 /** Send a packet to every address reachable on the connection.
132  *
133  * Packets are ran through the contained \ref Filter before being placed into
134  * the \ref PacketQueue given in the constructor. Packets are read from the
135  * queue (for sending) by using the \ref next_packet method.
136  *
137  * \note This disregards the destination address of the packet.
138  *
139  * \param packet The packet to send. Cannot be nullptr.
140  */
141 void Connection::send_to_all_(std::shared_ptr<const Packet> packet)
142 {
143  bool accept = false;
144  int priority = std::numeric_limits<int>::min();
145 
146  // Loop over addresses.
147  for (const auto &addr : pool_->addresses())
148  {
149  // Filter packet/address combination.
150  auto [accept_, priority_] = filter_->will_accept(*packet, addr);
151 
152  // Update accept/priority.
153  if (accept_)
154  {
155  accept = accept_;
156  priority = std::max(priority, priority_);
157  }
158  }
159 
160  // Add packet to the queue.
161  if (accept)
162  {
163  log_(true, *packet);
164  queue_->push(std::move(packet), priority);
165  }
166  else
167  {
168  log_(false, *packet);
169  }
170 }
171 
172 
173 /** Send a packet to every component of a system reachable on the connection.
174  *
175  * Packets are ran through the contained \ref Filter before being placed into
176  * the \ref PacketQueue given in the constructor. Packets are read from the
177  * queue (for sending) by using the \ref next_packet method.
178  *
179  * \note This disregards the destination address of the packet.
180  *
181  * \param packet The packet to send.
182  */
183 void Connection::send_to_system_(
184  std::shared_ptr<const Packet> packet, unsigned int system)
185 {
186  bool system_found = false;
187  bool accept = false;
188  int priority = std::numeric_limits<int>::min();
189 
190  // Loop over addresses.
191  for (const auto &addr : pool_->addresses())
192  {
193  if (system == addr.system())
194  {
195  system_found = true;
196  // Filter packet/address combination.
197  auto [accept_, priority_] = filter_->will_accept(*packet, addr);
198 
199  // Update accept/priority.
200  if (accept_)
201  {
202  accept = accept_;
203  priority = std::max(priority, priority_);
204  }
205  }
206  }
207 
208  // Add packet to the queue.
209  if (system_found)
210  {
211  if (accept)
212  {
213  log_(true, *packet);
214  queue_->push(std::move(packet), priority);
215  }
216  else
217  {
218  log_(false, *packet);
219  }
220  }
221 }
222 
223 
224 /** Construct a connection.
225  *
226  * \param name The name of the connection, should be the device string for a
227  * serial connection or the IP address and port number for a UDP
228  * connection.
229  * \param filter The packet filter to use for determining whether and with what
230  * priority to add a packet to the queue for transmission.
231  * \param mirror Set to true if this is to be a mirror connection. A mirror
232  * connection is one that will receive all packets, regardless of
233  * destination address. The default is false.
234  * \param pool The \ref AddressPool to use for keeping track of the addresses
235  * reachable by the connection. A default address pool will be used if
236  * none is given.
237  * \param queue The \ref PacketQueue to use to hold packets awaiting
238  * transmission. A default packet queue will be used if none is given.
239  * \throws std::invalid_argument if the given any of the \p filter, \p pool, or
240  * \p queue pointers are null.
241  * \remarks If the given \ref AddressPool and \ref PacketQueue are threadsafe
242  * then the connection will also be threadsafe.
243  */
245  std::string name,
246  std::shared_ptr<Filter> filter, bool mirror,
247  std::unique_ptr<AddressPool<>> pool,
248  std::unique_ptr<PacketQueue> queue)
249  : name_(std::move(name)),
250  filter_(std::move(filter)), pool_(std::move(pool)),
251  queue_(std::move(queue)), mirror_(mirror)
252 {
253  if (filter_ == nullptr)
254  {
255  throw std::invalid_argument("Given filter pointer is null.");
256  }
257 
258  if (pool_ == nullptr)
259  {
260  throw std::invalid_argument("Given pool pointer is null.");
261  }
262 
263  if (queue_ == nullptr)
264  {
265  throw std::invalid_argument("Given queue pointer is null.");
266  }
267 }
268 
269 
270 /** Add a MAVLink address to the connection.
271  *
272  * This adds an address to the list of systems/components that can be reached
273  * on this connection.
274  *
275  * \note Addresses will be removed after the timeout set in the \ref
276  * AddressPool given in the constructor. Re-adding the address (even
277  * before this time runs out) will reset the timeout.
278  *
279  * \param address The MAVLink address to add, or update the timeout for.
280  */
282 {
283  if (Logger::level() >= 1 && !pool_->contains(address))
284  {
285  Logger::log(1, "new component " + str(address) + " on " + name_);
286  }
287 
288  pool_->add(std::move(address));
289 }
290 
291 
292 
293 /** Get next packet to send.
294  *
295  * Blocks until a packet is ready to be sent or the \p timeout expires.
296  * Returns nullptr in the later case.
297  *
298  * \param timeout How long to block waiting for a packet. Set to 0s for non
299  * blocking.
300  * \returns The next packet to send. Or nullptr if the call times out waiting
301  * on a packet.
302  */
303 std::shared_ptr<const Packet> Connection::next_packet(
304  const std::chrono::nanoseconds &timeout)
305 {
306  return queue_->pop(timeout);
307 }
308 
309 
310 /** Send a packet out on the connection.
311  *
312  * Packets are ran through the contained \ref Filter before being placed into
313  * the \ref PacketQueue given in the constructor. Packets are read from the
314  * queue (for sending) by using the \ref next_packet method.
315  *
316  * \note %If the packet has a destination address that is not 0.0 (the
317  * broadcast address) it will only be sent if that system is reachable on
318  * this connection. It will still be sent even if the particular component
319  * cannot be found.
320  *
321  * \note %If this is a mirror connection then the destination address of the
322  * packet is ignored.
323  *
324  * \param packet The packet to send.
325  * \throws std::invalid_argument if the \p packet pointer is null.
326  */
327 void Connection::send(std::shared_ptr<const Packet> packet)
328 {
329  if (packet == nullptr)
330  {
331  throw std::invalid_argument("Given packet pointer is null.");
332  }
333 
334  // Drop packet if the source is reachable on this connection.
335  if (pool_->contains(packet->source()))
336  {
337  // log_(false, *packet);
338  return;
339  }
340 
341  auto dest = packet->dest();
342 
343  // Broadcast to all.
344  if (!dest.has_value() || dest.value() == MAVAddress(0, 0) || mirror_)
345  {
346  send_to_all_(std::move(packet));
347  }
348  // Broadcast to all components of given system.
349  else if (dest->component() == 0)
350  {
351  send_to_system_(std::move(packet), dest->system());
352  }
353  // Send to particular system and component.
354  else
355  {
356  send_to_address_(std::move(packet), dest.value());
357  }
358 }
359 
360 
361 /** Print the connection name to the given output stream.
362  *
363  * Some examples are:
364  * - `/dev/ttyUSB0`
365  * - `127.0.0.1:8000`
366  * - `127.0.0.1:14550`
367  */
368 std::ostream &operator<<(std::ostream &os, const Connection &connection)
369 {
370  os << connection.name_;
371  return os;
372 }
unsigned int component() const
Definition: MAVAddress.cpp:171
std::string str(const T &object)
Definition: utility.hpp:128
static void log(std::string message)
Definition: Logger.cpp:37
unsigned int system() const
Definition: MAVAddress.cpp:161
Connection(std::string name, std::shared_ptr< Filter > filter, bool mirror=false, std::unique_ptr< AddressPool<>> pool=std::make_unique< AddressPool<>>(), std::unique_ptr< PacketQueue > queue=std::make_unique< PacketQueue >())
Definition: Connection.cpp:244
STL namespace.
void connection(std::weak_ptr< Connection > connection)
Definition: Packet.cpp:63
std::ostream & operator<<(std::ostream &os, const Action &action)
Definition: Action.cpp:188
static unsigned int level()
Definition: Logger.cpp:96
TEST_VIRTUAL std::shared_ptr< const Packet > next_packet(const std::chrono::nanoseconds &timeout=std::chrono::nanoseconds(0))
Definition: Connection.cpp:303
TEST_VIRTUAL void add_address(MAVAddress address)
Definition: Connection.cpp:281
TEST_VIRTUAL void send(std::shared_ptr< const Packet > packet)
Definition: Connection.cpp:327