0
0
mirror of https://github.com/OpenVPN/openvpn3.git synced 2024-09-19 19:52:15 +02:00

Differentiate Packet ID types into data channel and control channel ids

Data channel packet ids (in the formats that OpenVPN 3.x supports)
are plain 32 or 64 bit ids while control channel is a 32 bit time + 32
bit counter id. Seperate these more clearly and let CBC mode use the
same Packet ID implementation that AEAD mode uses.

Also add more unit tests related to data channel tests packets by
adapting the control channel test where applicable and add a few more
related to packet id wrapping

Signed-off-by: Arne Schwabe <arne@openvpn.net>
This commit is contained in:
Arne Schwabe 2024-08-16 11:52:42 +02:00 committed by Jenkins-dev
parent 16b2c4afe0
commit c78aaecad7
18 changed files with 536 additions and 303 deletions

View File

@ -32,7 +32,7 @@
#include <openvpn/buffer/buffer.hpp> #include <openvpn/buffer/buffer.hpp>
#include <openvpn/frame/frame.hpp> #include <openvpn/frame/frame.hpp>
#include <openvpn/crypto/static_key.hpp> #include <openvpn/crypto/static_key.hpp>
#include <openvpn/crypto/packet_id_aead.hpp> #include <openvpn/crypto/packet_id_data.hpp>
#include <openvpn/log/sessionstats.hpp> #include <openvpn/log/sessionstats.hpp>
#include <openvpn/crypto/cryptodc.hpp> #include <openvpn/crypto/cryptodc.hpp>
@ -85,12 +85,12 @@ class Crypto : public CryptoDCInstance
} }
// for encrypt // for encrypt
Nonce(const Nonce &ref, PacketIDAEADSend &pid_send, const unsigned char *op32) Nonce(const Nonce &ref, PacketIDDataSend &pid_send, const unsigned char *op32)
{ {
/** Copy op code and tail of packet ID */ /** Copy op code and tail of packet ID */
std::memcpy(data, ref.data, sizeof(data)); std::memcpy(data, ref.data, sizeof(data));
Buffer buf(data + data_offset_pkt_id, PacketIDAEAD::long_id_size, false); Buffer buf(data + data_offset_pkt_id, PacketIDData::long_id_size, false);
pid_send.write_next(buf); pid_send.write_next(buf);
if (op32) if (op32)
{ {
@ -102,13 +102,13 @@ class Crypto : public CryptoDCInstance
} }
// for encrypt // for encrypt
void prepend_ad(Buffer &buf, const PacketIDAEADSend &pid_send) const void prepend_ad(Buffer &buf, const PacketIDDataSend &pid_send) const
{ {
buf.prepend(data + data_offset_pkt_id, pid_send.length()); buf.prepend(data + data_offset_pkt_id, pid_send.length());
} }
// for decrypt // for decrypt
Nonce(const Nonce &ref, const PacketIDAEADReceive &recv_pid, Buffer &buf, const unsigned char *op32) Nonce(const Nonce &ref, const PacketIDDataReceive &recv_pid, Buffer &buf, const unsigned char *op32)
{ {
/* Copy opcode and tail of packet ID */ /* Copy opcode and tail of packet ID */
std::memcpy(data, ref.data, sizeof(data)); std::memcpy(data, ref.data, sizeof(data));
@ -125,10 +125,10 @@ class Crypto : public CryptoDCInstance
} }
// for decrypt // for decrypt
bool verify_packet_id(PacketIDAEADReceive &pid_recv, const PacketID::time_t now) bool verify_packet_id(PacketIDDataReceive &pid_recv, const PacketIDControl::time_t now)
{ {
Buffer buf(data + data_offset_pkt_id, PacketIDAEAD::long_id_size, true); Buffer buf(data + data_offset_pkt_id, PacketIDData::long_id_size, true);
const PacketIDAEAD pid = pid_recv.read_next(buf); const PacketIDData pid = pid_recv.read_next(buf);
return pid_recv.test_add(pid, now); // verify packet ID return pid_recv.test_add(pid, now); // verify packet ID
} }
@ -142,12 +142,12 @@ class Crypto : public CryptoDCInstance
return ad_op32 ? data : data + data_offset_pkt_id; return ad_op32 ? data : data + data_offset_pkt_id;
} }
size_t ad_len(const PacketIDAEADSend &pid_send) const size_t ad_len(const PacketIDDataSend &pid_send) const
{ {
return (ad_op32 ? op32_size : 0) + pid_send.length(); return (ad_op32 ? op32_size : 0) + pid_send.length();
} }
size_t ad_len(const PacketIDAEADReceive &pid_recv) const size_t ad_len(const PacketIDDataReceive &pid_recv) const
{ {
return (ad_op32 ? op32_size : 0) + pid_recv.length(); return (ad_op32 ? op32_size : 0) + pid_recv.length();
} }
@ -168,7 +168,7 @@ class Crypto : public CryptoDCInstance
{ {
typename CRYPTO_API::CipherContextAEAD impl; typename CRYPTO_API::CipherContextAEAD impl;
Nonce nonce; Nonce nonce;
PacketIDAEADSend pid_send{false}; PacketIDDataSend pid_send{false};
BufferAllocated work; BufferAllocated work;
}; };
@ -176,7 +176,7 @@ class Crypto : public CryptoDCInstance
{ {
typename CRYPTO_API::CipherContextAEAD impl; typename CRYPTO_API::CipherContextAEAD impl;
Nonce nonce; Nonce nonce;
PacketIDAEADReceive pid_recv{}; PacketIDDataReceive pid_recv{};
BufferAllocated work; BufferAllocated work;
}; };
@ -197,7 +197,7 @@ class Crypto : public CryptoDCInstance
// Encrypt/Decrypt // Encrypt/Decrypt
// returns true if packet ID is close to wrapping // returns true if packet ID is close to wrapping
bool encrypt(BufferAllocated &buf, const PacketID::time_t now, const unsigned char *op32) override bool encrypt(BufferAllocated &buf, const unsigned char *op32) override
{ {
// only process non-null packets // only process non-null packets
if (buf.size()) if (buf.size())
@ -251,7 +251,7 @@ class Crypto : public CryptoDCInstance
return e.pid_send.wrap_warning(); return e.pid_send.wrap_warning();
} }
Error::Type decrypt(BufferAllocated &buf, const PacketID::time_t now, const unsigned char *op32) override Error::Type decrypt(BufferAllocated &buf, const std::time_t now, const unsigned char *op32) override
{ {
// only process non-null packets // only process non-null packets
if (buf.size()) if (buf.size())
@ -337,7 +337,7 @@ class Crypto : public CryptoDCInstance
const int recv_unit, const int recv_unit,
const SessionStats::Ptr &recv_stats_arg) override const SessionStats::Ptr &recv_stats_arg) override
{ {
e.pid_send = PacketIDAEADSend{dc_settings.use64bitPktCounter()}; e.pid_send = PacketIDDataSend{dc_settings.use64bitPktCounter()};
d.pid_recv.init(recv_name, recv_unit, dc_settings.use64bitPktCounter(), recv_stats_arg); d.pid_recv.init(recv_name, recv_unit, dc_settings.use64bitPktCounter(), recv_stats_arg);
} }

View File

@ -58,13 +58,13 @@ class CryptoCHM : public CryptoDCInstance
// Encrypt/Decrypt // Encrypt/Decrypt
/* returns true if packet ID is close to wrapping */ /* returns true if packet ID is close to wrapping */
bool encrypt(BufferAllocated &buf, const PacketID::time_t now, const unsigned char *op32) override bool encrypt(BufferAllocated &buf, const unsigned char *op32) override
{ {
encrypt_.encrypt(buf, now); encrypt_.encrypt(buf);
return encrypt_.pid_send.wrap_warning(); return encrypt_.pid_send.wrap_warning();
} }
Error::Type decrypt(BufferAllocated &buf, const PacketID::time_t now, const unsigned char *op32) override Error::Type decrypt(BufferAllocated &buf, const std::time_t now, const unsigned char *op32) override
{ {
return decrypt_.decrypt(buf, now); return decrypt_.decrypt(buf, now);
} }
@ -90,10 +90,10 @@ class CryptoCHM : public CryptoDCInstance
const SessionStats::Ptr &recv_stats_arg) override const SessionStats::Ptr &recv_stats_arg) override
{ {
/* CBC encryption always uses short packet ID */ /* CBC encryption always uses short packet ID */
auto pid_form = PacketID::SHORT_FORM; constexpr bool wide = false;
encrypt_.pid_send.init(pid_form); encrypt_.pid_send = PacketIDDataSend{wide};
decrypt_.pid_recv.init(pid_form, recv_name, recv_unit, recv_stats_arg); decrypt_.pid_recv.init(recv_name, recv_unit, wide, recv_stats_arg);
} }
bool consider_compression(const CompressContext &comp_ctx) override bool consider_compression(const CompressContext &comp_ctx) override

View File

@ -33,7 +33,7 @@
#include <openvpn/common/rc.hpp> #include <openvpn/common/rc.hpp>
#include <openvpn/frame/frame.hpp> #include <openvpn/frame/frame.hpp>
#include <openvpn/crypto/static_key.hpp> #include <openvpn/crypto/static_key.hpp>
#include <openvpn/crypto/packet_id.hpp> #include <openvpn/crypto/packet_id_control.hpp>
#include <openvpn/crypto/cryptoalgs.hpp> #include <openvpn/crypto/cryptoalgs.hpp>
#include <openvpn/compress/compress.hpp> #include <openvpn/compress/compress.hpp>
@ -48,9 +48,9 @@ class CryptoDCInstance : public RC<thread_unsafe_refcount>
// Encrypt/Decrypt // Encrypt/Decrypt
// returns true if packet ID is close to wrapping // returns true if packet ID is close to wrapping
virtual bool encrypt(BufferAllocated &buf, const PacketID::time_t now, const unsigned char *op32) = 0; virtual bool encrypt(BufferAllocated &buf, const unsigned char *op32) = 0;
virtual Error::Type decrypt(BufferAllocated &buf, const PacketID::time_t now, const unsigned char *op32) = 0; virtual Error::Type decrypt(BufferAllocated &buf, std::time_t now, const unsigned char *op32) = 0;
// Initialization // Initialization

View File

