/ src / mping_state.cpp
mping_state.cpp
  1  // SPDX-FileCopyrightText: Copyright (C) 2025 Marek Küthe <m.k@mk16.de>
  2  //
  3  // SPDX-License-Identifier: GPL-3.0-or-later
  4  
  5  #include "mping_state.hpp"
  6  
  7  using namespace MPingSender;
  8  
  9  MPingSender::MPingState::MPingState() :
 10      _type(MPING_STATE_TYPE::SENDER),
 11      _ttl(0),
 12      _src_host(),
 13      _src_port(0),
 14      _dest_host(),
 15      _dest_port(0),
 16      _sequence_number(0),
 17      _pid(0),
 18      _tv()
 19  {
 20  }
 21  
 22  MPingSender::MPingState::MPingState(const MPING_STATE_TYPE type,
 23                                      const uint8_t ttl,
 24                                      const boost::asio::ip::address src_host,
 25                                      const uint16_t src_port,
 26                                      const boost::asio::ip::address dest_host,
 27                                      const uint16_t dest_port) :
 28      _type(type),
 29      _ttl(ttl),
 30      _src_host(src_host),
 31      _src_port(src_port),
 32      _dest_host(dest_host),
 33      _dest_port(dest_port),
 34      _sequence_number(0),
 35      _tv()
 36  {
 37      static thread_local std::mt19937 generator{std::random_device()()};
 38      using pid_type = decltype(this->_pid);
 39      static thread_local std::uniform_int_distribution<pid_type> distribution(
 40          std::numeric_limits<pid_type>::min(),
 41          std::numeric_limits<pid_type>::max());
 42      this->_pid = distribution(generator);
 43  }
 44  
 45  MPingSender::MPingState::MPingState(
 46      const MPING_STATE_TYPE type,
 47      const uint8_t ttl,
 48      const boost::asio::ip::address src_host,
 49      const uint16_t src_port,
 50      const boost::asio::ip::address dest_host,
 51      const uint16_t dest_port,
 52      const uint32_t sequence_number,
 53      const uint32_t pid,
 54      const std::chrono::time_point<std::chrono::steady_clock> tv) :
 55      _type(type),
 56      _ttl(ttl),
 57      _src_host(src_host),
 58      _src_port(src_port),
 59      _dest_host(dest_host),
 60      _dest_port(dest_port),
 61      _sequence_number(sequence_number),
 62      _pid(pid),
 63      _tv(tv)
 64  {
 65  }
 66  
 67  void MPingSender::MPingState::next_seq_no() noexcept
 68  {
 69      static_assert(
 70          std::numeric_limits<decltype(this->_sequence_number)>::max() ==
 71          4'294'967'295UL);
 72      if (this->_sequence_number ==
 73          std::numeric_limits<decltype(this->_sequence_number)>::max())
 74          [[unlikely]]
 75      {
 76          this->_sequence_number = 0;
 77      }
 78      else [[likely]]
 79      {
 80          this->_sequence_number++;
 81      }
 82  }
 83  
 84  void MPingSender::MPingState::set_current_time() noexcept
 85  {
 86      this->_tv = std::chrono::steady_clock::now();
 87  }
 88  
 89  /*
 90  MPing packet =
 91      0                   1                   2                   3
 92      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 93      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 94      |  Ver  |T|L| 0 |                                               |
 95      |                                                               |
 96      |                     Source Host                               |
 97      |                                                               |
 98      |               |                                               |
 99      |                                                               |
100      |                     Destination Host                          |
101      |                                                               |
102      |               | Seq   | PID   | Seconds       | Microseconds  |
103      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
104  
105      Ver = VERSION = "2.0\0"
106      T = TYPE
107      +----------+-------+
108      | T = TYPE | VALUE |
109      +----------+-------+
110      | SENDER   | "s"   |
111      | RECEIVER | "r"   |
112      +----------+-------+
113      L = TTL
114      Source Host = Host
115      Destination Host = Host
116      Seq = Sequence number
117      PID = PID or Random Number
118      Seconds = Seconds (Monotonic clock)
119      Microseconds = Microseconds (Monotonic clock)
120  
121      IPv6 Host =
122      0                   1                   2                   3
123      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
124      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
125      |10 | P |   0   | IPv6 Address                  | 0             |
126      |                            0                                  |
127      |                            0                                  |
128      |                            0                                  |
129      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
130      10 = 10 (little endian)
131      P = Port
132  
133      IPv4 Host =
134      0                   1                   2                   3
135      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
136      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
137      | 2 | P | IPv4  |                   0                           |
138      |                            0                                  |
139      |                            0                                  |
140      |                            0                                  |
141      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
142      2 = 2 (little endian)
143  
144      otherwise bigendian
145  */
146  
147  std::string MPingSender::MPingState::serialize() const
148  {
149      std::string result;
150      result.reserve(288);
151  
152      /* Ver */
153      constexpr std::array<char, 4> mstate_version =
154          std::to_array(MPING_STATE_VERSION);
155      result.append(mstate_version.begin(), mstate_version.end());
156  
157      /* T */
158      result.push_back(std::to_underlying(this->_type));
159  
160      /* L */
161      auto ttl = NetworkSerialization::to_byte_array(
162          NetworkSerialization::to_bigendian(this->_ttl));
163      result.append(ttl.begin(), ttl.end());
164  
165      constexpr std::array<unsigned char, 2> two_null_characters = {'\0', '\0'};
166      result.append(two_null_characters.begin(), two_null_characters.end());
167  
168      /* Source Host */
169      if (this->_src_host.is_v6()) [[likely]]
170      {
171          const auto ipv6_sockaddr_storage =
172              NetworkSerialization::ipv6_to_sockaddr_storage(
173                  this->_src_host.to_v6(), this->_src_port);
174          result.append(ipv6_sockaddr_storage.begin(),
175                        ipv6_sockaddr_storage.end());
176      }
177      else if (this->_src_host.is_v4()) [[likely]]
178      {
179          const auto ipv4_sockaddr_storage =
180              NetworkSerialization::ipv4_to_sockaddr_storage(
181                  this->_src_host.to_v4(), this->_src_port);
182          result.append(ipv4_sockaddr_storage.begin(),
183                        ipv4_sockaddr_storage.end());
184      }
185      else [[unlikely]]
186      {
187          throw std::invalid_argument("Invalid source address");
188      }
189  
190      /* Destination Host */
191      if (this->_dest_host.is_v6()) [[likely]]
192      {
193          const auto ipv6_sockaddr_storage =
194              NetworkSerialization::ipv6_to_sockaddr_storage(
195                  this->_dest_host.to_v6(), this->_dest_port);
196          result.append(ipv6_sockaddr_storage.begin(),
197                        ipv6_sockaddr_storage.end());
198      }
199      else if (this->_dest_host.is_v4()) [[likely]]
200      {
201          const auto ipv4_sockaddr_storage =
202              NetworkSerialization::ipv4_to_sockaddr_storage(
203                  this->_dest_host.to_v4(), this->_dest_port);
204          result.append(ipv4_sockaddr_storage.begin(),
205                        ipv4_sockaddr_storage.end());
206      }
207      else [[unlikely]]
208      {
209          throw std::invalid_argument("Invalid destination address");
210      }
211  
212      /* Seq */
213      auto seq = NetworkSerialization::to_byte_array(
214          NetworkSerialization::to_bigendian(this->_sequence_number));
215      result.append(seq.begin(), seq.end());
216  
217      /* PID */
218      auto pid = NetworkSerialization::to_byte_array(
219          NetworkSerialization::to_bigendian(this->_pid));
220      result.append(pid.begin(), pid.end());
221  
222      /* Seconds */
223      auto seconds =
224          NetworkSerialization::to_byte_array(NetworkSerialization::to_bigendian(
225              static_cast<uint64_t>(this->get_seconds().count())));
226      static_assert(seconds.size() == 8);
227      result.append(seconds.begin(), seconds.end());
228  
229      /* Microseconds */
230      auto microseconds =
231          NetworkSerialization::to_byte_array(NetworkSerialization::to_bigendian(
232              static_cast<uint64_t>(this->get_microseconds().count())));
233      static_assert(microseconds.size() == 8);
234      result.append(microseconds.begin(), microseconds.end());
235  
236      return result;
237  }
238  
239  MPING_STATE_TYPE MPingSender::MPingState::get_type() const noexcept
240  {
241      return this->_type;
242  }
243  
244  uint8_t MPingSender::MPingState::get_ttl() const noexcept
245  {
246      return this->_ttl;
247  }
248  
249  boost::asio::ip::address MPingSender::MPingState::get_src_host() const noexcept
250  {
251      return this->_src_host;
252  }
253  
254  uint16_t MPingSender::MPingState::get_src_port() const noexcept
255  {
256      return this->_src_port;
257  }
258  
259  boost::asio::ip::address MPingSender::MPingState::get_dest_host() const noexcept
260  {
261      return this->_dest_host;
262  }
263  
264  uint16_t MPingSender::MPingState::get_dest_port() const noexcept
265  {
266      return this->_dest_port;
267  }
268  
269  uint32_t MPingSender::MPingState::get_sequence_number() const noexcept
270  {
271      return this->_sequence_number;
272  }
273  
274  uint32_t MPingSender::MPingState::get_pid() const noexcept
275  {
276      return this->_pid;
277  }
278  
279  std::chrono::time_point<std::chrono::steady_clock>
280      MPingSender::MPingState::get_tv() const noexcept
281  {
282      return this->_tv;
283  }
284  
285  std::chrono::seconds MPingSender::MPingState::get_seconds() const noexcept
286  {
287      return std::chrono::duration_cast<std::chrono::seconds>(
288          this->_tv.time_since_epoch());
289  }
290  
291  std::chrono::microseconds
292      MPingSender::MPingState::get_microseconds() const noexcept
293  {
294      auto duration = this->_tv.time_since_epoch();
295      auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration);
296      auto remaining = duration - seconds;
297      return std::chrono::duration_cast<std::chrono::microseconds>(remaining);
298  }