OpenVPN 3 Core Library
Loading...
Searching...
No Matches
packet_id_data.hpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2012- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11
12// Manage OpenVPN protocol Packet IDs for packet replay detection
13#pragma once
14
15#include <algorithm>
16#include <string>
17#include <cstring>
18#include <sstream>
19#include <cstdint> // for std::uint32_t
20
21#include <openvpn/io/io.hpp>
22
30#include <openvpn/time/time.hpp>
33
34namespace openvpn {
51{
52 typedef std::uint64_t data_id_t;
53
54 /* the part of the packet id that represents the PID, the first 16 bits
55 * are used by the Epoch*/
56 static constexpr inline data_id_t epoch_packet_id_mask = 0x0000ffffffffffffull;
57
58
59 data_id_t id = 0; // legal values are 1 through 2^64-1
60 bool wide = false;
61
66 [[nodiscard]] constexpr std::size_t size() const
67 {
68 return size(wide);
69 }
70
71 static constexpr size_t size(bool wide)
72 {
73 if (wide)
74 return long_id_size;
75 else
76 return short_id_size;
77 }
78
79
80 explicit PacketIDData(bool wide_arg)
81 : wide(wide_arg)
82 {
83 }
84
85 explicit PacketIDData(bool wide_arg, data_id_t id_arg)
86 : id(id_arg), wide(wide_arg)
87 {
88 }
89
90 constexpr static std::size_t short_id_size = sizeof(std::uint32_t);
91 constexpr static std::size_t long_id_size = sizeof(std::uint64_t);
92
93 [[nodiscard]] bool is_valid() const
94 {
95 return id != 0;
96 }
97
98 void reset()
99 {
100 id = data_id_t(0);
101 }
102
103 uint16_t get_epoch()
104 {
105 return static_cast<uint16_t>((id & ~epoch_packet_id_mask) >> 48ull);
106 }
107
112 void read(ConstBuffer &buf)
113 {
114 if (wide)
115 {
116 std::uint64_t net_id;
117 buf.read(reinterpret_cast<unsigned char *>(&net_id), sizeof(net_id));
118 id = Endian::rev64(net_id);
119 }
120 else
121 {
122 std::uint32_t net_id;
123 buf.read(reinterpret_cast<unsigned char *>(&net_id), sizeof(net_id));
124 id = ntohl(net_id);
125 }
126 }
127
129 void write(Buffer &buf) const
130 {
131 if (wide)
132 {
133 const std::uint64_t net_id = Endian::rev64(id);
134 buf.write(reinterpret_cast<const unsigned char *>(&net_id), sizeof(net_id));
135 }
136 else
137 {
138 const std::uint32_t net_id = htonl(static_cast<std::uint32_t>(id));
139 buf.write(reinterpret_cast<const unsigned char *>(&net_id), sizeof(net_id));
140 }
141 }
142
144 void write_prepend(Buffer &buf) const
145 {
146 if (wide)
147 {
148 const std::uint64_t net_id = Endian::rev64(id);
149 buf.prepend(reinterpret_cast<const unsigned char *>(&net_id), sizeof(net_id));
150 }
151 else
152 {
153 const std::uint32_t net_id = htonl(static_cast<std::uint32_t>(id));
154 buf.prepend(reinterpret_cast<const unsigned char *>(&net_id), sizeof(net_id));
155 }
156 }
157
158
159 [[nodiscard]] std::string str() const
160 {
161 std::ostringstream os;
162 os << std::hex << "[0x" << id << "]";
163 return os.str();
164 }
165};
166
168{
169 public:
171
173 static constexpr inline PacketIDData::data_id_t epoch_packet_id_max = 0x0000ffffffffffffull;
174
175
176 explicit PacketIDDataSend(bool wide_arg, uint16_t epoch)
177 : pid_(wide_arg, static_cast<PacketIDData::data_id_t>(epoch) << 48ull)
178 {
179 }
180
182 : pid_(false)
183 {
184 }
190 [[nodiscard]] PacketIDData next()
191 {
192 ++pid_.id;
194 if (at_limit())
195 {
196 throw packet_id_wrap();
197 }
198 return ret;
199 }
200
206 {
207 const PacketIDData pid = next();
208 pid.write(buf);
209 }
210
216 {
217 const PacketIDData pid = next();
218 pid.write_prepend(buf);
219 }
220
221 [[nodiscard]] std::string str() const
222 {
223 std::string ret;
224 ret = pid_.str();
225 if (pid_.wide)
226 ret += 'L';
227 return ret;
228 }
229
234 [[nodiscard]] constexpr std::size_t length() const
235 {
236 return pid_.size();
237 }
238
250 bool wrap_warning() const
251 {
252 if (pid_.wide)
253 return false;
254
255 const PacketIDData::data_id_t wrap_at = 0xFF000000;
256 return pid_.id >= wrap_at;
257 }
258
259 bool at_limit()
260 {
261 if (!pid_.wide && unlikely(pid_.id == std::numeric_limits<std::uint32_t>::max())) // wraparound
262 {
263 return true;
264 }
266 {
267 return true;
268 }
269 return false;
270 }
271
272
273 protected:
275};
276
277/*
278 * This is the data structure we keep on the receiving side,
279 * to check that no packet-id is accepted more than once.
280 *
281 * Replay window sizing in bytes = 2^REPLAY_WINDOW_ORDER.
282 * PKTID_RECV_EXPIRE is backtrack expire in seconds.
283 */
284template <unsigned int REPLAY_WINDOW_ORDER,
285 unsigned int PKTID_RECV_EXPIRE>
287{
288 public:
289 static constexpr unsigned int REPLAY_WINDOW_BYTES = 1u << REPLAY_WINDOW_ORDER;
290 static constexpr unsigned int REPLAY_WINDOW_SIZE = REPLAY_WINDOW_BYTES * 8;
291
292#if defined(__GNUC__) && (__GNUC__ < 11)
293 /*
294 * For some reason g++ versions 10.x are regenerating a move constructor
295 * and move assignment operators that g++ itself then complains about them
296 * with "error: writing 16 bytes into a region of size 0 [-Werror=stringop-overflow=]
297 *
298 * So we manually define these to avoid this behaviour
299 */
300
301 PacketIDDataReceiveType() = default;
302 PacketIDDataReceiveType(const PacketIDDataReceiveType &other) = default;
304 {
305 wide = other.wide;
306 base = other.base;
307 extent = other.extent;
308 expire = other.expire;
309 id_high = other.id_high;
310 id_floor = other.id_floor;
311 unit = other.unit;
312 name = other.name;
313 memcpy(history, other.history, sizeof(history));
314 }
315
316 PacketIDDataReceiveType &operator=(PacketIDDataReceiveType &&other) noexcept
317 {
318 wide = other.wide;
319 base = other.base;
320 extent = other.extent;
321 expire = other.expire;
322 id_high = other.id_high;
323 id_floor = other.id_floor;
324 unit = other.unit;
325 name = other.name;
326 memcpy(history, other.history, sizeof(history));
327 return *this;
328 }
329 PacketIDDataReceiveType &operator=(const PacketIDDataReceiveType &other) = default;
330#endif
331
332 void init(const char *name_arg,
333 const int unit_arg,
334 bool wide_arg)
335 {
336 wide = wide_arg;
337 base = 0;
338 extent = 0;
339 expire = 0;
340 id_high = 0;
341 id_floor = 0;
342 unit = unit_arg;
343 name = name_arg;
344 std::memset(history, 0, sizeof(history));
345 }
346
347
359 [[nodiscard]] bool test_add(const PacketIDData &pin,
360 const Time::base_type now,
361 const SessionStats::Ptr &stats)
362 {
363 const Error::Type err = do_test_add(pin, now);
364 if (unlikely(err != Error::SUCCESS))
365 {
366 stats->error(err);
367 return false;
368 }
369 else
370 return true;
371 }
372
382 [[nodiscard]] Error::Type do_test_add(const PacketIDData &pin,
383 const Time::base_type now)
384 {
385 // expire backtracks at or below id_floor after PKTID_RECV_EXPIRE time
386 if (unlikely(now >= expire))
388 expire = now + PKTID_RECV_EXPIRE;
389
390 // ID must not be zero
391 if (unlikely(!pin.is_valid()))
393
394
395 if (likely(pin.id == id_high + 1))
396 {
397 // well-formed ID sequence (incremented by 1)
398 base = replay_index(-1);
399 history[base / 8] |= static_cast<uint8_t>(1 << (base % 8));
401 ++extent;
402 id_high = pin.id;
403 }
404 else if (pin.id > id_high)
405 {
406 // ID jumped forward by more than one
407
408 const auto delta = pin.id - id_high;
409 if (delta < REPLAY_WINDOW_SIZE)
410 {
411 base = replay_index(-delta);
412 history[base / 8] |= static_cast<uint8_t>(1u << (base % 8));
413 extent += static_cast<std::size_t>(delta);
416 for (unsigned i = 1; i < delta; ++i)
417 {
418 const auto newbase = replay_index(i);
419 history[newbase / 8] &= static_cast<uint8_t>(~(1u << (newbase % 8)));
420 }
421 }
422 else
423 {
424 base = 0;
426 std::memset(history, 0, sizeof(history));
427 history[0] = 1;
428 }
429 id_high = pin.id;
430 }
431 else
432 {
433 // ID backtrack
434 const auto delta = id_high - pin.id;
435 if (delta < extent)
436 {
437 if (pin.id > id_floor)
438 {
439 const auto ri = replay_index(delta);
440 std::uint8_t *p = &history[ri / 8];
441 const std::uint8_t mask = static_cast<uint8_t>(1u << (ri % 8));
442 if (*p & mask)
443 return Error::PKTID_REPLAY;
444 *p |= mask;
445 }
446 else
447 return Error::PKTID_EXPIRE;
448 }
449 else
451 }
452
453 return Error::SUCCESS;
454 }
455
457 {
458 PacketIDData pid{wide};
459 pid.read(buf);
460 return pid;
461 }
462
463 [[nodiscard]] std::string str() const
464 {
465 std::ostringstream os;
466 os << "[e=" << extent << " f=" << id_floor << id_high << ']';
467 return os.str();
468 }
469
470 [[nodiscard]] std::size_t constexpr length() const
471 {
472 return PacketIDData::size(wide);
473 }
474
475 private:
476 [[nodiscard]] constexpr std::size_t replay_index(PacketIDData::data_id_t i) const
477 {
478 return (base + i) & (REPLAY_WINDOW_SIZE - 1);
479 }
480
481 std::size_t base = 0; // bit position of deque base in history
482 std::size_t extent = 0; // extent (in bits) of deque in history
483 Time::base_type expire = 0; // expiration of history
484 PacketIDData::data_id_t id_high = 0; // highest sequence number received
485 PacketIDData::data_id_t id_floor = 0; // we will only accept backtrack IDs > id_floor
486
488 bool wide = false;
489 int unit = -1; // unit number of this object (for debugging)
490 std::string name{"not initialised"}; // name of this object (for debugging)
491
494};
495
496// Our standard packet ID window with order=8 (window size=2048).
497// and recv expire=30 seconds.
499
500} // namespace openvpn
void prepend(const T *data, const size_t size)
Prepend data to the buffer.
Definition buffer.hpp:1558
void write(const T *data, const size_t size)
Write data to the buffer.
Definition buffer.hpp:1546
void read(NCT *data, const size_t size)
Read data from the buffer into the specified memory location.
Definition buffer.hpp:1314
static constexpr unsigned int REPLAY_WINDOW_BYTES
PacketIDData read_next(Buffer &buf) const
PacketIDData::data_id_t id_high
void init(const char *name_arg, const int unit_arg, bool wide_arg)
static constexpr unsigned int REPLAY_WINDOW_SIZE
std::size_t constexpr length() const
bool test_add(const PacketIDData &pin, const Time::base_type now, const SessionStats::Ptr &stats)
std::uint8_t history[REPLAY_WINDOW_BYTES]
"sliding window" bitmask of recent packet IDs received *‍/
bool wide
< 32 or 64 bit packet counter
constexpr std::size_t replay_index(PacketIDData::data_id_t i) const
PacketIDData::data_id_t id_floor
Error::Type do_test_add(const PacketIDData &pin, const Time::base_type now)
constexpr std::size_t length() const
PacketIDDataSend(bool wide_arg, uint16_t epoch)
OPENVPN_SIMPLE_EXCEPTION(packet_id_wrap)
void prepend_next(Buffer &buf)
static constexpr PacketIDData::data_id_t epoch_packet_id_max
virtual void error(const size_t type, const std::string *text=nullptr)
::time_t base_type
Definition time.hpp:64
#define likely(x)
Definition likely.hpp:21
#define unlikely(x)
Definition likely.hpp:22
std::uint64_t rev64(const std::uint64_t value)
Definition endian64.hpp:32
Support deferred server-side state creation when client connects.
Definition ovpncli.cpp:95
PacketIDDataReceiveType< 8, 30 > PacketIDDataReceive
void write(Buffer &buf) const
static constexpr std::size_t short_id_size
void write_prepend(Buffer &buf) const
void read(ConstBuffer &buf)
std::string str() const
PacketIDData(bool wide_arg, data_id_t id_arg)
PacketIDData(bool wide_arg)
constexpr std::size_t size() const
static constexpr data_id_t epoch_packet_id_mask
static constexpr std::size_t long_id_size
static constexpr size_t size(bool wide)
std::string ret