@ -34,7 +34,7 @@
#include <openvpn/crypto/cipher.hpp> #include <openvpn/crypto/cipher.hpp>
#include <openvpn/crypto/ovpnhmac.hpp> #include <openvpn/crypto/ovpnhmac.hpp>
#include <openvpn/crypto/static_key.hpp> #include <openvpn/crypto/static_key.hpp>
#include <openvpn/crypto/packet_id.hpp> #include <openvpn/crypto/packet_id_control.hpp>
#include <openvpn/log/sessionstats.hpp> #include <openvpn/log/sessionstats.hpp>
namespace openvpn { namespace openvpn {
@ -45,7 +45,7 @@ class DecryptCHM
public: public:
OPENVPN_SIMPLE_EXCEPTION(chm_unsupported_cipher_mode); OPENVPN_SIMPLE_EXCEPTION(chm_unsupported_cipher_mode);
Error::Type decrypt(BufferAllocated &buf, const PacketID::time_t now) Error::Type decrypt(BufferAllocated &buf, const std::time_t now)
{ {
// skip null packets // skip null packets
if (!buf.size()) if (!buf.size())
@ -118,19 +118,13 @@ class DecryptCHM
Frame::Ptr frame; Frame::Ptr frame;
CipherContext<CRYPTO_API> cipher; CipherContext<CRYPTO_API> cipher;
OvpnHMAC<CRYPTO_API> hmac; OvpnHMAC<CRYPTO_API> hmac;
PacketIDReceive pid_recv; PacketIDDataReceive pid_recv;
private: private:
bool verify_packet_id(BufferAllocated &buf, const PacketID::time_t now) bool verify_packet_id(BufferAllocated &buf, const std::time_t now)
{ {
// ignore packet ID if pid_recv is not initialized const PacketIDData pid = pid_recv.read_next(buf);
if (pid_recv.initialized()) return pid_recv.test_add(pid, now);
{
const PacketID pid = pid_recv.read_next(buf);
if (!pid_recv.test_add(pid, now, true)) // verify packet ID
return false;
}
return true;
} }
BufferAllocated work; BufferAllocated work;

View File

@ -35,7 +35,7 @@
#include <openvpn/crypto/cipher.hpp> #include <openvpn/crypto/cipher.hpp>
#include <openvpn/crypto/ovpnhmac.hpp> #include <openvpn/crypto/ovpnhmac.hpp>
#include <openvpn/crypto/static_key.hpp> #include <openvpn/crypto/static_key.hpp>
#include <openvpn/crypto/packet_id.hpp> #include <openvpn/crypto/packet_id_data.hpp>
namespace openvpn { namespace openvpn {
template <typename CRYPTO_API> template <typename CRYPTO_API>
@ -44,7 +44,7 @@ class EncryptCHM
public: public:
OPENVPN_SIMPLE_EXCEPTION(chm_unsupported_cipher_mode); OPENVPN_SIMPLE_EXCEPTION(chm_unsupported_cipher_mode);
void encrypt(BufferAllocated &buf, const PacketID::time_t now) void encrypt(BufferAllocated &buf)
{ {
// skip null packets // skip null packets
if (!buf.size()) if (!buf.size())
@ -64,7 +64,7 @@ class EncryptCHM
rng->rand_bytes(iv_buf, iv_length); rng->rand_bytes(iv_buf, iv_length);
// generate fresh outgoing packet ID and prepend to cleartext buffer // generate fresh outgoing packet ID and prepend to cleartext buffer
pid_send.write_next(buf, true, now); pid_send.prepend_next(buf);
} }
else else
{ {
@ -95,7 +95,7 @@ class EncryptCHM
else // no encryption else // no encryption
{ {
// generate fresh outgoing packet ID and prepend to cleartext buffer // generate fresh outgoing packet ID and prepend to cleartext buffer
pid_send.write_next(buf, true, now); pid_send.prepend_next(buf);
// HMAC the cleartext // HMAC the cleartext
prepend_hmac(buf); prepend_hmac(buf);
@ -110,7 +110,7 @@ class EncryptCHM
Frame::Ptr frame; Frame::Ptr frame;
CipherContext<CRYPTO_API> cipher; CipherContext<CRYPTO_API> cipher;
OvpnHMAC<CRYPTO_API> hmac; OvpnHMAC<CRYPTO_API> hmac;
PacketIDSend pid_send; PacketIDDataSend pid_send{false};
private: private:
// compute HMAC signature of data buffer, // compute HMAC signature of data buffer,

View File

@ -21,8 +21,7 @@
// Manage OpenVPN protocol Packet IDs for packet replay detection // Manage OpenVPN protocol Packet IDs for packet replay detection
#ifndef OPENVPN_CRYPTO_PACKET_ID_H #pragma once
#define OPENVPN_CRYPTO_PACKET_ID_H
#include <string> #include <string>
#include <cstring> #include <cstring>
@ -42,59 +41,38 @@
namespace openvpn { namespace openvpn {
/* /*
* Communicate packet-id over the wire. * Control channel Packet ID. These IDs have the format
* A short packet-id is just a 32 bit
* sequence number. A long packet-id
* includes a timestamp as well.
* *
* Long packet-ids are used as IVs for * | 32 bit integer timestamp in BE | 32 bit packet counter in BE |
* CFB/OFB ciphers.
* *
* This data structure is always sent * This format of long packet-ids is also used as IVs for CFB/OFB ciphers
* over the net in network byte order, * in OpenVPN 2.x but OpenVPN 3.x supports only CBC and AEAD ciphers, so
* by calling htonl, ntohl, on the * it is only used for control channel and control chanel authentication/encryption
* 32-bit data elements, id_t and * schemes like tls-auth/tls-crypt.
* net_time_t, to change them to and
* from network order.
* *
* In addition, time is converted to * This data structure is always sent over the net in network byte order,
* a PacketID::net_time_t before sending, * by calling htonl, ntohl, on the 32-bit data elements, id_t and
* since openvpn always * net_time_t, to change them to and from network order.
* uses a 32-bit time_t but some
* 64 bit platforms use a
* 64 bit time_t.
*/ */
struct PacketID struct PacketIDControl
{ {
typedef std::uint32_t id_t; typedef std::uint32_t id_t;
typedef std::uint32_t net_time_t; typedef std::uint32_t net_time_t;
typedef Time::base_type time_t; typedef Time::base_type time_t;
enum
{
SHORT_FORM = 0, // short form of ID (4 bytes)
LONG_FORM = 1, // long form of ID (8 bytes)
UNDEF = 0, // special undefined/null id_t value
};
id_t id; // legal values are 1 through 2^32-1 id_t id; // legal values are 1 through 2^32-1
time_t time; // converted to PacketID::net_time_t before transmission time_t time; // converted to PacketID::net_time_t before transmission
static constexpr size_t size(const int form) static constexpr size_t size()
{ {
if (form == PacketID::LONG_FORM) return idsize;
return longidsize;
else
return shortidsize;
} }
constexpr static size_t shortidsize = sizeof(id_t); constexpr static size_t idsize = sizeof(id_t) + sizeof(net_time_t);
constexpr static size_t longidsize = sizeof(id_t) + sizeof(net_time_t);
bool is_valid() const bool is_valid() const
{ {
return id != UNDEF; return id != 0;
} }
void reset() void reset()
@ -104,7 +82,7 @@ struct PacketID
} }
template <typename BufType> // so it can take a Buffer or a ConstBuffer template <typename BufType> // so it can take a Buffer or a ConstBuffer
void read(BufType &buf, const int form) void read(BufType &buf)
{ {
id_t net_id; id_t net_id;
net_time_t net_time; net_time_t net_time;
@ -112,16 +90,11 @@ struct PacketID
buf.read((unsigned char *)&net_id, sizeof(net_id)); buf.read((unsigned char *)&net_id, sizeof(net_id));
id = ntohl(net_id); id = ntohl(net_id);
if (form == LONG_FORM)
{
buf.read((unsigned char *)&net_time, sizeof(net_time)); buf.read((unsigned char *)&net_time, sizeof(net_time));
time = ntohl(net_time); time = ntohl(net_time);
} }
else
time = time_t(0);
}
void write(Buffer &buf, const int form, const bool prepend) const void write(Buffer &buf, const bool prepend) const
{ {
const id_t net_id = htonl(id); const id_t net_id = htonl(id);
const net_time_t net_time = htonl(static_cast<uint32_t>(time & 0x00000000FFFFFFFF)); const net_time_t net_time = htonl(static_cast<uint32_t>(time & 0x00000000FFFFFFFF));
@ -130,14 +103,12 @@ struct PacketID
if (prepend) if (prepend)
{ {
if (form == LONG_FORM)
buf.prepend((unsigned char *)&net_time, sizeof(net_time)); buf.prepend((unsigned char *)&net_time, sizeof(net_time));
buf.prepend((unsigned char *)&net_id, sizeof(net_id)); buf.prepend((unsigned char *)&net_id, sizeof(net_id));
} }
else else
{ {
buf.write((unsigned char *)&net_id, sizeof(net_id)); buf.write((unsigned char *)&net_id, sizeof(net_id));
if (form == LONG_FORM)
buf.write((unsigned char *)&net_time, sizeof(net_time)); buf.write((unsigned char *)&net_time, sizeof(net_time));
} }
} }
@ -150,51 +121,36 @@ struct PacketID
} }
}; };
struct PacketIDConstruct : public PacketID
{
PacketIDConstruct(const PacketID::time_t v_time = PacketID::time_t(0), const PacketID::id_t v_id = PacketID::id_t(0))
{
id = v_id;
time = v_time;
}
};
class PacketIDSend class PacketIDControlSend
{ {
public: public:
OPENVPN_SIMPLE_EXCEPTION(packet_id_wrap); OPENVPN_SIMPLE_EXCEPTION(packet_id_wrap);
PacketIDSend()
{
init(PacketID::SHORT_FORM);
}
explicit PacketIDSend(int form, PacketID::id_t start_at = PacketID::id_t(0)) explicit PacketIDControlSend(PacketIDControl::id_t start_at = PacketIDControl::id_t(0))
{ {
init(form, start_at); init(start_at);
} }
/** /**
* @param form PacketID::LONG_FORM or PacketID::SHORT_FORM * @param form PacketID::LONG_FORM or PacketID::SHORT_FORM
* @param start_at initial id for the sending * @param start_at initial id for the sending
*/ */
void init(const int form, PacketID::id_t start_at = 0) void init(PacketIDControl::id_t start_at = 0)
{ {
pid_.id = start_at; pid_.id = start_at;
pid_.time = PacketID::time_t(0); pid_.time = PacketIDControl::time_t(0);
form_ = form;
} }
PacketID next(const PacketID::time_t now) PacketIDControl next(const PacketIDControl::time_t now)
{ {
PacketID ret; PacketIDControl ret;
if (!pid_.time) if (!pid_.time)
pid_.time = now; pid_.time = now;
ret.id = ++pid_.id; ret.id = ++pid_.id;
if (unlikely(!pid_.id)) // wraparound if (unlikely(!pid_.id)) // wraparound
{ {
if (form_ != PacketID::LONG_FORM)
throw packet_id_wrap();
pid_.time = now; pid_.time = now;
ret.id = pid_.id = 1; ret.id = pid_.id = 1;
} }
@ -202,35 +158,22 @@ class PacketIDSend
return ret; return ret;
} }
void write_next(Buffer &buf, const bool prepend, const PacketID::time_t now) void write_next(Buffer &buf, const bool prepend, const PacketIDControl::time_t now)
{ {
const PacketID pid = next(now); const PacketIDControl pid = next(now);
pid.write(buf, form_, prepend); pid.write(buf, prepend);
}
/*
* In TLS mode, when a packet ID gets to this level,
* start thinking about triggering a new
* SSL/TLS handshake.
*/
bool wrap_warning() const
{
const PacketID::id_t wrap_at = 0xFF000000;
return pid_.id >= wrap_at;
} }
std::string str() const std::string str() const
{ {
std::string ret; std::string ret;
ret = pid_.str(); ret = pid_.str();
if (form_ == PacketID::LONG_FORM)
ret += 'L'; ret += 'L';
return ret; return ret;
} }
private: private:
PacketID pid_; PacketIDControl pid_;
int form_;
}; };
/* /*
@ -243,7 +186,7 @@ class PacketIDSend
*/ */
template <unsigned int REPLAY_WINDOW_ORDER, template <unsigned int REPLAY_WINDOW_ORDER,
unsigned int PKTID_RECV_EXPIRE> unsigned int PKTID_RECV_EXPIRE>
class PacketIDReceiveType class PacketIDControlReceiveType
{ {
public: public:
static constexpr unsigned int REPLAY_WINDOW_BYTES = 1 << REPLAY_WINDOW_ORDER; static constexpr unsigned int REPLAY_WINDOW_BYTES = 1 << REPLAY_WINDOW_ORDER;
@ -252,13 +195,11 @@ class PacketIDReceiveType
OPENVPN_SIMPLE_EXCEPTION(packet_id_not_initialized); OPENVPN_SIMPLE_EXCEPTION(packet_id_not_initialized);
// TODO: [OVPN3-933] Consider RAII'ifying this code // TODO: [OVPN3-933] Consider RAII'ifying this code
PacketIDReceiveType() PacketIDControlReceiveType()
: initialized_(false)
{ {
} }
void init(const int form_arg, void init(const char *name_arg,
const char *name_arg,
const int unit_arg, const int unit_arg,
const SessionStats::Ptr &stats_arg) const SessionStats::Ptr &stats_arg)
{ {
@ -270,20 +211,19 @@ class PacketIDReceiveType
time_high = 0; time_high = 0;
id_floor = 0; id_floor = 0;
max_backtrack = 0; max_backtrack = 0;
form = form_arg;
unit = unit_arg; unit = unit_arg;
name = name_arg; name = name_arg;
stats = stats_arg; stats = stats_arg;
std::memset(history, 0, sizeof(history)); std::memset(history, 0, sizeof(history));
} }
bool initialized() const [[nodiscard]] bool initialized() const
{ {
return initialized_; return initialized_;
} }
bool test_add(const PacketID &pin, bool test_add(const PacketIDControl &pin,
const PacketID::time_t now, const PacketIDControl::time_t now,
const bool mod) // don't modify history unless mod is true const bool mod) // don't modify history unless mod is true
{ {
const Error::Type err = do_test_add(pin, now, mod); const Error::Type err = do_test_add(pin, now, mod);
@ -296,8 +236,8 @@ class PacketIDReceiveType
return true; return true;
} }
Error::Type do_test_add(const PacketID &pin, Error::Type do_test_add(const PacketIDControl &pin,
const PacketID::time_t now, const PacketIDControl::time_t now,
const bool mod) // don't modify history unless mod is true const bool mod) // don't modify history unless mod is true
{ {
// make sure we were initialized // make sure we were initialized
@ -402,12 +342,12 @@ class PacketIDReceiveType
return Error::SUCCESS; return Error::SUCCESS;
} }
PacketID read_next(Buffer &buf) const PacketIDControl read_next(Buffer &buf) const
{ {
if (!initialized_) if (!initialized_)
throw packet_id_not_initialized(); throw packet_id_not_initialized();
PacketID pid; PacketIDControl pid{};
pid.read(buf, form); pid.read(buf);
return pid; return pid;
} }
@ -424,18 +364,17 @@ class PacketIDReceiveType
return (base + i) & (REPLAY_WINDOW_SIZE - 1); return (base + i) & (REPLAY_WINDOW_SIZE - 1);
} }
bool initialized_; bool initialized_ = false;
unsigned int base; // bit position of deque base in history unsigned int base = 0; // bit position of deque base in history
unsigned int extent; // extent (in bits) of deque in history unsigned int extent = 0; // extent (in bits) of deque in history
PacketID::time_t expire; // expiration of history PacketIDControl::time_t expire = 0; // expiration of history
PacketID::id_t id_high; // highest sequence number received PacketIDControl::id_t id_high = 0; // highest sequence number received
PacketID::time_t time_high; // highest time stamp received PacketIDControl::time_t time_high = 0; // highest time stamp received
PacketID::id_t id_floor; // we will only accept backtrack IDs > id_floor PacketIDControl::id_t id_floor = 0; // we will only accept backtrack IDs > id_floor
unsigned int max_backtrack; unsigned int max_backtrack = 0;
int form; // PacketID::LONG_FORM or PacketID::SHORT_FORM int unit = -1; // unit number of this object (for debugging)
int unit; // unit number of this object (for debugging)
std::string name; // name of this object (for debugging) std::string name; // name of this object (for debugging)
SessionStats::Ptr stats; SessionStats::Ptr stats;
@ -445,8 +384,6 @@ class PacketIDReceiveType
// Our standard packet ID window with order=8 (window size=2048). // Our standard packet ID window with order=8 (window size=2048).
// and recv expire=30 seconds. // and recv expire=30 seconds.
typedef PacketIDReceiveType<8, 30> PacketIDReceive; typedef PacketIDControlReceiveType<8, 30> PacketIDControlReceive;
} // namespace openvpn } // namespace openvpn
#endif // OPENVPN_CRYPTO_PACKET_ID_H

View File

@ -43,24 +43,25 @@
namespace openvpn { namespace openvpn {
/** /**
* Communicate packet-id over the wire for AEAD * Communicate packet-id over the wire for data channel packets
* A short packet-id is just a 32 bit sequence number. A long packet-id is a * A short packet-id is just a 32 bit sequence number. A long packet-id is a
* 64 bit sequence number. This sequence number is reused for AEAD IV. * 64 bit sequence number. This sequence number is reused for AEAD IV when
* AEAD is used as a cipher. CBC transmits an additional IV.
* *
* This data structure is always sent over the net in network byte order, * This data structure is always sent over the net in network byte order,
* *
* This class is different from PacketID in the way that it always uses * This class is different from PacketIDControl in the way that it always uses
* a "flat" packet id that is either 32 or 64 bit while PacketID has a long * a "flat" packet id that is either 32 or 64 bit while PacketIDControl has a long
* packet id that is 32bit + 32bit but follow different rules and includes * packet id that is 32bit + 32bit but follow different rules and includes
* a timestamp. Merging PacketIDAEAD and PacketID would result in a much * a timestamp. Merging PacketIData and PacketIDControl would result in a much
* more convoluted and hard to understand class than keeping them seperate * more convoluted and hard to understand class than keeping them separate.
* *
*/ */
struct PacketIDAEAD struct PacketIDData
{ {
typedef std::uint64_t aead_id_t; typedef std::uint64_t data_id_t;
aead_id_t id = 0; // legal values are 1 through 2^64-1 data_id_t id = 0; // legal values are 1 through 2^64-1
bool wide = false; bool wide = false;
/** /**
@ -81,12 +82,12 @@ struct PacketIDAEAD
} }
explicit PacketIDAEAD(bool wide_arg) explicit PacketIDData(bool wide_arg)
: wide(wide_arg) : wide(wide_arg)
{ {
} }
explicit PacketIDAEAD(bool wide_arg, aead_id_t id_arg) explicit PacketIDData(bool wide_arg, data_id_t id_arg)
: id(id_arg), wide(wide_arg) : id(id_arg), wide(wide_arg)
{ {
} }
@ -94,14 +95,14 @@ struct PacketIDAEAD
constexpr static std::size_t short_id_size = sizeof(std::uint32_t); constexpr static std::size_t short_id_size = sizeof(std::uint32_t);
constexpr static std::size_t long_id_size = sizeof(std::uint64_t); constexpr static std::size_t long_id_size = sizeof(std::uint64_t);
bool is_valid() const [[nodiscard]] bool is_valid() const
{ {
return id != 0; return id != 0;
} }
void reset() void reset()
{ {
id = aead_id_t(0); id = data_id_t(0);
} }
/** /**
@ -139,7 +140,23 @@ struct PacketIDAEAD
} }
} }
std::string str() const /** Prepend the packet id to a buffer */
void write_prepend(Buffer &buf) const
{
if (wide)
{
const std::uint64_t net_id = Endian::rev64(id);
buf.prepend(reinterpret_cast<const unsigned char *>(&net_id), sizeof(net_id));
}
else
{
const std::uint32_t net_id = htonl(static_cast<std::uint32_t>(id));
buf.prepend(reinterpret_cast<const unsigned char *>(&net_id), sizeof(net_id));
}
}
[[nodiscard]] std::string str() const
{ {
std::ostringstream os; std::ostringstream os;
os << std::hex << "[0x" << id << "]"; os << std::hex << "[0x" << id << "]";
@ -147,12 +164,12 @@ struct PacketIDAEAD
} }
}; };
class PacketIDAEADSend class PacketIDDataSend
{ {
public: public:
OPENVPN_SIMPLE_EXCEPTION(packet_id_wrap); OPENVPN_SIMPLE_EXCEPTION(packet_id_wrap);
PacketIDAEADSend(bool wide_arg) explicit PacketIDDataSend(bool wide_arg)
: pid_(wide_arg) : pid_(wide_arg)
{ {
} }
@ -163,10 +180,10 @@ class PacketIDAEADSend
* @throws packet_id_wrap if the packet id space is exhausted * @throws packet_id_wrap if the packet id space is exhausted
* @return packet id to use next. * @return packet id to use next.
*/ */
PacketIDAEAD next() [[nodiscard]] PacketIDData next()
{ {
++pid_.id; ++pid_.id;
PacketIDAEAD ret{pid_.wide, pid_.id}; PacketIDData ret{pid_.wide, pid_.id};
if (!pid_.wide && unlikely(pid_.id == std::numeric_limits<std::uint32_t>::max())) // wraparound if (!pid_.wide && unlikely(pid_.id == std::numeric_limits<std::uint32_t>::max())) // wraparound
{ {
throw packet_id_wrap(); throw packet_id_wrap();
@ -184,26 +201,21 @@ class PacketIDAEADSend
*/ */
void write_next(Buffer &buf) void write_next(Buffer &buf)
{ {
const PacketIDAEAD pid = next(); const PacketIDData pid = next();
pid.write(buf); pid.write(buf);
} }
/** /**
* When a VPN runs in TLS mode (the only mode that OpenVPN supports, * increases the packet id and prepends it to a buffer
* there is no --secret mode anymore), it needs to be warned about wrapping to * @param buf buffer to write to
* start thinking about triggering a new SSL/TLS handshake.
* This method can be called to see if that level has been reached.
*/ */
bool wrap_warning() const void prepend_next(Buffer &buf)
{ {
if (pid_.wide) const PacketIDData pid = next();
return false; pid.write_prepend(buf);
const PacketIDAEAD::aead_id_t wrap_at = 0xFF000000;
return pid_.id >= wrap_at;
} }
std::string str() const [[nodiscard]] std::string str() const
{ {
std::string ret; std::string ret;
ret = pid_.str(); ret = pid_.str();
@ -221,8 +233,28 @@ class PacketIDAEADSend
return pid_.size(); return pid_.size();
} }
private: /**
PacketIDAEAD pid_; * When a VPN runs in TLS mode (the only mode that OpenVPN supports,
* there is no --secret mode anymore), it needs to be warned about wrapping to
* start thinking about triggering a new SSL/TLS handshake.
* This method can be called to see if that level has been reached.
*
* For 64bit counters, even with (non-existing) 1 byte packets, we would need
* to transfer 16 EB (exabytes) and 1,6 ZB (zettabytes) with 100 byte packets.
* This is not reachable in reasonable amount of time. And we still have the
* failsafe to throw an exception if we would overflow the ocunter.
*/
bool wrap_warning() const
{
if (pid_.wide)
return false;
const PacketIDData::data_id_t wrap_at = 0xFF000000;
return pid_.id >= wrap_at;
}
protected:
PacketIDData pid_;
}; };
/* /*
@ -234,7 +266,7 @@ class PacketIDAEADSend
*/ */
template <unsigned int REPLAY_WINDOW_ORDER, template <unsigned int REPLAY_WINDOW_ORDER,
unsigned int PKTID_RECV_EXPIRE> unsigned int PKTID_RECV_EXPIRE>
class PacketIDAEADReceiveType class PacketIDDataReceiveType
{ {
public: public:
static constexpr unsigned int REPLAY_WINDOW_BYTES = 1u << REPLAY_WINDOW_ORDER; static constexpr unsigned int REPLAY_WINDOW_BYTES = 1u << REPLAY_WINDOW_ORDER;
@ -268,7 +300,7 @@ class PacketIDAEADReceiveType
* @param now Current time to check that reordered packets are in the allowed time * @param now Current time to check that reordered packets are in the allowed time
* @return true if the packet id is okay and has been accepted * @return true if the packet id is okay and has been accepted
*/ */
[[nodiscard]] bool test_add(const PacketIDAEAD &pin, [[nodiscard]] bool test_add(const PacketIDData &pin,
const Time::base_type now) const Time::base_type now)
{ {
const Error::Type err = do_test_add(pin, now); const Error::Type err = do_test_add(pin, now);
@ -290,7 +322,7 @@ class PacketIDAEADReceiveType
* @param now Current time to check that reordered packets are in the allowed time * @param now Current time to check that reordered packets are in the allowed time
* @return Error::SUCCESS if successful, otherwise PKTID_EXPIRE, PKTID_BACKTRACK or PKTID_REPLAY * @return Error::SUCCESS if successful, otherwise PKTID_EXPIRE, PKTID_BACKTRACK or PKTID_REPLAY
*/ */
[[nodiscard]] Error::Type do_test_add(const PacketIDAEAD &pin, [[nodiscard]] Error::Type do_test_add(const PacketIDData &pin,
const Time::base_type now) const Time::base_type now)
{ {
// expire backtracks at or below id_floor after PKTID_RECV_EXPIRE time // expire backtracks at or below id_floor after PKTID_RECV_EXPIRE time
@ -364,9 +396,9 @@ class PacketIDAEADReceiveType
return Error::SUCCESS; return Error::SUCCESS;
} }
PacketIDAEAD read_next(Buffer &buf) const PacketIDData read_next(Buffer &buf) const
{ {
PacketIDAEAD pid{wide}; PacketIDData pid{wide};
pid.read(buf); pid.read(buf);
return pid; return pid;
} }
@ -380,11 +412,11 @@ class PacketIDAEADReceiveType
[[nodiscard]] std::size_t constexpr length() const [[nodiscard]] std::size_t constexpr length() const
{ {
return PacketIDAEAD::size(wide); return PacketIDData::size(wide);
} }
private: private:
[[nodiscard]] constexpr std::size_t replay_index(PacketIDAEAD::aead_id_t i) const [[nodiscard]] constexpr std::size_t replay_index(PacketIDData::data_id_t i) const
{ {
return (base + i) & (REPLAY_WINDOW_SIZE - 1); return (base + i) & (REPLAY_WINDOW_SIZE - 1);
} }
@ -392,8 +424,8 @@ class PacketIDAEADReceiveType
std::size_t base; // bit position of deque base in history std::size_t base; // bit position of deque base in history
std::size_t extent; // extent (in bits) of deque in history std::size_t extent; // extent (in bits) of deque in history
Time::base_type expire; // expiration of history Time::base_type expire; // expiration of history
PacketIDAEAD::aead_id_t id_high; // highest sequence number received PacketIDData::data_id_t id_high; // highest sequence number received
PacketIDAEAD::aead_id_t id_floor; // we will only accept backtrack IDs > id_floor PacketIDData::data_id_t id_floor; // we will only accept backtrack IDs > id_floor
//!< 32 or 64 bit packet counter //!< 32 or 64 bit packet counter
bool wide; bool wide;
@ -408,6 +440,6 @@ class PacketIDAEADReceiveType
// Our standard packet ID window with order=8 (window size=2048). // Our standard packet ID window with order=8 (window size=2048).
// and recv expire=30 seconds. // and recv expire=30 seconds.
typedef PacketIDAEADReceiveType<8, 30> PacketIDAEADReceive; typedef PacketIDDataReceiveType<8, 30> PacketIDDataReceive;
} // namespace openvpn } // namespace openvpn

View File

@ -32,7 +32,7 @@
#include <openvpn/common/memneq.hpp> #include <openvpn/common/memneq.hpp>
#include <openvpn/crypto/static_key.hpp> #include <openvpn/crypto/static_key.hpp>
#include <openvpn/crypto/cryptoalgs.hpp> #include <openvpn/crypto/cryptoalgs.hpp>
#include <openvpn/crypto/packet_id.hpp> #include <openvpn/crypto/packet_id_control.hpp>
#include <openvpn/ssl/psid.hpp> #include <openvpn/ssl/psid.hpp>
namespace openvpn { namespace openvpn {
@ -190,7 +190,7 @@ class TLSCryptContext : public RC<thread_unsafe_refcount>
// [OP] [PSID] [PID] [HMAC] [...] // [OP] [PSID] [PID] [HMAC] [...]
// //
constexpr const static size_t hmac_offset = 1 + ProtoSessionID::SIZE + PacketID::longidsize; constexpr const static size_t hmac_offset = 1 + ProtoSessionID::SIZE + PacketIDControl::idsize;
}; };

View File

@ -108,12 +108,12 @@ class Instance : public CryptoDCInstance
// should never be reached. // should never be reached.
// returns true if packet ID is close to wrapping // returns true if packet ID is close to wrapping
bool encrypt(BufferAllocated &buf, const PacketID::time_t now, const unsigned char *op32) override bool encrypt(BufferAllocated &buf, const unsigned char *op32) override
{ {
throw korekey_error("encrypt"); throw korekey_error("encrypt");
} }
Error::Type decrypt(BufferAllocated &buf, const PacketID::time_t now, const unsigned char *op32) override Error::Type decrypt(BufferAllocated &buf, const std::time_t now, const unsigned char *op32) override
{ {
throw korekey_error("decrypt"); throw korekey_error("decrypt");
} }

View File

@ -30,7 +30,7 @@
#include <openvpn/common/socktypes.hpp> #include <openvpn/common/socktypes.hpp>
#include <openvpn/buffer/buffer.hpp> #include <openvpn/buffer/buffer.hpp>
#include <openvpn/crypto/packet_id.hpp> #include <openvpn/crypto/packet_id_control.hpp>
#include <openvpn/reliable/relcommon.hpp> #include <openvpn/reliable/relcommon.hpp>
namespace openvpn { namespace openvpn {

View File

@ -24,13 +24,15 @@
#ifndef OPENVPN_RELIABLE_RELCOMMON_H #ifndef OPENVPN_RELIABLE_RELCOMMON_H
#define OPENVPN_RELIABLE_RELCOMMON_H #define OPENVPN_RELIABLE_RELCOMMON_H
#include <openvpn/crypto/packet_id.hpp> #include <openvpn/crypto/packet_id_control.hpp>
namespace openvpn { namespace openvpn {
namespace reliable { namespace reliable {
typedef PacketID::id_t id_t; typedef std::uint32_t id_t;
} constexpr static std::size_t id_size = sizeof(id_t);
} // namespace reliable
template <typename PACKET> template <typename PACKET>
class ReliableMessageBase class ReliableMessageBase

View File

@ -66,7 +66,7 @@
#include <openvpn/crypto/ovpnhmac.hpp> #include <openvpn/crypto/ovpnhmac.hpp>
#include <openvpn/crypto/tls_crypt.hpp> #include <openvpn/crypto/tls_crypt.hpp>
#include <openvpn/crypto/tls_crypt_v2.hpp> #include <openvpn/crypto/tls_crypt_v2.hpp>
#include <openvpn/crypto/packet_id.hpp> #include <openvpn/crypto/packet_id_control.hpp>
#include <openvpn/crypto/static_key.hpp> #include <openvpn/crypto/static_key.hpp>
#include <openvpn/crypto/bs64_data_limit.hpp> #include <openvpn/crypto/bs64_data_limit.hpp>
#include <openvpn/log/sessionstats.hpp> #include <openvpn/log/sessionstats.hpp>
@ -1183,7 +1183,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
const size_t adj = protocol.extra_transport_bytes() + // extra 2 bytes for TCP-streamed packet length const size_t adj = protocol.extra_transport_bytes() + // extra 2 bytes for TCP-streamed packet length
(enable_op32 ? 4 : 1) + // leading op (enable_op32 ? 4 : 1) + // leading op
comp_ctx.extra_payload_bytes() + // compression header comp_ctx.extra_payload_bytes() + // compression header
PacketID::size(PacketID::SHORT_FORM) + // sequence number PacketIDData::size(false) + // sequence number
dc_overhead; // data channel crypto layer overhead dc_overhead; // data channel crypto layer overhead
return (unsigned int)adj; return (unsigned int)adj;
} }
@ -1438,8 +1438,8 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
if (tls_wrap_mode == TLS_CRYPT || tls_wrap_mode == TLS_CRYPT_V2) if (tls_wrap_mode == TLS_CRYPT || tls_wrap_mode == TLS_CRYPT_V2)
{ {
PacketID pid; PacketIDControl pid;
pid.read(b, PacketID::LONG_FORM); pid.read(b);
out << " PID=" << pid.str(); out << " PID=" << pid.str();
const unsigned char *hmac = b.read_alloc(hmac_size); const unsigned char *hmac = b.read_alloc(hmac_size);
@ -1453,8 +1453,8 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
const unsigned char *hmac = b.read_alloc(hmac_size); const unsigned char *hmac = b.read_alloc(hmac_size);
out << " HMAC=" << render_hex(hmac, hmac_size); out << " HMAC=" << render_hex(hmac, hmac_size);
PacketID pid; PacketIDControl pid;
pid.read(b, PacketID::LONG_FORM); pid.read(b);
out << " PID=" << pid.str(); out << " PID=" << pid.str();
} }
@ -2213,7 +2213,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
} }
if (CryptoAlgs::mode(c.dc.cipher()) == CryptoAlgs::CBC_HMAC) if (CryptoAlgs::mode(c.dc.cipher()) == CryptoAlgs::CBC_HMAC)
payload_overhead += PacketID::size(PacketID::SHORT_FORM); payload_overhead += PacketIDData::size(false);
// account for IPv4 and TCP headers of the payload, mssfix method // account for IPv4 and TCP headers of the payload, mssfix method
// will add 20 extra bytes if payload is IPv6 // will add 20 extra bytes if payload is IPv6
@ -2225,7 +2225,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
// in CBC mode, the packet id is part of the payload size / overhead // in CBC mode, the packet id is part of the payload size / overhead
if (CryptoAlgs::mode(c.dc.cipher()) != CryptoAlgs::CBC_HMAC) if (CryptoAlgs::mode(c.dc.cipher()) != CryptoAlgs::CBC_HMAC)
overhead += PacketID::size(PacketID::SHORT_FORM); overhead += PacketIDData::size(false);
if (c.mss_parms.mtu) if (c.mss_parms.mtu)
{ {
@ -2361,7 +2361,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
orig_size, orig_size,
1 + ProtoSessionID::SIZE, 1 + ProtoSessionID::SIZE,
proto.hmac_size, proto.hmac_size,
PacketID::size(PacketID::LONG_FORM))) PacketIDControl::size()))
{ {
return false; return false;
} }
@ -2372,10 +2372,10 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
return false; return false;
// read tls_auth packet ID // read tls_auth packet ID
const PacketID pid = proto.ta_pid_recv.read_next(recv); const PacketIDControl pid = proto.ta_pid_recv.read_next(recv);
// get current time_t // get current time_t
const PacketID::time_t t = now->seconds_since_epoch(); const PacketIDControl::time_t t = now->seconds_since_epoch();
// verify tls_auth packet ID // verify tls_auth packet ID
const bool pid_ok = proto.ta_pid_recv.test_add(pid, t, false); const bool pid_ok = proto.ta_pid_recv.test_add(pid, t, false);
@ -2401,11 +2401,11 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
// get source PSID // get source PSID
ProtoSessionID src_psid(recv); ProtoSessionID src_psid(recv);
// read tls_auth packet ID // read tls_auth packet ID
const PacketID pid = proto.ta_pid_recv.read_next(recv); const PacketIDControl pid = proto.ta_pid_recv.read_next(recv);
recv.advance(proto.hmac_size); recv.advance(proto.hmac_size);
const size_t head_size = 1 + ProtoSessionID::SIZE + PacketID::size(PacketID::LONG_FORM); const size_t head_size = 1 + ProtoSessionID::SIZE + PacketIDControl::size();
const size_t data_offset = head_size + proto.hmac_size; const size_t data_offset = head_size + proto.hmac_size;
if (orig_size < data_offset) if (orig_size < data_offset)
return false; return false;
@ -2445,7 +2445,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
} }
// get current time_t // get current time_t
const PacketID::time_t t = now->seconds_since_epoch(); const PacketIDControl::time_t t = now->seconds_since_epoch();
// verify tls_auth packet ID // verify tls_auth packet ID
const bool pid_ok = proto.ta_pid_recv.test_add(pid, t, false); const bool pid_ok = proto.ta_pid_recv.test_add(pid, t, false);
@ -2507,7 +2507,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
static_assert(sizeof(op32) == OP_SIZE_V2, "OP_SIZE_V2 inconsistency"); static_assert(sizeof(op32) == OP_SIZE_V2, "OP_SIZE_V2 inconsistency");
// encrypt packet // encrypt packet
pid_wrap = crypto->encrypt(buf, now->seconds_since_epoch(), (const unsigned char *)&op32); pid_wrap = crypto->encrypt(buf, (const unsigned char *)&op32);
// prepend op // prepend op
buf.prepend((const unsigned char *)&op32, sizeof(op32)); buf.prepend((const unsigned char *)&op32, sizeof(op32));
@ -2515,7 +2515,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
else else
{ {
// encrypt packet // encrypt packet
pid_wrap = crypto->encrypt(buf, now->seconds_since_epoch(), nullptr); pid_wrap = crypto->encrypt(buf, nullptr);
// prepend op // prepend op
buf.push_front(op_compose(DATA_V1, key_id_)); buf.push_front(op_compose(DATA_V1, key_id_));
@ -3002,7 +3002,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
buf.size(), buf.size(),
1 + ProtoSessionID::SIZE, 1 + ProtoSessionID::SIZE,
proto.hmac_size, proto.hmac_size,
PacketID::size(PacketID::LONG_FORM)); PacketIDControl::size());
} }
void gen_head_tls_crypt(const unsigned int opcode, BufferAllocated &buf) void gen_head_tls_crypt(const unsigned int opcode, BufferAllocated &buf)
@ -3104,7 +3104,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
gen_head(ACK_V1, buf); gen_head(ACK_V1, buf);
} }
bool decapsulate_post_process(Packet &pkt, ProtoSessionID &src_psid, const PacketID pid) bool decapsulate_post_process(Packet &pkt, ProtoSessionID &src_psid, const PacketIDControl pid)
{ {
Buffer &recv = *pkt.buf; Buffer &recv = *pkt.buf;
@ -3116,7 +3116,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
return false; return false;
// get current time_t // get current time_t
const PacketID::time_t t = now->seconds_since_epoch(); const PacketIDControl::time_t t = now->seconds_since_epoch();
// verify tls_auth/crypt packet ID // verify tls_auth/crypt packet ID
const bool pid_ok = proto.ta_pid_recv.test_add(pid, t, false); const bool pid_ok = proto.ta_pid_recv.test_add(pid, t, false);
@ -3190,7 +3190,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
orig_size, orig_size,
1 + ProtoSessionID::SIZE, 1 + ProtoSessionID::SIZE,
proto.hmac_size, proto.hmac_size,
PacketID::size(PacketID::LONG_FORM))) PacketIDControl::size()))
{ {
proto.stats->error(Error::HMAC_ERROR); proto.stats->error(Error::HMAC_ERROR);
if (proto.is_tcp()) if (proto.is_tcp())
@ -3200,7 +3200,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
} }
// read tls_auth packet ID // read tls_auth packet ID
const PacketID pid = proto.ta_pid_recv.read_next(recv); const PacketIDControl pid = proto.ta_pid_recv.read_next(recv);
return decapsulate_post_process(pkt, src_psid, pid); return decapsulate_post_process(pkt, src_psid, pid);
} }
@ -3216,7 +3216,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
// get source PSID // get source PSID
ProtoSessionID src_psid(recv); ProtoSessionID src_psid(recv);
// get tls-crypt packet ID // get tls-crypt packet ID
const PacketID pid = proto.ta_pid_recv.read_next(recv); const PacketIDControl pid = proto.ta_pid_recv.read_next(recv);
// skip the hmac // skip the hmac
recv.advance(proto.hmac_size); recv.advance(proto.hmac_size);
@ -3319,7 +3319,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
const size_t orig_size = recv.size(); const size_t orig_size = recv.size();
const size_t hmac_size = proto.config->tls_crypt_context->digest_size(); const size_t hmac_size = proto.config->tls_crypt_context->digest_size();
const size_t tls_frame_size = 1 + ProtoSessionID::SIZE const size_t tls_frame_size = 1 + ProtoSessionID::SIZE
+ PacketID::size(PacketID::LONG_FORM) + PacketIDControl::size()
+ hmac_size + hmac_size
// the following is the tls-crypt payload // the following is the tls-crypt payload
+ sizeof(char) // length of ACK array + sizeof(char) // length of ACK array
@ -3631,7 +3631,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
net_buf.size(), net_buf.size(),
1 + ProtoSessionID::SIZE, 1 + ProtoSessionID::SIZE,
ta_hmac_recv->output_size(), ta_hmac_recv->output_size(),
PacketID::size(PacketID::LONG_FORM)); PacketIDControl::size());
} }
catch (BufferException &) catch (BufferException &)
{ {
@ -3825,8 +3825,8 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
// get HMAC size from Digest object // get HMAC size from Digest object
hmac_size = c.tls_crypt_context->digest_size(); hmac_size = c.tls_crypt_context->digest_size();
ta_pid_send.init(PacketID::LONG_FORM); ta_pid_send.init();
ta_pid_recv.init(PacketID::LONG_FORM, "SSL-CC", 0, stats); ta_pid_recv.init("SSL-CC", 0, stats);
reset_tls_crypt(c, dyn_key); reset_tls_crypt(c, dyn_key);
} }
@ -3874,7 +3874,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
upcoming_key_id = 0; upcoming_key_id = 0;
unsigned int key_dir; unsigned int key_dir;
const PacketID::id_t EARLY_NEG_START = 0x0f000000; const PacketIDControl::id_t EARLY_NEG_START = 0x0f000000;
// tls-auth initialization // tls-auth initialization
reset_tls_wrap_mode(c); reset_tls_wrap_mode(c);
@ -3883,8 +3883,8 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
case TLS_CRYPT: case TLS_CRYPT:
reset_tls_crypt(c, c.tls_key); reset_tls_crypt(c, c.tls_key);
// init tls_crypt packet ID // init tls_crypt packet ID
ta_pid_send.init(PacketID::LONG_FORM); ta_pid_send.init();
ta_pid_recv.init(PacketID::LONG_FORM, "SSL-CC", 0, stats); ta_pid_recv.init("SSL-CC", 0, stats);
break; break;
case TLS_CRYPT_V2: case TLS_CRYPT_V2:
if (is_server()) if (is_server())
@ -3897,8 +3897,8 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
/** tls-auth/tls-crypt packet id. We start with a different id here /** tls-auth/tls-crypt packet id. We start with a different id here
* to indicate EARLY_NEG_START/CONTROL_WKC_V1 support */ * to indicate EARLY_NEG_START/CONTROL_WKC_V1 support */
// init tls_crypt packet ID // init tls_crypt packet ID
ta_pid_send.init(PacketID::LONG_FORM, EARLY_NEG_START); ta_pid_send.init(EARLY_NEG_START);
ta_pid_recv.init(PacketID::LONG_FORM, "SSL-CC", 0, stats); ta_pid_recv.init("SSL-CC", 0, stats);
break; break;
case TLS_AUTH: case TLS_AUTH:
// init OvpnHMACInstance // init OvpnHMACInstance
@ -3929,8 +3929,8 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
* expect to handle the 1st packet, id 0. * expect to handle the 1st packet, id 0.
* *
*/ */
ta_pid_send.init(PacketID::LONG_FORM, cookie_psid.defined() ? 1 : 0); ta_pid_send.init(cookie_psid.defined() ? 1 : 0);
ta_pid_recv.init(PacketID::LONG_FORM, "SSL-CC", 0, stats); ta_pid_recv.init("SSL-CC", 0, stats);
break; break;
case TLS_PLAIN: case TLS_PLAIN:
break; break;
@ -4702,8 +4702,8 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
TLSCryptInstance::Ptr tls_crypt_server; TLSCryptInstance::Ptr tls_crypt_server;
TLSCryptMetadata::Ptr tls_crypt_metadata; TLSCryptMetadata::Ptr tls_crypt_metadata;
PacketIDSend ta_pid_send; PacketIDControlSend ta_pid_send;
PacketIDReceive ta_pid_recv; PacketIDControlReceive ta_pid_recv;
ProtoSessionID psid_self; ProtoSessionID psid_self;
ProtoSessionID psid_peer; ProtoSessionID psid_peer;

View File

@ -144,7 +144,7 @@ class PsidCookieImpl : public PsidCookie
{ {
static const size_t hmac_size = ta_hmac_recv_->output_size(); static const size_t hmac_size = ta_hmac_recv_->output_size();
// ovpn_hmac_cmp checks for adequate pkt_buf.size() // ovpn_hmac_cmp checks for adequate pkt_buf.size()
bool pkt_hmac_valid = ta_hmac_recv_->ovpn_hmac_cmp(pkt_buf.c_data(), pkt_buf.size(), 1 + SID_SIZE, hmac_size, long_pktid_size_); bool pkt_hmac_valid = ta_hmac_recv_->ovpn_hmac_cmp(pkt_buf.c_data(), pkt_buf.size(), 1 + SID_SIZE, hmac_size, PacketIDControl::idsize);
if (!pkt_hmac_valid) if (!pkt_hmac_valid)
{ {
// JMD_TODO: log failure? Logging DDoS? // JMD_TODO: log failure? Logging DDoS?
@ -155,7 +155,7 @@ class PsidCookieImpl : public PsidCookie
static const size_t reqd_packet_size static const size_t reqd_packet_size
// clang-format off // clang-format off
// [op_field] [cli_psid] [HMAC] [cli_auth_pktid] [cli_pktid] // [op_field] [cli_psid] [HMAC] [cli_auth_pktid] [cli_pktid]
= 1 + SID_SIZE + hmac_size + long_pktid_size_ + short_pktid_size_; = 1 + SID_SIZE + hmac_size + PacketIDControl::idsize + reliable::id_size;
// clang-format on // clang-format on
if (pkt_buf.size() < reqd_packet_size) if (pkt_buf.size() < reqd_packet_size)
{ {
@ -169,10 +169,13 @@ class PsidCookieImpl : public PsidCookie
// decapsulate_tls_auth // decapsulate_tls_auth
const ProtoSessionID cli_psid(recv_buf_copy); const ProtoSessionID cli_psid(recv_buf_copy);
recv_buf_copy.advance(hmac_size); recv_buf_copy.advance(hmac_size);
PacketID cli_auth_pktid; // a.k.a, replay_packet_id in draft RFC
cli_auth_pktid.read(recv_buf_copy, PacketID::LONG_FORM); PacketIDControl cli_auth_pktid; // a.k.a, replay_packet_id in draft RFC
PacketID cli_pktid; // a.k.a., packet_id in draft RFC cli_auth_pktid.read(recv_buf_copy);
cli_pktid.read(recv_buf_copy, PacketID::SHORT_FORM);
uint8_t cli_net_id[4]; // a.k.a., packet_id in draft RFC
recv_buf_copy.read(cli_net_id, sizeof(cli_net_id));
// start building the server reply HARD_RESET packet // start building the server reply HARD_RESET packet
BufferAllocated send_buf; BufferAllocated send_buf;
@ -186,12 +189,11 @@ class PsidCookieImpl : public PsidCookie
// prepend_dest_psid_and_acks // prepend_dest_psid_and_acks
cli_psid.prepend(send_buf); cli_psid.prepend(send_buf);
const id_t cli_net_id = htonl(cli_pktid.id); send_buf.prepend(cli_net_id, sizeof(cli_net_id));
send_buf.prepend((unsigned char *)&cli_net_id, sizeof(cli_net_id));
send_buf.push_front((unsigned char)1); send_buf.push_front((unsigned char)1);
// gen head // gen head
PacketIDSend svr_auth_pid(PacketID::LONG_FORM); PacketIDControlSend svr_auth_pid{};
svr_auth_pid.write_next(send_buf, true, now_->seconds_since_epoch()); svr_auth_pid.write_next(send_buf, true, now_->seconds_since_epoch());
// make space for tls-auth HMAC // make space for tls-auth HMAC
send_buf.prepend_alloc(ta_hmac_send_->output_size()); send_buf.prepend_alloc(ta_hmac_send_->output_size());
@ -202,7 +204,7 @@ class PsidCookieImpl : public PsidCookie
const unsigned char op_field = CookieHelper::get_server_hard_reset_opfield(); const unsigned char op_field = CookieHelper::get_server_hard_reset_opfield();
send_buf.push_front(op_field); send_buf.push_front(op_field);
// write hmac // write hmac
ta_hmac_send_->ovpn_hmac_gen(send_buf.data(), send_buf.size(), 1 + SID_SIZE, ta_hmac_send_->output_size(), long_pktid_size_); ta_hmac_send_->ovpn_hmac_gen(send_buf.data(), send_buf.size(), 1 + SID_SIZE, ta_hmac_send_->output_size(), PacketIDControl::idsize);
// consumer's implementation to send the SERVER_HARD_RESET to the client // consumer's implementation to send the SERVER_HARD_RESET to the client
bool send_ok = pctb_->psid_cookie_send_const(send_buf, pcaib); bool send_ok = pctb_->psid_cookie_send_const(send_buf, pcaib);
@ -218,7 +220,7 @@ class PsidCookieImpl : public PsidCookie
{ {
static const size_t hmac_size = ta_hmac_recv_->output_size(); static const size_t hmac_size = ta_hmac_recv_->output_size();
// ovpn_hmac_cmp checks for adequate pkt_buf.size() // ovpn_hmac_cmp checks for adequate pkt_buf.size()
bool pkt_hmac_valid = ta_hmac_recv_->ovpn_hmac_cmp(pkt_buf.c_data(), pkt_buf.size(), 1 + SID_SIZE, hmac_size, long_pktid_size_); bool pkt_hmac_valid = ta_hmac_recv_->ovpn_hmac_cmp(pkt_buf.c_data(), pkt_buf.size(), 1 + SID_SIZE, hmac_size, PacketIDControl::idsize);
if (!pkt_hmac_valid) if (!pkt_hmac_valid)
{ {
// JMD_TODO: log failure? Logging DDoS? // JMD_TODO: log failure? Logging DDoS?
@ -228,7 +230,7 @@ class PsidCookieImpl : public PsidCookie
static const size_t reqd_packet_size static const size_t reqd_packet_size
// clang-format off // clang-format off
// [op_field] [cli_psid] [HMAC] [cli_auth_pktid] [acked] [srv_psid] // [op_field] [cli_psid] [HMAC] [cli_auth_pktid] [acked] [srv_psid]
= 1 + SID_SIZE + hmac_size + long_pktid_size_ + 5 + SID_SIZE; = 1 + SID_SIZE + hmac_size + PacketIDControl::size() + 5 + SID_SIZE;
// the fixed size, 5, of the [acked] field recognizes that the client's first // the fixed size, 5, of the [acked] field recognizes that the client's first
// response will ack exactly one packet, the server's HARD_RESET // response will ack exactly one packet, the server's HARD_RESET
// clang-format on // clang-format on
@ -244,8 +246,10 @@ class PsidCookieImpl : public PsidCookie
// decapsulate_tls_auth // decapsulate_tls_auth
const ProtoSessionID cli_psid(recv_buf_copy); const ProtoSessionID cli_psid(recv_buf_copy);
recv_buf_copy.advance(hmac_size); recv_buf_copy.advance(hmac_size);
PacketID cli_auth_pktid; // a.k.a, replay_packet_id in draft RFC
cli_auth_pktid.read(recv_buf_copy, PacketID::LONG_FORM); PacketIDControl cli_auth_pktid; // a.k.a, replay_packet_id in draft RFC
cli_auth_pktid.read(recv_buf_copy);
unsigned int ack_count = recv_buf_copy[0]; unsigned int ack_count = recv_buf_copy[0];
if (ack_count != 1) if (ack_count != 1)
{ {
@ -349,8 +353,6 @@ class PsidCookieImpl : public PsidCookie
} }
static constexpr CryptoAlgs::Type digest_ = CryptoAlgs::Type::SHA256; static constexpr CryptoAlgs::Type digest_ = CryptoAlgs::Type::SHA256;
static constexpr size_t long_pktid_size_ = PacketID::size(PacketID::LONG_FORM);
static constexpr size_t short_pktid_size_ = PacketID::size(PacketID::SHORT_FORM);
const ProtoContext::ProtoConfig &pcfg_; const ProtoContext::ProtoConfig &pcfg_;
bool not_tls_auth_mode_; bool not_tls_auth_mode_;

View File

@ -63,7 +63,8 @@ add_executable(coreUnitTests
test_ostream_containers.cpp test_ostream_containers.cpp
test_parseargv.cpp test_parseargv.cpp
test_path.cpp test_path.cpp
test_pktid.cpp test_pktid_control.cpp
test_pktid_data.cpp
test_prefixlen.cpp test_prefixlen.cpp
test_randapi.cpp test_randapi.cpp
test_rc.cpp test_rc.cpp

View File

@ -149,11 +149,11 @@ void test_datachannel_crypto(bool tag_at_the_end, bool longpktcounter = false)
const unsigned char *data = work.data(); const unsigned char *data = work.data();
EXPECT_TRUE(std::memcmp(data, plaintext, std::strlen(plaintext)) == 0); EXPECT_TRUE(std::memcmp(data, plaintext, std::strlen(plaintext)) == 0);
const openvpn::PacketID::time_t now = 42; const std::time_t now = 42;
const unsigned char op32[]{7, 0, 0, 23}; const unsigned char op32[]{7, 0, 0, 23};
bool const wrapwarn = cryptodc.encrypt(work, now, op32); bool const wrapwarn = cryptodc.encrypt(work, op32);
ASSERT_FALSE(wrapwarn); ASSERT_FALSE(wrapwarn);
size_t pkt_counter_len = longpktcounter ? 8 : 4; size_t pkt_counter_len = longpktcounter ? 8 : 4;

View File

@ -1,29 +1,38 @@
#include "test_common.h" #include "test_common.h"
#include <openvpn/crypto/packet_id.hpp> #include <openvpn/crypto/packet_id_control.hpp>
#include <openvpn/crypto/packet_id_aead.hpp>
using namespace openvpn; using namespace openvpn;
struct PacketIDControlConstruct : public PacketIDControl
{
PacketIDControlConstruct(const PacketIDControl::time_t v_time = PacketIDControl::time_t(0), const PacketIDControl::id_t v_id = PacketIDControl::id_t(0))
{
id = v_id;
time = v_time;
}
};
template <typename PIDRecv> template <typename PIDRecv>
void testcase(PIDRecv &pr, void testcase(PIDRecv &pr,
const PacketID::time_t t, const PacketIDControl::time_t t,
const PacketID::time_t pkt_time, const PacketIDControl::time_t pkt_time,
const PacketID::id_t pkt_id, const PacketIDControl::id_t pkt_id,
const Error::Type expected_status) const Error::Type expected_status)
{ {
const PacketIDConstruct pid(pkt_time, pkt_id); const PacketIDControlConstruct pid(pkt_time, pkt_id);
const Error::Type status = pr.do_test_add(pid, t, true); const Error::Type status = pr.do_test_add(pid, t, true);
// OPENVPN_LOG("[" << t << "] id=" << pkt_id << " time=" << pkt_time << ' ' << Error::name(status)); // OPENVPN_LOG("[" << t << "] id=" << pkt_id << " time=" << pkt_time << ' ' << Error::name(status));
ASSERT_EQ(status, expected_status); ASSERT_EQ(status, expected_status);
} }
template <typename PIDRecv> TEST(misc, pktid_test_control)
void do_packet_id_recv_test()
{ {
typedef PacketIDControlReceiveType<3, 5> PIDRecv;
SessionStats::Ptr stats(new SessionStats()); SessionStats::Ptr stats(new SessionStats());
PIDRecv pr; PIDRecv pr;
pr.init(PacketID::SHORT_FORM, "test", 0, stats); pr.init("test", 0, stats);
testcase(pr, 0, 0, 0, Error::PKTID_INVALID); testcase(pr, 0, 0, 0, Error::PKTID_INVALID);
testcase(pr, 1, 0, 1, Error::SUCCESS); testcase(pr, 1, 0, 1, Error::SUCCESS);
@ -78,12 +87,6 @@ void do_packet_id_recv_test()
testcase(pr, 85, 15, 66, Error::SUCCESS); testcase(pr, 85, 15, 66, Error::SUCCESS);
} }
TEST(misc, pktid_test_normal)
{
do_packet_id_recv_test<PacketIDReceiveType<3, 5>>();
}
template <unsigned int ORDER, unsigned int EXPIRE> template <unsigned int ORDER, unsigned int EXPIRE>
void perfiter(const long n, void perfiter(const long n,
const long range, const long range,
@ -91,20 +94,20 @@ void perfiter(const long n,
const long iter_per_step_pre, const long iter_per_step_pre,
long &count) long &count)
{ {
typedef PacketIDReceiveType<ORDER, EXPIRE> PIDRecv; typedef PacketIDControlReceiveType<ORDER, EXPIRE> PIDRecv;
const long iter_per_step = iter_per_step_pre * step; const long iter_per_step = iter_per_step_pre * step;
// OPENVPN_LOG("ITER order=" << ORDER << " n=" << n << " range=" << range << " step=" << step << " iter_per_step=" // OPENVPN_LOG("ITER order=" << ORDER << " n=" << n << " range=" << range << " step=" << step << " iter_per_step="
// << iter_per_step); // << iter_per_step);
constexpr PacketID::time_t pkt_time = 1234; constexpr PacketIDControl::time_t pkt_time = 1234;
MTRand urand; MTRand urand;
std::vector<bool> bv(n); std::vector<bool> bv(n);
long high = 0; long high = 0;
SessionStats::Ptr stats(new SessionStats()); SessionStats::Ptr stats(new SessionStats());
PIDRecv pr; PIDRecv pr;
pr.init(PacketID::SHORT_FORM, "test", 0, stats); pr.init("test", 0, stats);
for (long i = 1; i < n; i += step) for (long i = 1; i < n; i += step)
{ {
@ -123,7 +126,7 @@ void perfiter(const long n,
expected = Error::PKTID_BACKTRACK; expected = Error::PKTID_BACKTRACK;
else if (bv[id]) else if (bv[id])
expected = Error::PKTID_REPLAY; expected = Error::PKTID_REPLAY;
const PacketIDConstruct pid(0, static_cast<unsigned>(id)); const PacketIDControlConstruct pid(0, static_cast<unsigned>(id));
const Error::Type result = pr.do_test_add(pid, pkt_time, true); const Error::Type result = pr.do_test_add(pid, pkt_time, true);
++count; ++count;
#define INFO "i=" << i << " id=" << id << " high=" << high << " result=" << Error::name(result) << " expected=" << Error::name(expected) #define INFO "i=" << i << " id=" << id << " high=" << high << " result=" << Error::name(result) << " expected=" << Error::name(expected)
@ -139,7 +142,7 @@ void perfiter(const long n,
template <unsigned int ORDER, unsigned int EXPIRE> template <unsigned int ORDER, unsigned int EXPIRE>
void perf(long &count) void perf(long &count)
{ {
typedef PacketIDReceiveType<ORDER, EXPIRE> PIDRecv; typedef PacketIDControlReceiveType<ORDER, EXPIRE> PIDRecv;
perfiter<ORDER, EXPIRE>(20000, PIDRecv::REPLAY_WINDOW_SIZE * 3, 1, 10, count); perfiter<ORDER, EXPIRE>(20000, PIDRecv::REPLAY_WINDOW_SIZE * 3, 1, 10, count);
perfiter<ORDER, EXPIRE>(20000, PIDRecv::REPLAY_WINDOW_SIZE * 3, PIDRecv::REPLAY_WINDOW_SIZE / 2, 10, count); perfiter<ORDER, EXPIRE>(20000, PIDRecv::REPLAY_WINDOW_SIZE * 3, PIDRecv::REPLAY_WINDOW_SIZE / 2, 10, count);
@ -151,8 +154,7 @@ void perf(long &count)
perfiter<ORDER, EXPIRE>(20000, 4, PIDRecv::REPLAY_WINDOW_SIZE / 2, 10, count); perfiter<ORDER, EXPIRE>(20000, 4, PIDRecv::REPLAY_WINDOW_SIZE / 2, 10, count);
} }
TEST(misc, pktid_perf) TEST(misc, pktid_control_perf)
{
{ {
long count = 0; long count = 0;
perf<3, 5>(count); perf<3, 5>(count);
@ -160,4 +162,3 @@ TEST(misc, pktid_perf)
perf<8, 5>(count); perf<8, 5>(count);
// ASSERT_EQ(4746439, count); // ASSERT_EQ(4746439, count);
} }
}

View File

@ -0,0 +1,264 @@
#include "test_common.h"
#include <openvpn/crypto/packet_id_control.hpp>
#include <openvpn/crypto/packet_id_data.hpp>
using namespace openvpn;
struct PacketIDDataConstruct : public PacketIDData
{
explicit PacketIDDataConstruct(const PacketIDData::data_id_t v_id = PacketIDData::data_id_t{0}, bool wide = false)
: PacketIDData(wide)
{
id = v_id;
}
};
template <typename PIDRecv>
void testcase(PIDRecv &pr,
const std::time_t t,
const PacketIDData::data_id_t pkt_id,
const Error::Type expected_status)
{
bool wide = pr.length() > 4;
const PacketIDDataConstruct pid(pkt_id, wide);
const Error::Type status = pr.do_test_add(pid, t);
// OPENVPN_LOG("[" << t << "] id=" << pkt_id << ' ' << Error::name(status));
ASSERT_EQ(status, expected_status);
}
void do_packet_id_recv_test_short_ids(bool usewide)
{
typedef PacketIDDataReceiveType<3, 5> PIDRecv;
SessionStats::Ptr stats(new SessionStats());
PIDRecv pr;
pr.init("test", 0, usewide, stats);
testcase(pr, 0, 0, Error::PKTID_INVALID);
testcase(pr, 1, 1, Error::SUCCESS);
testcase(pr, 1, 1, Error::PKTID_REPLAY); /* replay */
testcase(pr, 2, 2, Error::SUCCESS);
testcase(pr, 3, 4, Error::SUCCESS);
testcase(pr, 4, 1, Error::PKTID_REPLAY); /* replay */
testcase(pr, 5, 3, Error::SUCCESS);
testcase(pr, 6, 8, Error::SUCCESS);
testcase(pr, 10, 5, Error::SUCCESS);
testcase(pr, 15, 7, Error::PKTID_EXPIRE); /* expire backtrack */
testcase(pr, 20, 127, Error::SUCCESS);
testcase(pr, 20, 127, Error::PKTID_REPLAY);
testcase(pr, 21, 128, Error::SUCCESS);
testcase(pr, 22, 64, Error::PKTID_BACKTRACK); /* large backtrack */
testcase(pr, 23, 65, Error::SUCCESS);
testcase(pr, 24, 66, Error::SUCCESS);
testcase(pr, 30, 0, Error::PKTID_INVALID);
testcase(pr, 33, 3, Error::PKTID_BACKTRACK); /* time backtrack */
testcase(pr, 40, 0xfffffffe, Error::SUCCESS);
testcase(pr, 41, 0xffffffff, Error::SUCCESS);
}
TEST(misc, do_packet_id_recv_test_long_ids)
{
typedef PacketIDDataReceiveType<3, 5> PIDRecv;
PIDRecv pr;
SessionStats::Ptr stats{new SessionStats()};
pr.init("test", 0, true, stats);
testcase(pr, 40, 0xfffffffe, Error::SUCCESS);
testcase(pr, 41, 0xffffffff, Error::SUCCESS);
testcase(pr, 42, 0x100000001, Error::SUCCESS);
testcase(pr, 42, 0xffffff0d, Error::PKTID_BACKTRACK);
testcase(pr, 50, 0x200000000, Error::SUCCESS);
testcase(pr, 50, 0x500000000, Error::SUCCESS);
testcase(pr, 50, 0x400000000, Error::PKTID_BACKTRACK);
testcase(pr, 50, 0x399999999, Error::PKTID_BACKTRACK);
testcase(pr, 50, 0x3ffffffff, Error::PKTID_BACKTRACK);
testcase(pr, 50, 0x4ffffffff, Error::SUCCESS);
}
TEST(misc, pktid_test_data_32bit)
{
do_packet_id_recv_test_short_ids(false);
}
TEST(misc, pktid_test_data_64bit)
{
do_packet_id_recv_test_short_ids(true);
}
template <unsigned int ORDER, unsigned int EXPIRE>
void perfiter(const long n,
const long range,
const long step,
const long iter_per_step_pre,
long &count)
{
typedef PacketIDDataReceiveType<ORDER, EXPIRE> PIDRecv;
const long iter_per_step = iter_per_step_pre * step;
// OPENVPN_LOG("ITER order=" << ORDER << " n=" << n << " range=" << range << " step=" << step << " iter_per_step="
// << iter_per_step);
constexpr std::time_t pkt_time = 1234;
MTRand urand;
std::vector<bool> bv(n);
long high = 0;
SessionStats::Ptr stats(new SessionStats());
PIDRecv pr;
pr.init("test", 0, false, stats);
for (long i = 1; i < n; i += step)
{
for (long j = 0; j < iter_per_step; ++j)
{
const long delta = long(urand.randrange32(static_cast<uint32_t>(range))) - range / 2;
const long id = i + delta;
if (id >= 0 && id < n)
{
if (id > high)
high = id;
Error::Type expected = Error::SUCCESS;
if (!id)
expected = Error::PKTID_INVALID;
else if (high - id >= (const long)PIDRecv::REPLAY_WINDOW_SIZE)
expected = Error::PKTID_BACKTRACK;
else if (bv[id])
expected = Error::PKTID_REPLAY;
const PacketIDDataConstruct pid(id);
const Error::Type result = pr.do_test_add(pid, pkt_time);
++count;
#define INFO "i=" << i << " id=" << id << " high=" << high << " result=" << Error::name(result) << " expected=" << Error::name(expected)
// OPENVPN_LOG(INFO);
ASSERT_EQ(result, expected) << INFO;
if (expected == Error::SUCCESS)
bv[id] = true;
}
}
}
}
template <unsigned int ORDER, unsigned int EXPIRE>
void perf(long &count)
{
typedef PacketIDDataReceiveType<ORDER, EXPIRE> PIDRecv;
perfiter<ORDER, EXPIRE>(20000, PIDRecv::REPLAY_WINDOW_SIZE * 3, 1, 10, count);
perfiter<ORDER, EXPIRE>(20000, PIDRecv::REPLAY_WINDOW_SIZE * 3, PIDRecv::REPLAY_WINDOW_SIZE / 2, 10, count);
perfiter<ORDER, EXPIRE>(20000, PIDRecv::REPLAY_WINDOW_SIZE * 2, 1, 10, count);
perfiter<ORDER, EXPIRE>(20000, PIDRecv::REPLAY_WINDOW_SIZE * 2, PIDRecv::REPLAY_WINDOW_SIZE / 2, 10, count);
perfiter<ORDER, EXPIRE>(20000, 16, 1, 10, count);
perfiter<ORDER, EXPIRE>(20000, 16, PIDRecv::REPLAY_WINDOW_SIZE / 2, 10, count);
perfiter<ORDER, EXPIRE>(20000, 4, 1, 10, count);
perfiter<ORDER, EXPIRE>(20000, 4, PIDRecv::REPLAY_WINDOW_SIZE / 2, 10, count);
}
class PacketIDDataSendTest : public openvpn::PacketIDDataSend
{
public:
PacketIDDataSendTest(bool wide, std::uint64_t start)
: openvpn::PacketIDDataSend(wide)
{
pid_ = PacketIDDataConstruct{start, wide};
}
};
TEST(misc, pktid_32_bit_overrun_32bit_counter)
{
PacketIDDataSendTest pidsend{false, 0xfffffffc};
auto ret = pidsend.next();
EXPECT_EQ(ret.str(), "[0xfffffffd]");
ret = pidsend.next();
EXPECT_EQ(ret.str(), "[0xfffffffe]");
EXPECT_THROW(ret = pidsend.next(), PacketIDDataSend::packet_id_wrap);
}
TEST(misc, pktid_32_bit_overrun_64bit_counter)
{
PacketIDDataSendTest pidsend{true, 0xfffffffd};
auto ret = pidsend.next();
EXPECT_EQ(ret.str(), "[0xfffffffe]");
ret = pidsend.next();
EXPECT_EQ(ret.str(), "[0xffffffff]");
ret = pidsend.next();
EXPECT_EQ(ret.str(), "[0x100000000]");
ret = pidsend.next();
EXPECT_EQ(ret.str(), "[0x100000001]");
}
TEST(misc, pktid_64_bit_overrun_64bit_counter)
{
PacketIDDataSendTest pidsend{true, 0xfffffffffffffffc};
auto ret = pidsend.next();
EXPECT_EQ(ret.str(), "[0xfffffffffffffffd]");
ret = pidsend.next();
EXPECT_EQ(ret.str(), "[0xfffffffffffffffe]");
EXPECT_THROW(ret = pidsend.next(), PacketIDDataSend::packet_id_wrap);
}
TEST(misc, pktid_32_bit_warn)
{
PacketIDDataSendTest pidsend{false, 0xfefffffe};
EXPECT_FALSE(pidsend.wrap_warning());
auto ret = pidsend.next();
EXPECT_EQ(ret.str(), "[0xfeffffff]");
EXPECT_FALSE(pidsend.wrap_warning());
ret = pidsend.next();
EXPECT_EQ(ret.str(), "[0xff000000]");
EXPECT_TRUE(pidsend.wrap_warning());
ret = pidsend.next();
EXPECT_EQ(ret.str(), "[0xff000001]");
EXPECT_TRUE(pidsend.wrap_warning());
}
TEST(misc, pktid_64_bit_warn_32bit)
{
// Test we are not warning at 32bit
PacketIDDataSendTest pidsend{true, 0xfefffffe};
EXPECT_FALSE(pidsend.wrap_warning());
auto ret = pidsend.next();
EXPECT_EQ(ret.str(), "[0xfeffffff]");
EXPECT_FALSE(pidsend.wrap_warning());
ret = pidsend.next();
EXPECT_EQ(ret.str(), "[0xff000000]");
EXPECT_FALSE(pidsend.wrap_warning());
ret = pidsend.next();
EXPECT_EQ(ret.str(), "[0xff000001]");
EXPECT_FALSE(pidsend.wrap_warning());
}
TEST(misc, pktid_data_perf)
{
{
long count = 0;
perf<3, 5>(count);
perf<6, 5>(count);
perf<8, 5>(count);
// ASSERT_EQ(4746439, count);
}
}

View File

@ -18,7 +18,7 @@
#include <openvpn/reliable/relrecv.hpp> #include <openvpn/reliable/relrecv.hpp>
#include <openvpn/reliable/relsend.hpp> #include <openvpn/reliable/relsend.hpp>
#include <openvpn/reliable/relack.hpp> #include <openvpn/reliable/relack.hpp>
#include <openvpn/crypto/packet_id.hpp> #include <openvpn/crypto/packet_id_control.hpp>
using namespace openvpn; using namespace openvpn;
@ -154,14 +154,14 @@ typedef ReliableRecvTemplate<Packet> ReliableRecv;
struct Message struct Message
{ {
openvpn::PacketID::id_t id; openvpn::PacketIDControl::id_t id;
BufferPtr buffer; BufferPtr buffer;
}; };
void print_msg(const Time::Duration t, void print_msg(const Time::Duration t,
const char *title, const char *title,
BufferPtr &buf, BufferPtr &buf,
const openvpn::PacketID::id_t id, const openvpn::PacketIDControl::id_t id,
std::stringstream &case_detail) std::stringstream &case_detail)
{ {
case_detail << t.raw() << ' ' << title case_detail << t.raw() << ' ' << title
@ -175,7 +175,7 @@ void test(MTRand &rand,
const Time::Duration end, const Time::Duration end,
const Time::Duration step, const Time::Duration step,
const Time::Duration end_sends, const Time::Duration end_sends,
const openvpn::PacketID::id_t relsize, const openvpn::PacketIDControl::id_t relsize,
const size_t wiresize, const size_t wiresize,
const unsigned int reorder_prob, const unsigned int reorder_prob,
const unsigned int drop_prob, const unsigned int drop_prob,
@ -193,8 +193,8 @@ void test(MTRand &rand,
long iterations = 0; long iterations = 0;
Time::Duration t; Time::Duration t;
openvpn::PacketID::id_t send_id = 0; openvpn::PacketIDControl::id_t send_id = 0;
openvpn::PacketID::id_t rec_id = 0; openvpn::PacketIDControl::id_t rec_id = 0;
for (t = Time::Duration(); t < end; t += step) for (t = Time::Duration(); t < end; t += step)
{ {
@ -204,7 +204,7 @@ void test(MTRand &rand,
// sender processes ACKs received from receiver // sender processes ACKs received from receiver
while (!acklist.empty()) while (!acklist.empty())
{ {
const openvpn::PacketID::id_t id = acklist.front(); const openvpn::PacketIDControl::id_t id = acklist.front();
acklist.pop_front(); acklist.pop_front();
if (rand.randrange(40)) // with small probability, simulate a dropped ACK if (rand.randrange(40)) // with small probability, simulate a dropped ACK
// JMD_TODO: why wouldn't this have drop_prob probability // JMD_TODO: why wouldn't this have drop_prob probability
@ -221,7 +221,7 @@ void test(MTRand &rand,
// scan the sender history for un-ACKed packets that need to be retransmitted // scan the sender history for un-ACKed packets that need to be retransmitted
if (now >= retrans) if (now >= retrans)
{ {
for (openvpn::PacketID::id_t i = send.head_id(); i < send.tail_id(); ++i) for (openvpn::PacketIDControl::id_t i = send.head_id(); i < send.tail_id(); ++i)
{ {
ReliableSend::Message &m = send.ref_by_id(i); ReliableSend::Message &m = send.ref_by_id(i);
if (m.ready_retransmit(now)) if (m.ready_retransmit(now))
@ -327,7 +327,7 @@ void test(MTRand &rand,
struct test_params struct test_params
{ {
int test_case; int test_case;
openvpn::PacketID::id_t relsize; openvpn::PacketIDControl::id_t relsize;
size_t wiresize; size_t wiresize;
unsigned int reorder_prob; unsigned int reorder_prob;
unsigned int drop_prob; unsigned int drop_prob;