diff --git a/openvpn/crypto/crypto_aead.hpp b/openvpn/crypto/crypto_aead.hpp index 90ba38fc..da3cc3aa 100644 --- a/openvpn/crypto/crypto_aead.hpp +++ b/openvpn/crypto/crypto_aead.hpp @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include @@ -85,12 +85,12 @@ class Crypto : public CryptoDCInstance } // 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 */ 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); if (op32) { @@ -102,13 +102,13 @@ class Crypto : public CryptoDCInstance } // 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()); } // 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 */ std::memcpy(data, ref.data, sizeof(data)); @@ -125,10 +125,10 @@ class Crypto : public CryptoDCInstance } // 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); - const PacketIDAEAD pid = pid_recv.read_next(buf); + Buffer buf(data + data_offset_pkt_id, PacketIDData::long_id_size, true); + const PacketIDData pid = pid_recv.read_next(buf); 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; } - 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(); } - 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(); } @@ -168,7 +168,7 @@ class Crypto : public CryptoDCInstance { typename CRYPTO_API::CipherContextAEAD impl; Nonce nonce; - PacketIDAEADSend pid_send{false}; + PacketIDDataSend pid_send{false}; BufferAllocated work; }; @@ -176,7 +176,7 @@ class Crypto : public CryptoDCInstance { typename CRYPTO_API::CipherContextAEAD impl; Nonce nonce; - PacketIDAEADReceive pid_recv{}; + PacketIDDataReceive pid_recv{}; BufferAllocated work; }; @@ -197,7 +197,7 @@ class Crypto : public CryptoDCInstance // Encrypt/Decrypt // 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 if (buf.size()) @@ -251,7 +251,7 @@ class Crypto : public CryptoDCInstance 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 if (buf.size()) @@ -337,7 +337,7 @@ class Crypto : public CryptoDCInstance const int recv_unit, 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); } diff --git a/openvpn/crypto/crypto_chm.hpp b/openvpn/crypto/crypto_chm.hpp index 5fa13aa6..fa149b79 100644 --- a/openvpn/crypto/crypto_chm.hpp +++ b/openvpn/crypto/crypto_chm.hpp @@ -58,13 +58,13 @@ class CryptoCHM : public CryptoDCInstance // Encrypt/Decrypt /* 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(); } - 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); } @@ -90,10 +90,10 @@ class CryptoCHM : public CryptoDCInstance const SessionStats::Ptr &recv_stats_arg) override { /* CBC encryption always uses short packet ID */ - auto pid_form = PacketID::SHORT_FORM; + constexpr bool wide = false; - encrypt_.pid_send.init(pid_form); - decrypt_.pid_recv.init(pid_form, recv_name, recv_unit, recv_stats_arg); + encrypt_.pid_send = PacketIDDataSend{wide}; + decrypt_.pid_recv.init(recv_name, recv_unit, wide, recv_stats_arg); } bool consider_compression(const CompressContext &comp_ctx) override diff --git a/openvpn/crypto/cryptodc.hpp b/openvpn/crypto/cryptodc.hpp index bc85204c..f65a979d 100644 --- a/openvpn/crypto/cryptodc.hpp +++ b/openvpn/crypto/cryptodc.hpp @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include @@ -48,9 +48,9 @@ class CryptoDCInstance : public RC // Encrypt/Decrypt // 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 diff --git a/openvpn/crypto/decrypt_chm.hpp b/openvpn/crypto/decrypt_chm.hpp index c3a0d42b..d55c3f93 100644 --- a/openvpn/crypto/decrypt_chm.hpp +++ b/openvpn/crypto/decrypt_chm.hpp @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include namespace openvpn { @@ -45,7 +45,7 @@ class DecryptCHM public: 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 if (!buf.size()) @@ -118,19 +118,13 @@ class DecryptCHM Frame::Ptr frame; CipherContext cipher; OvpnHMAC hmac; - PacketIDReceive pid_recv; + PacketIDDataReceive pid_recv; 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 - if (pid_recv.initialized()) - { - const PacketID pid = pid_recv.read_next(buf); - if (!pid_recv.test_add(pid, now, true)) // verify packet ID - return false; - } - return true; + const PacketIDData pid = pid_recv.read_next(buf); + return pid_recv.test_add(pid, now); } BufferAllocated work; diff --git a/openvpn/crypto/encrypt_chm.hpp b/openvpn/crypto/encrypt_chm.hpp index 3bb9677f..8f0d3bfd 100644 --- a/openvpn/crypto/encrypt_chm.hpp +++ b/openvpn/crypto/encrypt_chm.hpp @@ -35,7 +35,7 @@ #include #include #include -#include +#include namespace openvpn { template @@ -44,7 +44,7 @@ class EncryptCHM public: OPENVPN_SIMPLE_EXCEPTION(chm_unsupported_cipher_mode); - void encrypt(BufferAllocated &buf, const PacketID::time_t now) + void encrypt(BufferAllocated &buf) { // skip null packets if (!buf.size()) @@ -64,7 +64,7 @@ class EncryptCHM rng->rand_bytes(iv_buf, iv_length); // generate fresh outgoing packet ID and prepend to cleartext buffer - pid_send.write_next(buf, true, now); + pid_send.prepend_next(buf); } else { @@ -95,7 +95,7 @@ class EncryptCHM else // no encryption { // 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 prepend_hmac(buf); @@ -110,7 +110,7 @@ class EncryptCHM Frame::Ptr frame; CipherContext cipher; OvpnHMAC hmac; - PacketIDSend pid_send; + PacketIDDataSend pid_send{false}; private: // compute HMAC signature of data buffer, diff --git a/openvpn/crypto/packet_id.hpp b/openvpn/crypto/packet_id_control.hpp similarity index 69% rename from openvpn/crypto/packet_id.hpp rename to openvpn/crypto/packet_id_control.hpp index 3eb3e8bf..f150184d 100644 --- a/openvpn/crypto/packet_id.hpp +++ b/openvpn/crypto/packet_id_control.hpp @@ -21,8 +21,7 @@ // Manage OpenVPN protocol Packet IDs for packet replay detection -#ifndef OPENVPN_CRYPTO_PACKET_ID_H -#define OPENVPN_CRYPTO_PACKET_ID_H +#pragma once #include #include @@ -42,59 +41,38 @@ namespace openvpn { /* - * Communicate packet-id over the wire. - * A short packet-id is just a 32 bit - * sequence number. A long packet-id - * includes a timestamp as well. + * Control channel Packet ID. These IDs have the format * - * Long packet-ids are used as IVs for - * CFB/OFB ciphers. + * | 32 bit integer timestamp in BE | 32 bit packet counter in BE | * - * This data structure is always sent - * over the net in network byte order, - * by calling htonl, ntohl, on the - * 32-bit data elements, id_t and - * net_time_t, to change them to and - * from network order. + * This format of long packet-ids is also used as IVs for CFB/OFB ciphers + * in OpenVPN 2.x but OpenVPN 3.x supports only CBC and AEAD ciphers, so + * it is only used for control channel and control chanel authentication/encryption + * schemes like tls-auth/tls-crypt. * - * In addition, time is converted to - * a PacketID::net_time_t before sending, - * since openvpn always - * uses a 32-bit time_t but some - * 64 bit platforms use a - * 64 bit time_t. + * This data structure is always sent over the net in network byte order, + * by calling htonl, ntohl, on the 32-bit data elements, id_t and + * net_time_t, to change them to and from network order. */ -struct PacketID +struct PacketIDControl { typedef std::uint32_t id_t; typedef std::uint32_t net_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 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 longidsize; - else - return shortidsize; + return idsize; } - constexpr static size_t shortidsize = sizeof(id_t); - constexpr static size_t longidsize = sizeof(id_t) + sizeof(net_time_t); + constexpr static size_t idsize = sizeof(id_t) + sizeof(net_time_t); bool is_valid() const { - return id != UNDEF; + return id != 0; } void reset() @@ -104,7 +82,7 @@ struct PacketID } template // so it can take a Buffer or a ConstBuffer - void read(BufType &buf, const int form) + void read(BufType &buf) { id_t net_id; net_time_t net_time; @@ -112,16 +90,11 @@ struct PacketID buf.read((unsigned char *)&net_id, sizeof(net_id)); id = ntohl(net_id); - if (form == LONG_FORM) - { - buf.read((unsigned char *)&net_time, sizeof(net_time)); - time = ntohl(net_time); - } - else - time = time_t(0); + buf.read((unsigned char *)&net_time, sizeof(net_time)); + time = ntohl(net_time); } - 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 net_time_t net_time = htonl(static_cast(time & 0x00000000FFFFFFFF)); @@ -130,15 +103,13 @@ struct PacketID 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)); } else { 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: 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 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_.time = PacketID::time_t(0); - form_ = form; + pid_.time = PacketIDControl::time_t(0); } - PacketID next(const PacketID::time_t now) + PacketIDControl next(const PacketIDControl::time_t now) { - PacketID ret; + PacketIDControl ret; if (!pid_.time) pid_.time = now; ret.id = ++pid_.id; if (unlikely(!pid_.id)) // wraparound { - if (form_ != PacketID::LONG_FORM) - throw packet_id_wrap(); pid_.time = now; ret.id = pid_.id = 1; } @@ -202,35 +158,22 @@ class PacketIDSend 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); - pid.write(buf, form_, 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; + const PacketIDControl pid = next(now); + pid.write(buf, prepend); } std::string str() const { std::string ret; ret = pid_.str(); - if (form_ == PacketID::LONG_FORM) - ret += 'L'; + ret += 'L'; return ret; } private: - PacketID pid_; - int form_; + PacketIDControl pid_; }; /* @@ -243,7 +186,7 @@ class PacketIDSend */ template -class PacketIDReceiveType +class PacketIDControlReceiveType { public: static constexpr unsigned int REPLAY_WINDOW_BYTES = 1 << REPLAY_WINDOW_ORDER; @@ -252,13 +195,11 @@ class PacketIDReceiveType OPENVPN_SIMPLE_EXCEPTION(packet_id_not_initialized); // TODO: [OVPN3-933] Consider RAII'ifying this code - PacketIDReceiveType() - : initialized_(false) + PacketIDControlReceiveType() { } - void init(const int form_arg, - const char *name_arg, + void init(const char *name_arg, const int unit_arg, const SessionStats::Ptr &stats_arg) { @@ -270,20 +211,19 @@ class PacketIDReceiveType time_high = 0; id_floor = 0; max_backtrack = 0; - form = form_arg; unit = unit_arg; name = name_arg; stats = stats_arg; std::memset(history, 0, sizeof(history)); } - bool initialized() const + [[nodiscard]] bool initialized() const { return initialized_; } - bool test_add(const PacketID &pin, - const PacketID::time_t now, + bool test_add(const PacketIDControl &pin, + const PacketIDControl::time_t now, const bool mod) // don't modify history unless mod is true { const Error::Type err = do_test_add(pin, now, mod); @@ -296,8 +236,8 @@ class PacketIDReceiveType return true; } - Error::Type do_test_add(const PacketID &pin, - const PacketID::time_t now, + Error::Type do_test_add(const PacketIDControl &pin, + const PacketIDControl::time_t now, const bool mod) // don't modify history unless mod is true { // make sure we were initialized @@ -402,12 +342,12 @@ class PacketIDReceiveType return Error::SUCCESS; } - PacketID read_next(Buffer &buf) const + PacketIDControl read_next(Buffer &buf) const { if (!initialized_) throw packet_id_not_initialized(); - PacketID pid; - pid.read(buf, form); + PacketIDControl pid{}; + pid.read(buf); return pid; } @@ -424,18 +364,17 @@ class PacketIDReceiveType return (base + i) & (REPLAY_WINDOW_SIZE - 1); } - bool initialized_; + bool initialized_ = false; - unsigned int base; // bit position of deque base in history - unsigned int extent; // extent (in bits) of deque in history - PacketID::time_t expire; // expiration of history - PacketID::id_t id_high; // highest sequence number received - PacketID::time_t time_high; // highest time stamp received - PacketID::id_t id_floor; // we will only accept backtrack IDs > id_floor - unsigned int max_backtrack; + unsigned int base = 0; // bit position of deque base in history + unsigned int extent = 0; // extent (in bits) of deque in history + PacketIDControl::time_t expire = 0; // expiration of history + PacketIDControl::id_t id_high = 0; // highest sequence number received + PacketIDControl::time_t time_high = 0; // highest time stamp received + PacketIDControl::id_t id_floor = 0; // we will only accept backtrack IDs > id_floor + unsigned int max_backtrack = 0; - int form; // PacketID::LONG_FORM or PacketID::SHORT_FORM - int unit; // unit number of this object (for debugging) + int unit = -1; // unit number of this object (for debugging) std::string name; // name of this object (for debugging) SessionStats::Ptr stats; @@ -445,8 +384,6 @@ class PacketIDReceiveType // Our standard packet ID window with order=8 (window size=2048). // and recv expire=30 seconds. -typedef PacketIDReceiveType<8, 30> PacketIDReceive; +typedef PacketIDControlReceiveType<8, 30> PacketIDControlReceive; } // namespace openvpn - -#endif // OPENVPN_CRYPTO_PACKET_ID_H diff --git a/openvpn/crypto/packet_id_aead.hpp b/openvpn/crypto/packet_id_data.hpp similarity index 81% rename from openvpn/crypto/packet_id_aead.hpp rename to openvpn/crypto/packet_id_data.hpp index 06eca020..33160af4 100644 --- a/openvpn/crypto/packet_id_aead.hpp +++ b/openvpn/crypto/packet_id_data.hpp @@ -43,24 +43,25 @@ 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 - * 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 class is different from PacketID in the way that it always uses - * a "flat" packet id that is either 32 or 64 bit while PacketID has a long + * 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 PacketIDControl has a long * packet id that is 32bit + 32bit but follow different rules and includes - * a timestamp. Merging PacketIDAEAD and PacketID would result in a much - * more convoluted and hard to understand class than keeping them seperate + * a timestamp. Merging PacketIData and PacketIDControl would result in a much + * 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; /** @@ -81,12 +82,12 @@ struct PacketIDAEAD } - explicit PacketIDAEAD(bool wide_arg) + explicit PacketIDData(bool 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) { } @@ -94,14 +95,14 @@ struct PacketIDAEAD constexpr static std::size_t short_id_size = sizeof(std::uint32_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; } 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(&net_id), sizeof(net_id)); + } + else + { + const std::uint32_t net_id = htonl(static_cast(id)); + buf.prepend(reinterpret_cast(&net_id), sizeof(net_id)); + } + } + + + [[nodiscard]] std::string str() const { std::ostringstream os; os << std::hex << "[0x" << id << "]"; @@ -147,12 +164,12 @@ struct PacketIDAEAD } }; -class PacketIDAEADSend +class PacketIDDataSend { public: OPENVPN_SIMPLE_EXCEPTION(packet_id_wrap); - PacketIDAEADSend(bool wide_arg) + explicit PacketIDDataSend(bool wide_arg) : pid_(wide_arg) { } @@ -163,10 +180,10 @@ class PacketIDAEADSend * @throws packet_id_wrap if the packet id space is exhausted * @return packet id to use next. */ - PacketIDAEAD next() + [[nodiscard]] PacketIDData next() { ++pid_.id; - PacketIDAEAD ret{pid_.wide, pid_.id}; + PacketIDData ret{pid_.wide, pid_.id}; if (!pid_.wide && unlikely(pid_.id == std::numeric_limits::max())) // wraparound { throw packet_id_wrap(); @@ -184,26 +201,21 @@ class PacketIDAEADSend */ void write_next(Buffer &buf) { - const PacketIDAEAD pid = next(); + const PacketIDData pid = next(); pid.write(buf); } /** - * 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. + * increases the packet id and prepends it to a buffer + * @param buf buffer to write to */ - bool wrap_warning() const + void prepend_next(Buffer &buf) { - if (pid_.wide) - return false; - - const PacketIDAEAD::aead_id_t wrap_at = 0xFF000000; - return pid_.id >= wrap_at; + const PacketIDData pid = next(); + pid.write_prepend(buf); } - std::string str() const + [[nodiscard]] std::string str() const { std::string ret; ret = pid_.str(); @@ -221,8 +233,28 @@ class PacketIDAEADSend 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 -class PacketIDAEADReceiveType +class PacketIDDataReceiveType { public: 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 * @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 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 * @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) { // expire backtracks at or below id_floor after PKTID_RECV_EXPIRE time @@ -364,9 +396,9 @@ class PacketIDAEADReceiveType return Error::SUCCESS; } - PacketIDAEAD read_next(Buffer &buf) const + PacketIDData read_next(Buffer &buf) const { - PacketIDAEAD pid{wide}; + PacketIDData pid{wide}; pid.read(buf); return pid; } @@ -380,11 +412,11 @@ class PacketIDAEADReceiveType [[nodiscard]] std::size_t constexpr length() const { - return PacketIDAEAD::size(wide); + return PacketIDData::size(wide); } 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); } @@ -392,8 +424,8 @@ class PacketIDAEADReceiveType std::size_t base; // bit position of deque base in history std::size_t extent; // extent (in bits) of deque in history Time::base_type expire; // expiration of history - PacketIDAEAD::aead_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_high; // highest sequence number received + PacketIDData::data_id_t id_floor; // we will only accept backtrack IDs > id_floor //!< 32 or 64 bit packet counter bool wide; @@ -408,6 +440,6 @@ class PacketIDAEADReceiveType // Our standard packet ID window with order=8 (window size=2048). // and recv expire=30 seconds. -typedef PacketIDAEADReceiveType<8, 30> PacketIDAEADReceive; +typedef PacketIDDataReceiveType<8, 30> PacketIDDataReceive; } // namespace openvpn diff --git a/openvpn/crypto/tls_crypt.hpp b/openvpn/crypto/tls_crypt.hpp index 17f02b0d..40341c77 100644 --- a/openvpn/crypto/tls_crypt.hpp +++ b/openvpn/crypto/tls_crypt.hpp @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include namespace openvpn { @@ -190,7 +190,7 @@ class TLSCryptContext : public RC // [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; }; diff --git a/openvpn/dco/korekey.hpp b/openvpn/dco/korekey.hpp index bcdf4e1e..d048b863 100644 --- a/openvpn/dco/korekey.hpp +++ b/openvpn/dco/korekey.hpp @@ -108,12 +108,12 @@ class Instance : public CryptoDCInstance // should never be reached. // 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"); } - 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"); } diff --git a/openvpn/reliable/relack.hpp b/openvpn/reliable/relack.hpp index a54a28a7..d2741b16 100644 --- a/openvpn/reliable/relack.hpp +++ b/openvpn/reliable/relack.hpp @@ -30,7 +30,7 @@ #include #include -#include +#include #include namespace openvpn { diff --git a/openvpn/reliable/relcommon.hpp b/openvpn/reliable/relcommon.hpp index 1b8e8903..e0387d79 100644 --- a/openvpn/reliable/relcommon.hpp +++ b/openvpn/reliable/relcommon.hpp @@ -24,13 +24,15 @@ #ifndef OPENVPN_RELIABLE_RELCOMMON_H #define OPENVPN_RELIABLE_RELCOMMON_H -#include +#include namespace openvpn { 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 class ReliableMessageBase diff --git a/openvpn/ssl/proto.hpp b/openvpn/ssl/proto.hpp index 084cb3c0..42888275 100644 --- a/openvpn/ssl/proto.hpp +++ b/openvpn/ssl/proto.hpp @@ -66,7 +66,7 @@ #include #include #include -#include +#include #include #include #include @@ -1180,11 +1180,11 @@ class ProtoContext : public logging::LoggingMixinencrypt(buf, now->seconds_since_epoch(), (const unsigned char *)&op32); + pid_wrap = crypto->encrypt(buf, (const unsigned char *)&op32); // prepend op buf.prepend((const unsigned char *)&op32, sizeof(op32)); @@ -2515,7 +2515,7 @@ class ProtoContext : public logging::LoggingMixinencrypt(buf, now->seconds_since_epoch(), nullptr); + pid_wrap = crypto->encrypt(buf, nullptr); // prepend op buf.push_front(op_compose(DATA_V1, key_id_)); @@ -3002,7 +3002,7 @@ class ProtoContext : public logging::LoggingMixinseconds_since_epoch(); + const PacketIDControl::time_t t = now->seconds_since_epoch(); // verify tls_auth/crypt packet ID const bool pid_ok = proto.ta_pid_recv.test_add(pid, t, false); @@ -3190,7 +3190,7 @@ class ProtoContext : public logging::LoggingMixinerror(Error::HMAC_ERROR); if (proto.is_tcp()) @@ -3200,7 +3200,7 @@ class ProtoContext : public logging::LoggingMixintls_crypt_context->digest_size(); const size_t tls_frame_size = 1 + ProtoSessionID::SIZE - + PacketID::size(PacketID::LONG_FORM) + + PacketIDControl::size() + hmac_size // the following is the tls-crypt payload + sizeof(char) // length of ACK array @@ -3631,7 +3631,7 @@ class ProtoContext : public logging::LoggingMixinoutput_size(), - PacketID::size(PacketID::LONG_FORM)); + PacketIDControl::size()); } catch (BufferException &) { @@ -3825,8 +3825,8 @@ class ProtoContext : public logging::LoggingMixindigest_size(); - ta_pid_send.init(PacketID::LONG_FORM); - ta_pid_recv.init(PacketID::LONG_FORM, "SSL-CC", 0, stats); + ta_pid_send.init(); + ta_pid_recv.init("SSL-CC", 0, stats); reset_tls_crypt(c, dyn_key); } @@ -3874,7 +3874,7 @@ class ProtoContext : public logging::LoggingMixinoutput_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) { // JMD_TODO: log failure? Logging DDoS? @@ -155,7 +155,7 @@ class PsidCookieImpl : public PsidCookie static const size_t reqd_packet_size // clang-format off // [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 if (pkt_buf.size() < reqd_packet_size) { @@ -169,10 +169,13 @@ class PsidCookieImpl : public PsidCookie // decapsulate_tls_auth const ProtoSessionID cli_psid(recv_buf_copy); 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); - PacketID cli_pktid; // a.k.a., packet_id in draft RFC - cli_pktid.read(recv_buf_copy, PacketID::SHORT_FORM); + + PacketIDControl cli_auth_pktid; // a.k.a, replay_packet_id in draft RFC + cli_auth_pktid.read(recv_buf_copy); + + 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 BufferAllocated send_buf; @@ -186,12 +189,11 @@ class PsidCookieImpl : public PsidCookie // prepend_dest_psid_and_acks cli_psid.prepend(send_buf); - const id_t cli_net_id = htonl(cli_pktid.id); - send_buf.prepend((unsigned char *)&cli_net_id, sizeof(cli_net_id)); + send_buf.prepend(cli_net_id, sizeof(cli_net_id)); send_buf.push_front((unsigned char)1); // 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()); // make space for tls-auth HMAC 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(); send_buf.push_front(op_field); // 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 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(); // 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) { // JMD_TODO: log failure? Logging DDoS? @@ -228,7 +230,7 @@ class PsidCookieImpl : public PsidCookie static const size_t reqd_packet_size // clang-format off // [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 // response will ack exactly one packet, the server's HARD_RESET // clang-format on @@ -244,8 +246,10 @@ class PsidCookieImpl : public PsidCookie // decapsulate_tls_auth const ProtoSessionID cli_psid(recv_buf_copy); 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]; if (ack_count != 1) { @@ -349,8 +353,6 @@ class PsidCookieImpl : public PsidCookie } 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_; bool not_tls_auth_mode_; diff --git a/test/unittests/CMakeLists.txt b/test/unittests/CMakeLists.txt index 544683f6..d545fc80 100644 --- a/test/unittests/CMakeLists.txt +++ b/test/unittests/CMakeLists.txt @@ -63,7 +63,8 @@ add_executable(coreUnitTests test_ostream_containers.cpp test_parseargv.cpp test_path.cpp - test_pktid.cpp + test_pktid_control.cpp + test_pktid_data.cpp test_prefixlen.cpp test_randapi.cpp test_rc.cpp diff --git a/test/unittests/test_crypto.cpp b/test/unittests/test_crypto.cpp index 6cecdece..396a9f5c 100644 --- a/test/unittests/test_crypto.cpp +++ b/test/unittests/test_crypto.cpp @@ -149,11 +149,11 @@ void test_datachannel_crypto(bool tag_at_the_end, bool longpktcounter = false) const unsigned char *data = work.data(); 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}; - bool const wrapwarn = cryptodc.encrypt(work, now, op32); + bool const wrapwarn = cryptodc.encrypt(work, op32); ASSERT_FALSE(wrapwarn); size_t pkt_counter_len = longpktcounter ? 8 : 4; diff --git a/test/unittests/test_pktid.cpp b/test/unittests/test_pktid_control.cpp similarity index 82% rename from test/unittests/test_pktid.cpp rename to test/unittests/test_pktid_control.cpp index 652fa2ad..38292d38 100644 --- a/test/unittests/test_pktid.cpp +++ b/test/unittests/test_pktid_control.cpp @@ -1,29 +1,38 @@ #include "test_common.h" -#include -#include +#include 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 void testcase(PIDRecv &pr, - const PacketID::time_t t, - const PacketID::time_t pkt_time, - const PacketID::id_t pkt_id, + const PacketIDControl::time_t t, + const PacketIDControl::time_t pkt_time, + const PacketIDControl::id_t pkt_id, 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); // OPENVPN_LOG("[" << t << "] id=" << pkt_id << " time=" << pkt_time << ' ' << Error::name(status)); ASSERT_EQ(status, expected_status); } -template -void do_packet_id_recv_test() +TEST(misc, pktid_test_control) { + typedef PacketIDControlReceiveType<3, 5> PIDRecv; SessionStats::Ptr stats(new SessionStats()); 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, 1, 0, 1, Error::SUCCESS); @@ -78,12 +87,6 @@ void do_packet_id_recv_test() testcase(pr, 85, 15, 66, Error::SUCCESS); } -TEST(misc, pktid_test_normal) -{ - do_packet_id_recv_test>(); -} - - template void perfiter(const long n, const long range, @@ -91,20 +94,20 @@ void perfiter(const long n, const long iter_per_step_pre, long &count) { - typedef PacketIDReceiveType PIDRecv; + typedef PacketIDControlReceiveType 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 PacketID::time_t pkt_time = 1234; + constexpr PacketIDControl::time_t pkt_time = 1234; MTRand urand; std::vector bv(n); long high = 0; SessionStats::Ptr stats(new SessionStats()); PIDRecv pr; - pr.init(PacketID::SHORT_FORM, "test", 0, stats); + pr.init("test", 0, stats); for (long i = 1; i < n; i += step) { @@ -123,7 +126,7 @@ void perfiter(const long n, expected = Error::PKTID_BACKTRACK; else if (bv[id]) expected = Error::PKTID_REPLAY; - const PacketIDConstruct pid(0, static_cast(id)); + const PacketIDControlConstruct pid(0, static_cast(id)); const Error::Type result = pr.do_test_add(pid, pkt_time, true); ++count; #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 void perf(long &count) { - typedef PacketIDReceiveType PIDRecv; + typedef PacketIDControlReceiveType PIDRecv; perfiter(20000, PIDRecv::REPLAY_WINDOW_SIZE * 3, 1, 10, count); perfiter(20000, PIDRecv::REPLAY_WINDOW_SIZE * 3, PIDRecv::REPLAY_WINDOW_SIZE / 2, 10, count); @@ -151,13 +154,11 @@ void perf(long &count) perfiter(20000, 4, PIDRecv::REPLAY_WINDOW_SIZE / 2, 10, count); } -TEST(misc, pktid_perf) +TEST(misc, pktid_control_perf) { - { - long count = 0; - perf<3, 5>(count); - perf<6, 5>(count); - perf<8, 5>(count); - // ASSERT_EQ(4746439, count); - } + long count = 0; + perf<3, 5>(count); + perf<6, 5>(count); + perf<8, 5>(count); + // ASSERT_EQ(4746439, count); } diff --git a/test/unittests/test_pktid_data.cpp b/test/unittests/test_pktid_data.cpp new file mode 100644 index 00000000..bcdd9357 --- /dev/null +++ b/test/unittests/test_pktid_data.cpp @@ -0,0 +1,264 @@ +#include "test_common.h" + +#include +#include + +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 +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 +void perfiter(const long n, + const long range, + const long step, + const long iter_per_step_pre, + long &count) +{ + typedef PacketIDDataReceiveType 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 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(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 +void perf(long &count) +{ + typedef PacketIDDataReceiveType PIDRecv; + + perfiter(20000, PIDRecv::REPLAY_WINDOW_SIZE * 3, 1, 10, count); + perfiter(20000, PIDRecv::REPLAY_WINDOW_SIZE * 3, PIDRecv::REPLAY_WINDOW_SIZE / 2, 10, count); + perfiter(20000, PIDRecv::REPLAY_WINDOW_SIZE * 2, 1, 10, count); + perfiter(20000, PIDRecv::REPLAY_WINDOW_SIZE * 2, PIDRecv::REPLAY_WINDOW_SIZE / 2, 10, count); + perfiter(20000, 16, 1, 10, count); + perfiter(20000, 16, PIDRecv::REPLAY_WINDOW_SIZE / 2, 10, count); + perfiter(20000, 4, 1, 10, count); + perfiter(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); + } +} diff --git a/test/unittests/test_reliable.cpp b/test/unittests/test_reliable.cpp index b48aeb4d..993c7e70 100644 --- a/test/unittests/test_reliable.cpp +++ b/test/unittests/test_reliable.cpp @@ -18,7 +18,7 @@ #include #include #include -#include +#include using namespace openvpn; @@ -154,14 +154,14 @@ typedef ReliableRecvTemplate ReliableRecv; struct Message { - openvpn::PacketID::id_t id; + openvpn::PacketIDControl::id_t id; BufferPtr buffer; }; void print_msg(const Time::Duration t, const char *title, BufferPtr &buf, - const openvpn::PacketID::id_t id, + const openvpn::PacketIDControl::id_t id, std::stringstream &case_detail) { case_detail << t.raw() << ' ' << title @@ -175,7 +175,7 @@ void test(MTRand &rand, const Time::Duration end, const Time::Duration step, const Time::Duration end_sends, - const openvpn::PacketID::id_t relsize, + const openvpn::PacketIDControl::id_t relsize, const size_t wiresize, const unsigned int reorder_prob, const unsigned int drop_prob, @@ -193,8 +193,8 @@ void test(MTRand &rand, long iterations = 0; Time::Duration t; - openvpn::PacketID::id_t send_id = 0; - openvpn::PacketID::id_t rec_id = 0; + openvpn::PacketIDControl::id_t send_id = 0; + openvpn::PacketIDControl::id_t rec_id = 0; for (t = Time::Duration(); t < end; t += step) { @@ -204,7 +204,7 @@ void test(MTRand &rand, // sender processes ACKs received from receiver while (!acklist.empty()) { - const openvpn::PacketID::id_t id = acklist.front(); + const openvpn::PacketIDControl::id_t id = acklist.front(); acklist.pop_front(); if (rand.randrange(40)) // with small probability, simulate a dropped ACK // 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 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); if (m.ready_retransmit(now)) @@ -327,7 +327,7 @@ void test(MTRand &rand, struct test_params { int test_case; - openvpn::PacketID::id_t relsize; + openvpn::PacketIDControl::id_t relsize; size_t wiresize; unsigned int reorder_prob; unsigned int drop_prob;