mavtables  0.2.1
MAVLink router and firewall.
MAVSubnet.cpp
Go to the documentation of this file.
1 // MAVLink router and firewall.
2 // Copyright (C) 2017-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 <exception>
19 #include <sstream>
20 #include <stdexcept>
21 #include <string>
22 #include <vector>
23 
24 #include <boost/tokenizer.hpp>
25 
26 #include "MAVAddress.hpp"
27 #include "MAVSubnet.hpp"
28 
29 
30 /** Construct a MAVLink subnet from a MAVLink address and mask.
31  *
32  * \param address MAVLink address of subnet.
33  * \param mask Two byte subnet mask, where the system mask is in the MSB and
34  * the component mask is in the LSB.
35  * \throws std::out_of_range if the mask is not between 0x0000 and 0xFFFF.
36  * \sa Check if a \ref MAVAddress is within the subnet with \ref contains.
37  */
38 MAVSubnet::MAVSubnet(const MAVAddress &address, unsigned int mask)
39  : address_(address)
40 {
41  // Ensure mask is withing range.
42  if (mask > 0xFFFF)
43  {
44  std::ostringstream ss;
45  ss << "mask (0x"
46  << std::uppercase << std::hex << mask << std::nouppercase
47  << ") is outside of the allowed range (0x0000 - 0xFFFF).";
48  throw std::out_of_range(ss.str());
49  }
50 
51  mask_ = mask;
52 }
53 
54 
55 /** Construct MAVLink subnet from an address, system mask, and component mask.
56  *
57  * \param address MAVLink address of subnet.
58  * \param system_mask One byte subnet system mask with the bits set that must
59  * match the subnet address for a MAVLink address to be contained within
60  * the subnet. Valid range is 0 to 255.
61  * \param component_mask One byte subnet component mask with the bits set that
62  * must match the subnet address for a MAVLink address to be contained
63  * within the subnet. Valid range is 0 to 255.
64  * \throws std::out_of_range if the system and component masks are not each
65  * between 0x00 and 0xFF.
66  * \sa Check if a \ref MAVAddress is within the subnet with \ref contains.
67  */
68 MAVSubnet::MAVSubnet(const MAVAddress &address, unsigned int system_mask,
69  unsigned int component_mask)
70  : address_(address)
71 {
72  // Ensure system mask is withing range.
73  if (system_mask > 0xFF)
74  {
75  std::ostringstream ss;
76  ss << "System mask (0x"
77  << std::uppercase << std::hex << system_mask << std::nouppercase
78  << ") is outside of the allowed range (0x00 - 0xFF).";
79  throw std::out_of_range(ss.str());
80  }
81 
82  // Ensure component mask is withing range.
83  if (component_mask > 0xFF)
84  {
85  std::ostringstream ss;
86  ss << "Component mask (0x" << std::hex << component_mask <<
87  ") is outside of the allowed range (0x00 - 0xFF).";
88  throw std::out_of_range(ss.str());
89  }
90 
91  mask_ = ((system_mask << 8) & 0xFF00) | (component_mask & 0x00FF);
92 }
93 
94 
95 /** Construct a MAVLink subnet from a string.
96  *
97  * There are four string forms of MAVLink subnets:
98  *
99  * 1. "<System ID>.<Component ID>:<System ID mask>.<Component ID mask>"
100  * 2. "<System ID>.<Component ID>/<bits>"
101  * 3. "<System ID>.<Component ID><bits>"
102  * 4. "<System ID>.<Component ID>"
103  *
104  * The first form is self explanatory, but the 2nd and 3rd are not as simple.
105  * In the 2nd case the number of bits (0 - 16) is the number of bits from the
106  * left that must match for an address to be in the subnet. The 3rd form is
107  * like the 2nd, but does not require the system ID (first octet) to match and
108  * starts with the number of bits of the component ID (0 - 8) that must match
109  * from the left for an address to be in the subnet. The last form is
110  * shorthand for "<System ID>.<Component ID>/16", that is an exact match.
111  *
112  * Below is a table relating the slash postfix to the subnet mask in <System
113  * mask>.<Component mask> notation.
114  *
115  * | Mask with `/` | Mask with `\` | Postfix (\#bits) |
116  * | -------------:| -------------:| ----------------:|
117  * | 255.255 | out of range | 16 |
118  * | 255.254 | out of range | 15 |
119  * | 255.252 | out of range | 14 |
120  * | 255.248 | out of range | 13 |
121  * | 255.240 | out of range | 12 |
122  * | 255.224 | out of range | 11 |
123  * | 255.192 | out of range | 10 |
124  * | 255.128 | out of range | 9 |
125  * | 255.0 | 0.255 | 8 |
126  * | 254.0 | 0.254 | 7 |
127  * | 252.0 | 0.252 | 6 |
128  * | 248.0 | 0.248 | 5 |
129  * | 240.0 | 0.240 | 4 |
130  * | 224.0 | 0.224 | 3 |
131  * | 192.0 | 0.192 | 2 |
132  * | 128.0 | 0.128 | 1 |
133  * | 0.0 | 0 | 0 |
134  *
135  * Some examples are:
136  *
137  * - "128.0/8" - Matches addresses with system ID 128 and any component ID.
138  * - "128.0/9" - Matches addresses with system ID 128 and components ID's of
139  * 127 or less.
140  * - "128.255/9" - Matches addresses with system ID 128 and components ID's
141  * of 128 or more.
142  * - "128.0\1" - Matches addresses any system ID and components ID's of 127
143  * or less.
144  * - "128.255\1" - Matches addresses any system ID and components ID's of 128
145  * or more.
146  * - "255.0:128.240" - Matches system ID's 128 or greater and component ID's
147  * from 0 to 15.
148  * - "255.16:128.240" - Matches system ID's 128 or greater and component ID's
149  * from 16 to 31.
150  * - "255.16" - Matches only the address with system ID 255 and component ID
151  * 16.
152  *
153  * \param subnet String representing the MAVLink subnet.
154  * \throws std::out_of_range if either the system ID or the component ID is out
155  * of range.
156  * \throws std::out_of_range if the mask or slash bits are out of range.
157  * \throws std::invalid_argument if the subnet string does not represent a
158  * valid MAVLink subnet.
159  * \sa Check if a \ref MAVAddress is within the subnet with \ref contains.
160  */
161 MAVSubnet::MAVSubnet(std::string subnet)
162  : address_(0)
163 {
164  // If only an address was given (exact match subnet).
165  try
166  {
167  address_ = MAVAddress(subnet);
168  mask_ = 0xFFFF;
169  return;
170  }
171  catch (std::exception)
172  {
173  // Continue on parsing normally.
174  }
175 
176  // Extract parts of subnet string.
177  std::vector<std::string> parts;
178  boost::char_separator<char> sep("", ":/\\");
179  boost::tokenizer<boost::char_separator<char>> tokens(subnet, sep);
180 
181  for (auto i : tokens)
182  {
183  parts.push_back(i);
184  }
185 
186  // Ensure proper format.
187  if (parts.size() != 3)
188  {
189  throw std::invalid_argument(
190  "Invalid MAVLink subnet: \"" + subnet + "\".");
191  }
192 
193  address_ = MAVAddress(parts[0]);
194  // Determine format of subnet string.
195  int slashmask;
196 
197  // If a regular subnet was given.
198  switch (parts[1].at(0))
199  {
200  // Mask based subnet.
201  case ':':
202  try
203  {
204  mask_ = MAVAddress(parts[2]).address();
205  }
206  catch (std::invalid_argument)
207  {
208  throw std::invalid_argument(
209  "Invalid MAVLink subnet: \"" + subnet + "\".");
210  }
211 
212  break;
213 
214  // Forward slash based subnet (bits from left).
215  case '/':
216  std::istringstream(parts.at(2)) >> slashmask;
217 
218  if (slashmask < 0 || slashmask > 16)
219  {
220  throw std::out_of_range(
221  "Forward slash mask (" + std::to_string(slashmask)
222  + ") is outside of allowed range (0 - 16).");
223  }
224 
225  mask_ = (0xFFFF << (16 - slashmask)) & 0xFFFF;
226  break;
227 
228  // Backward slash based subnet (bits from left of component).
229  case '\\':
230  std::istringstream(parts.at(2)) >> slashmask;
231 
232  if (slashmask < 0 || slashmask > 8)
233  {
234  throw std::out_of_range(
235  "Backslash mask (" + std::to_string(slashmask)
236  + ") is outside of allowed range (0 - 8).");
237  }
238 
239  mask_ = (0xFFFF << (8 - slashmask)) & 0x00FF;
240  break;
241  }
242 }
243 
244 
245 /** Determine whether or not the subnet contains a given MAVLink address.
246  *
247  * \param address The MAVLink address to test.
248  * \retval true if \p address is part of the subnet.
249  */
250 bool MAVSubnet::contains(const MAVAddress &address) const
251 {
252  return (address.address() & mask_) == (address_.address() & mask_);
253 }
254 
255 
256 /** Equality comparison.
257  *
258  * Compares both address and mask.
259  *
260  * \relates MAVSubnet
261  * \param lhs The left hand side MAVLink subnet.
262  * \param rhs The right hand side MAVLink subnet.
263  * \retval true if \p lhs and \p rhs are the same.
264  * \retval false if \p lhs and \p rhs are not the same.
265  */
266 bool operator==(const MAVSubnet &lhs, const MAVSubnet &rhs)
267 {
268  return (lhs.address_ == rhs.address_) && (lhs.mask_ == rhs.mask_);
269 }
270 
271 
272 /** Inequality comparison.
273  *
274  * Compares both address and mask.
275  *
276  * \relates MAVSubnet
277  * \param lhs The left hand side MAVLink subnet.
278  * \param rhs The right hand side MAVLink subnet.
279  * \retval true if \p lhs and \p rhs are not the same.
280  * \retval false if \p lhs and \p rhs are the same.
281  */
282 bool operator!=(const MAVSubnet &lhs, const MAVSubnet &rhs)
283 {
284  return (lhs.address_ != rhs.address_) || (lhs.mask_ != rhs.mask_);
285 }
286 
287 
288 /** Print the MAVLink subnet to the given output stream.
289  *
290  * There are three string forms of MAVLink subnets.
291  *
292  * 1. "<System ID>.<Component ID>:<System ID mask>.<Component ID mask>"
293  * 2. "<System ID>.<Component ID>/<bits>"
294  * 3. "<System ID>.<Component ID><bits>"
295  * 4. "<System ID>.<Component ID>"
296  *
297  * The slash notation is preferred. The last form is used when the mask
298  * requires all bits of a subnet to match an address.
299  *
300  * See \ref MAVSubnet::MAVSubnet(std::string address) for more information on
301  * the string format.
302  *
303  * \note The string constructor \ref MAVSubnet(std::string) and the output
304  * stream operator are not inverses because of form preferences:
305  *
306  * \relates MAVSubnet
307  * \param os The output stream to print to.
308  * \param mavsubnet The MAVLink subnet to print.
309  * \returns The output stream.
310  */
311 std::ostream &operator<<(std::ostream &os, const MAVSubnet &mavsubnet)
312 {
313  os << mavsubnet.address_;
314 
315  switch (mavsubnet.mask_)
316  {
317  case 0b1111111111111111:
318  // return os << "/16"
319  // Represent exact masks as an address.
320  return os;
321 
322  case 0b1111111111111110:
323  return os << "/15";
324 
325  case 0b1111111111111100:
326  return os << "/14";
327 
328  case 0b1111111111111000:
329  return os << "/13";
330 
331  case 0b1111111111110000:
332  return os << "/12";
333 
334  case 0b1111111111100000:
335  return os << "/11";
336 
337  case 0b1111111111000000:
338  return os << "/10";
339 
340  case 0b1111111110000000:
341  return os << "/9";
342 
343  case 0b1111111100000000:
344  return os << "/8";
345 
346  case 0b1111111000000000:
347  return os << "/7";
348 
349  case 0b1111110000000000:
350  return os << "/6";
351 
352  case 0b1111100000000000:
353  return os << "/5";
354 
355  case 0b1111000000000000:
356  return os << "/4";
357 
358  case 0b1110000000000000:
359  return os << "/3";
360 
361  case 0b1100000000000000:
362  return os << "/2";
363 
364  case 0b1000000000000000:
365  return os << "/1";
366 
367  case 0b0000000000000000:
368  return os << "/0";
369 
370  case 0b0000000011111111:
371  return os << "\\8";
372 
373  case 0b0000000011111110:
374  return os << "\\7";
375 
376  case 0b0000000011111100:
377  return os << "\\6";
378 
379  case 0b0000000011111000:
380  return os << "\\5";
381 
382  case 0b0000000011110000:
383  return os << "\\4";
384 
385  case 0b0000000011100000:
386  return os << "\\3";
387 
388  case 0b0000000011000000:
389  return os << "\\2";
390 
391  case 0b0000000010000000:
392  return os << "\\1";
393 
394  default:
395  return os << ":" << MAVAddress(mavsubnet.mask_);
396  }
397 }
MAVSubnet(const MAVSubnet &other)=default
std::ostream & operator<<(std::ostream &os, const Action &action)
Definition: Action.cpp:188
unsigned int address() const
Definition: MAVAddress.cpp:151
bool operator==(const Action &lhs, const Action &rhs)
Definition: Action.cpp:154
bool operator!=(const Action &lhs, const Action &rhs)
Definition: Action.cpp:168
bool contains(const MAVAddress &address) const
Definition: MAVSubnet.cpp:250