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 }