Redefine VpnPackets as TLV containers

This commit is contained in:
Théophile Bastian 2020-06-12 17:36:55 +02:00
parent 20d0d3fc59
commit 324d156cf3
2 changed files with 206 additions and 7 deletions

View file

@ -2,8 +2,10 @@
#include "VpnPeer.hpp" #include "VpnPeer.hpp"
#include <chrono> #include <chrono>
#include <cstring>
const size_t VpnPacket::VPN_HEADER_BYTES = 8; const size_t VpnPacket::VPN_HEADER_BYTES = 8;
const size_t VpnPacket::TLV_HEADER_BYTES = 3;
uint32_t VpnPacket::_next_general_seqno = 0; uint32_t VpnPacket::_next_general_seqno = 0;
static const size_t OUTER_HEADERS_BYTES = static const size_t OUTER_HEADERS_BYTES =
@ -15,8 +17,8 @@ static const int
DATA_TIMESTAMP_POS = 4; DATA_TIMESTAMP_POS = 4;
VpnPacket::VpnPacket(size_t mtu) VpnPacket::VpnPacket(size_t mtu)
: _peer(nullptr), _data_space(mtu-OUTER_HEADERS_BYTES), _data_size(0), : _peer(nullptr), _data_space(mtu-OUTER_HEADERS_BYTES),
_reception_timestamp(0) _data_size(VPN_HEADER_BYTES), _reception_timestamp(0)
{ {
_data = new char[mtu - OUTER_HEADERS_BYTES]; _data = new char[mtu - OUTER_HEADERS_BYTES];
} }
@ -25,6 +27,13 @@ VpnPacket::~VpnPacket() {
delete[] _data; delete[] _data;
} }
VpnPacket::iterator VpnPacket::begin() {
return iterator(VpnPacketTLV(*this, 0));
}
VpnPacket::iterator VpnPacket::end() {
return iterator(VpnPacketTLV(*this, get_payload_size()));
}
bool VpnPacket::parse_as_ipv6() { bool VpnPacket::parse_as_ipv6() {
return parse_ipv6_header(get_payload(), get_payload_size(), _ipv6_header); return parse_ipv6_header(get_payload(), get_payload_size(), _ipv6_header);
} }
@ -55,3 +64,50 @@ uint32_t VpnPacket::next_seqno() {
return _peer->next_seqno(); return _peer->next_seqno();
return _next_general_seqno++; return _next_general_seqno++;
} }
VpnPacketTLV::VpnPacketTLV(VpnPacket& packet, size_t payload_offset)
: _packet(packet), _tlv_pos(payload_offset)
{}
VpnPacketTLV VpnPacketTLV::create(
VpnPacket& packet, VpnPacket::PayloadType type)
{
VpnPacketTLV tlv = VpnPacketTLV(packet, packet.get_payload_size());
packet.increase_payload_size(VpnPacket::TLV_HEADER_BYTES);
char* data = tlv.get_data();
data[0] = type;
*(uint16_t*)(data+1) = 0; // Set len to 0
return tlv;
}
uint16_t VpnPacketTLV::get_payload_size() const {
return *(uint16_t*)(get_data() + 1);
}
void VpnPacketTLV::set_payload_size(uint16_t size) {
uint16_t* data_size_ptr = (uint16_t*)(get_data() + 1);
uint16_t old_size = *data_size_ptr;
*data_size_ptr = size;
_packet.increase_payload_size(size - old_size);
}
VpnPacket::PayloadType VpnPacketTLV::get_type() const {
return (VpnPacket::PayloadType)(*(uint8_t*)(get_data()));
}
void VpnPacketTLV::set_type(VpnPacket::PayloadType type) {
*(uint8_t*)(get_data()) = (uint8_t) type;
}
TunnelledPacket::TunnelledPacket(VpnPacket& packet, size_t payload_offset)
: VpnPacketTLV(packet, payload_offset)
{}
TunnelledPacket::TunnelledPacket(const VpnPacketTLV& copy)
: VpnPacketTLV(copy._packet, copy._tlv_pos)
{}
TunnelledPacket TunnelledPacket::create(VpnPacket& packet) {
return VpnPacketTLV::create(packet, VpnPacket::PAYLOAD_TYPE_TUNNELLED);
}

View file

