mavtables  0.2.1
MAVLink router and firewall.
PacketVersion2.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 <cstddef>
19 #include <cstdint>
20 #include <memory>
21 #include <sstream>
22 #include <stdexcept>
23 #include <string>
24 #include <vector>
25 
26 #include "InvalidPacketIDError.hpp"
27 #include "mavlink.hpp"
28 #include "PacketVersion2.hpp"
29 
30 
31 namespace packet_v2
32 {
33 
34  /** \copydoc ::Packet::Packet(std::vector<uint8_t> data)
35  *
36  * \throws std::invalid_argument if packet data does not start with the
37  * magic byte (0xFD).
38  * \throws std::length_error if packet data is either too short or too
39  * long.
40  */
41  Packet::Packet(std::vector<uint8_t> data)
42  : ::Packet(std::move(data))
43  {
44  const std::vector<uint8_t> &packet_data = this->data();
45 
46  // Check that data was given.
47  if (packet_data.empty())
48  {
49  throw std::length_error("Packet is empty.");
50  }
51 
52  // Check that a complete header was given (including magic number).
53  if (!header_complete(packet_data))
54  {
55  // Could be the magic number.
56  if (START_BYTE != packet_data.front())
57  {
58  std::stringstream ss;
59  ss << "Invalid packet starting byte (0x"
60  << std::uppercase << std::hex
61  << static_cast<unsigned int>(packet_data.front())
62  << std::nouppercase << "), v2.0 packets should start with 0x"
63  << std::uppercase << std::hex
64  << static_cast<unsigned int>(START_BYTE)
65  << std::nouppercase << ".";
66  throw std::invalid_argument(ss.str());
67  }
68  // Otherwise the packet is not long enough.
69  else
70  {
71  throw std::length_error(
72  "Packet (" + std::to_string(packet_data.size()) +
73  " bytes) is shorter than a v2.0 header (" +
74  std::to_string(HEADER_LENGTH) +
75  " bytes).");
76  }
77  }
78 
79  // Verify the message ID.
80  if (mavlink_get_message_info_by_id(header(packet_data)->msgid) ==
81  nullptr)
82  {
83  throw InvalidPacketIDError(header(packet_data)->msgid);
84  }
85 
86  // Ensure a complete packet was given.
87  if (!packet_complete(packet_data))
88  {
89  std::string prefix = "Packet";
90  size_t expected_length =
91  HEADER_LENGTH + header(packet_data)->len + CHECKSUM_LENGTH;
92 
93  if (is_signed(packet_data))
94  {
95  expected_length += SIGNATURE_LENGTH;
96  prefix = "Signed packet";
97  }
98 
99  throw std::length_error(
100  prefix + " is " + std::to_string(packet_data.size()) +
101  " bytes, should be " +
102  std::to_string(expected_length) + " bytes.");
103  }
104  }
105 
106 
107  /** \copydoc ::Packet::version()
108  *
109  * \returns 0x0200 (v2.0) - ::Packet::V2
110  */
112  {
113  return ::Packet::V2;
114  }
115 
116 
117  unsigned long Packet::id() const
118  {
119  return header(data())->msgid;
120  }
121 
122 
123  /** \copydoc ::Packet::name()
124  *
125  * \throws std::runtime_error %If the packet data has an invalid ID.
126  */
127  std::string Packet::name() const
128  {
129  if (const mavlink_message_info_t *msg_info =
130  mavlink_get_message_info_by_id(header(data())->msgid))
131  {
132  return std::string(msg_info->name);
133  }
134 
135  // There should never be any way to reach this point since the message
136  // ID was checked in the constructor. It is here just in case the
137  // MAVLink C library has an error in it.
138  // LCOV_EXCL_START
139  throw InvalidPacketIDError(header(data())->msgid);
140  // LCOV_EXCL_STOP
141  }
142 
143 
145  {
146  return MAVAddress(header(data())->sysid, header(data())->compid);
147  }
148 
149 
150  /** \copydoc ::Packet::dest()
151  *
152  * \thanks The [mavlink-router](https://github.com/intel/mavlink-router)
153  * project for an example of how to extract the destination address.
154  */
155  std::optional<MAVAddress> Packet::dest() const
156  {
157  if (const mavlink_msg_entry_t *msg_entry =
158  mavlink_get_msg_entry(header(data())->msgid))
159  {
160  int dest_system = -1;
161  int dest_component = 0;
162 
163  // Extract destination system.
164  if (msg_entry->flags & MAV_MSG_ENTRY_FLAG_HAVE_TARGET_SYSTEM)
165  {
166  // Must check to make sure the target system offset is within
167  // the packet payload because it can be striped out in v2.0
168  // packets if it is 0.
169  if (msg_entry->target_system_ofs < header(data())->len)
170  {
171  // target_system_ofs is offset from start of payload
172  size_t offset = msg_entry->target_system_ofs +
173  sizeof(mavlink::v2_header);
174  dest_system = data()[offset];
175  }
176  else
177  {
178  dest_system = 0;
179  }
180  }
181 
182  // Extract destination component.
183  if (msg_entry->flags & MAV_MSG_ENTRY_FLAG_HAVE_TARGET_COMPONENT)
184  {
185  // Must check to make sure the target component offset is within
186  // the packet payload because it can be striped out in v2.0
187  // packets if it is 0.
188  if (msg_entry->target_component_ofs < header(data())->len)
189  {
190  // target_compoent_ofs is offset from start of payload
191  size_t offset = msg_entry->target_component_ofs +
192  sizeof(mavlink::v2_header);
193  dest_component = data()[offset];
194  }
195  else
196  {
197  dest_component = 0;
198  }
199  }
200 
201  // Construct MAVLink address.
202  if (dest_system >= 0)
203  {
204  return MAVAddress(static_cast<unsigned int>(dest_system),
205  static_cast<unsigned int>(dest_component));
206  }
207 
208  // No destination address.
209  return {};
210  }
211 
212  // There should never be any way to reach this point since the message
213  // ID was checked in the constructor. It is here just in case the
214  // MAVLink C library has an error in it.
215  // LCOV_EXCL_START
216  throw InvalidPacketIDError(header(data())->msgid);
217  // LCOV_EXCL_STOP
218  }
219 
220 
221  /** Determine if a MAVLink v2.0 packet is signed or not.
222  *
223  * \relates packet_v2::Packet
224  * \param data The packet data.
225  * \throws std::invalid_argument Header is not complete or is invalid.
226  */
227  bool is_signed(const std::vector<uint8_t> &data)
228  {
229  // Check that a complete header was given (including magic number).
230  if (!header_complete(data))
231  {
232  throw std::invalid_argument("Header is incomplete or invalid.");
233  }
234 
235  return (header(data)->incompat_flags & MAVLINK_IFLAG_SIGNED);
236  }
237 
238 
239  /** Determine if the given data contains a complete v2.0 header.
240  *
241  * \relates packet_v2::Packet
242  * \param data The packet data.
243  * \retval true if \p data contains a complete header (starting with the
244  * magic byte).
245  * \retval false if \p data does not contain a complete v2.0 header.
246  */
247  bool header_complete(const std::vector<uint8_t> &data)
248  {
249  return (data.size() >= HEADER_LENGTH) &&
250  (START_BYTE == data.front());
251  }
252 
253 
254  /** Determine if the given data contains a complete v2.0 packet.
255  *
256  * \relates packet_v2::Packet
257  * \param data The packet data.
258  * \retval true if \p data contains a complete packet (starting with the
259  * magic byte).
260  * \retval false if \p data does not contain a complete v1.0 packet, or if
261  * there is extra bytes in \p data beyond the packet.
262  */
263  bool packet_complete(const std::vector<uint8_t> &data)
264  {
265  if (header_complete(data))
266  {
267  size_t expected_length =
269 
270  if (header(data)->incompat_flags & MAVLINK_IFLAG_SIGNED)
271  {
272  expected_length += SIGNATURE_LENGTH;
273  }
274 
275  return data.size() == expected_length;
276  }
277 
278  return false;
279  }
280 
281 
282  /** Cast data as a v2.0 packet header structure pointer.
283  *
284  * \relates packet_v2::Packet
285  * \param data The packet data.
286  * \returns A pointer to the given data, cast to a v2.0 header structure.
287  * %If an incomplete header is given a nullptr will be returned.
288  */
289  const struct mavlink::v2_header *header(
290  const std::vector<uint8_t> &data)
291  {
292  if (header_complete(data))
293  {
294  return reinterpret_cast <
295  const struct mavlink::v2_header * >(&(data[0]));
296  }
297 
298  return nullptr;
299  }
300 
301 }
const size_t CHECKSUM_LENGTH
bool header_complete(const std::vector< uint8_t > &data)
STL namespace.
virtual ::Packet::Version version() const
const struct mavlink::v2_header * header(const std::vector< uint8_t > &data)
const size_t HEADER_LENGTH
const struct mavlink::v2_header * header(const std::vector< uint8_t > &data)
const size_t SIGNATURE_LENGTH
bool is_signed(const std::vector< uint8_t > &data)
bool header_complete(const std::vector< uint8_t > &data)
Version
Definition: Packet.hpp:47
virtual std::string name() const
virtual MAVAddress source() const
bool packet_complete(const std::vector< uint8_t > &data)
const uint8_t START_BYTE
Packet(const Packet &other)=default
virtual std::optional< MAVAddress > dest() const
const std::vector< uint8_t > & data() const
Definition: Packet.cpp:52
virtual unsigned long id() const