@ -9,24 +9,53 @@
/** VPN packet layout: /** VPN packet layout:
* *
* +-------+-------+-------+-------+-------+-------+-------+-------+ * 0 1 2 3
* | 1B | 1B | 1B | 1B | 1B | 1B | 1B | 1B | * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-------+-------+-------+-------+-------+-------+-------+-------+ * +---------------+---------------+---------------+---------------+
* | Sequence number [4B] | Sending timestamp (μs) [4B] | * | Sequence number [4B] |
* +---------------------------------------------------------------+ * +---------------------------------------------------------------+
* | Nested packet (payload) | * | Sending timestamp (μs) [4B] |
* +---------------+-----------------------------------------------+
* | Type [1B] | First payload size (B) [2B] | Payload |
* +---------------+-----------------------------------------------+
* | Payload (cont.) |
* +---------------+-----------------------------------------------+
* | Type [1B] | Second payload size (B) [2B] | Payload |
* +---------------+-----------------------------------------------+
* | Payload (cont.) |
* +---------------------------------------------------------------+ * +---------------------------------------------------------------+
* | ... |
* +---------------------------------------------------------------+
*
* Where
* - Type is one of the values from PayloadType below;
* - Payload size is the size of the payload (excluding headers), in bytes;
* - Payload is an arbitrary value, defined by Type
*/ */
class VpnPeer; class VpnPeer;
class VpnPacketTLVIterator;
class VpnPacket { class VpnPacket {
public: public:
static const size_t VPN_HEADER_BYTES; static const size_t VPN_HEADER_BYTES;
static const size_t TLV_HEADER_BYTES;
typedef VpnPacketTLVIterator iterator;
enum PayloadType {
PAYLOAD_TYPE_UNDEF, ///< Undefined packet type
PAYLOAD_TYPE_TUNNELLED, ///< A tunnelled packet
PAYLOAD_TYPE_RR, ///< Receiver report
PAYLOAD_TYPE_REMB, ///< Receiver Estimated Maximum Bitrate
};
VpnPacket(size_t mtu); VpnPacket(size_t mtu);
~VpnPacket(); ~VpnPacket();
iterator begin();
iterator end();
/// Set packet peer -- used for sequence numbers /// Set packet peer -- used for sequence numbers
void set_peer(std::shared_ptr<VpnPeer> peer) { _peer = peer; } void set_peer(std::shared_ptr<VpnPeer> peer) { _peer = peer; }
@ -35,23 +64,42 @@ class VpnPacket {
bool ipv6_parsed() const { return _ipv6_parsed; } bool ipv6_parsed() const { return _ipv6_parsed; }
const IPv6Header& get_ipv6_header() const { return _ipv6_header; } const IPv6Header& get_ipv6_header() const { return _ipv6_header; }
/// Get a pointer to the packet payload (const version)
const char* get_payload() const { return _data + VPN_HEADER_BYTES; } const char* get_payload() const { return _data + VPN_HEADER_BYTES; }
/// Get a pointer to the packet payload
char* get_payload() { return _data + VPN_HEADER_BYTES; } char* get_payload() { return _data + VPN_HEADER_BYTES; }
/// Get a pointer to the first free byte of the packet payload
char* get_next_payload() { return get_payload() + get_payload_size(); }
/// Get the space allocated for the packet payload
size_t get_payload_space() const { size_t get_payload_space() const {
return _data_space - VPN_HEADER_BYTES; } return _data_space - VPN_HEADER_BYTES; }
/// Get the current size filled by the payload
size_t get_payload_size() const { size_t get_payload_size() const {
return _data_size - VPN_HEADER_BYTES; } return _data_size - VPN_HEADER_BYTES; }
/// Set current size filled by the packet payload
void set_payload_size(size_t payload_size) { void set_payload_size(size_t payload_size) {
_data_size = payload_size + VPN_HEADER_BYTES; } _data_size = payload_size + VPN_HEADER_BYTES; }
/// Increase the current size filled by the packet payload (size may be
/// negative).
void increase_payload_size(ssize_t payload_size_increment) {
_data_size += payload_size_increment; }
/// Get a pointer to the full packet data (const version)
const char* get_data() const { return _data; } const char* get_data() const { return _data; }
/// Get a pointer to the full packet data
char* get_data() { return _data; } char* get_data() { return _data; }
/// Get the space allocated for the packet
size_t get_data_space() const { return _data_space; } size_t get_data_space() const { return _data_space; }
/// Get the total current size of the packet
size_t get_data_size() const { return _data_size; } size_t get_data_size() const { return _data_size; }
/// Set the total current size of the packet
void set_data_size(size_t data_size) { _data_size = data_size; } void set_data_size(size_t data_size) { _data_size = data_size; }
/// Get this packet's seqno
uint32_t get_seqno() const; uint32_t get_seqno() const;
/// Get this packet's sending timestamp
uint32_t get_sending_timestamp() const; uint32_t get_sending_timestamp() const;
/// Get this packet's reception timestamp
uint32_t get_reception_timestamp() const { return _reception_timestamp; } uint32_t get_reception_timestamp() const { return _reception_timestamp; }
/** Fill the headers of the packet. This method must be called as close /** Fill the headers of the packet. This method must be called as close
@ -81,3 +129,98 @@ class VpnPacket {
static uint32_t _next_general_seqno; static uint32_t _next_general_seqno;
}; };
/** Base class for a TLV contained in a VpnPacket */
class VpnPacketTLV {
public:
VpnPacketTLV(VpnPacket& packet, size_t payload_offset);
static VpnPacketTLV create(
VpnPacket& packet,
VpnPacket::PayloadType type=VpnPacket::PAYLOAD_TYPE_UNDEF);
const VpnPacket& get_packet() const { return _packet; }
VpnPacket& get_packet() { return _packet; }
/// Get the offset in the packet
size_t get_offset() const { return _tlv_pos; }
/// Get a pointer to the payload (const version)
const char* get_payload() const {
return _packet.get_payload() + _tlv_pos + VpnPacket::TLV_HEADER_BYTES; }
/// Get a pointer to the payload
char* get_payload() {
return _packet.get_payload() + _tlv_pos + VpnPacket::TLV_HEADER_BYTES; }
/// Get the current payload size
uint16_t get_payload_size() const;
/// Set the current payload size
void set_payload_size(uint16_t size);
/// Get a pointer to the raw data (const version)
const char* get_data() const {
return _packet.get_payload() + _tlv_pos; }
/// Get a pointer to the raw data
char* get_data() { return _packet.get_payload() + _tlv_pos; }
/// Get the current raw data size
uint16_t get_data_size() const {
return get_payload_size() + VpnPacket::TLV_HEADER_BYTES; }
/// Set the current raw data size
void set_data_size(uint16_t size) {
set_payload_size(size + VpnPacket::TLV_HEADER_BYTES); }
/// Get this TLV's type
VpnPacket::PayloadType get_type() const;
bool operator==(const VpnPacketTLV& other) const {
return &_packet == &other._packet && _tlv_pos == other._tlv_pos; }
protected: // meth
/// Set this TLV's type
void set_type(VpnPacket::PayloadType type);
protected:
VpnPacket& _packet;
size_t _tlv_pos;
friend class TunnelledPacket;
};
/** An iterator over the VpnPacketTLVs of a VpnPacket */
class VpnPacketTLVIterator {
public:
using iterator_category = std::input_iterator_tag;
using value_type = VpnPacketTLV;
using difference_type = ptrdiff_t;
using pointer = VpnPacketTLV*;
using reference = VpnPacketTLV&;
VpnPacketTLVIterator(const VpnPacketTLV& tlv) : _tlv(tlv) {}
VpnPacketTLVIterator& operator++();
VpnPacketTLVIterator operator++(int) {
VpnPacketTLVIterator tmp(*this);
operator++();
return tmp;
}
bool operator==(const VpnPacketTLVIterator& other) {
return _tlv == other._tlv; }
bool operator!=(const VpnPacketTLVIterator& other) {
return !(operator==(other)); }
bool past_the_end() const {
return _tlv.get_offset() >= _tlv.get_packet().get_payload_size(); }
reference operator*() { return _tlv; }
private:
VpnPacketTLV _tlv;
};
/** A packet sent through the VPN tunnel.
*
* This must instantiated just before filling it with data. */
class TunnelledPacket: public VpnPacketTLV {
public:
TunnelledPacket(VpnPacket& packet, size_t payload_offset);
TunnelledPacket(const VpnPacketTLV& copy);
static TunnelledPacket create(VpnPacket& packet);
};