congestvpn/VpnPacket.hpp
Théophile Bastian 7b5ffa4d46 Implement loss-based congestion controller
Still lacks an outbound bandwidth limiter and outbound actual emission
bitrate, to avoid increasing the available bandwidth when the bandwidth
is not saturated.
2020-07-03 16:13:47 +02:00

289 lines
11 KiB
C++

#pragma once
/** A packet to be transmitted or received over the VPN socket */
#include <cstdlib>
#include <memory>
#include "ip_header.hpp"
/** VPN packet layout:
*
* 0 1 2 3
* 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] |
* +---------------------------------------------------------------+
* |C| Sending timestamp (μs) [4B-1b] |
* +---------------+-----------------------------------------------+
* | ... |
* +---------------------------------------------------------------+
*
* Where C is a single bit. If set, the packet is a control packet, containing
* TLVs. If unset, the packet is a tunnelled packet bearing data to be
* reinjected.
*
* If C is set, the remaining of the packet has the following structure:
*
* +---------------+-----------------------------------------------+
* | 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;
* - Sender ID is an arbitrary value, recommended to be randomly chosen
* - Payload size is the size of the payload (excluding headers), in bytes;
* - Payload is an arbitrary value, defined by Type
*/
class VpnPeer;
class VpnPacketTLV;
class VpnPacket {
public:
static const size_t VPN_HEADER_BYTES;
class PeerNotSet: public std::exception {};
VpnPacket(size_t mtu, bool inbound);
~VpnPacket();
// Remove copy constructor and operator=
VpnPacket(const VpnPacket& copy) = delete;
VpnPacket& operator=(const VpnPacket& copy) = delete;
/// Move constructor
VpnPacket(VpnPacket&& move_from);
VpnPacket& operator=(VpnPacket&& move_from) = delete;
/// Get the maximal payload space for a given tunnel MTU
static size_t get_tunnelled_mtu(size_t udp_mtu);
/// Set packet peer -- used for sequence numbers and loss rate
void set_peer(VpnPeer* peer);
/// Checks whether the packet currently bears a payload
bool is_empty() const { return get_payload_size() == 0; }
/// Get a pointer to the packet payload (const version)
const char* get_payload() const {
return _data.get() + VPN_HEADER_BYTES; }
/// Get a pointer to the packet payload
char* get_payload() { return _data.get() + 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 {
return _data_space - VPN_HEADER_BYTES; }
/// Get the current size filled by the payload
size_t get_payload_size() const {
return _data_size - VPN_HEADER_BYTES; }
/// Set current size filled by the packet payload
void set_payload_size(size_t payload_size) {
_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.get(); }
/// Get a pointer to the full packet data
char* get_data() { return _data.get(); }
/// Get the space allocated for the packet
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; }
/// Set the total current size of the packet
void set_data_size(size_t data_size) { _data_size = data_size; }
/// Get this packet's seqno
uint32_t get_seqno() const;
/// Get this packet's sending timestamp
uint32_t get_sending_timestamp() const;
/// Get this packet's reception timestamp
uint32_t get_reception_timestamp() const { return _reception_timestamp; }
/// Check whether this packet is control or data
bool is_control() const;
/** Fill the headers of the packet. This method must be called as close
* to the time the packet is actually sent as possible, as it handles
* timestamps. */
void prepare_for_sending();
/** Do what must be done at reception, eg. keep the reception
* timestamp. This method must be called as close to the time the
* packet is actually received as possible, as it handles timestamps.
*/
void upon_reception();
protected:
void set_control(bool is_control);
private: // methods
inline uint32_t next_seqno();
private:
VpnPeer* _peer; // raw pointer: we do not own the peer in any way
bool _inbound; ///< is the packet received or sent?
std::unique_ptr<char[]> _data;
size_t _data_space, _data_size;
uint32_t _reception_timestamp;
};
class VpnControlPacket: public VpnPacket {
public:
static const size_t TLV_HEADER_BYTES;
VpnControlPacket(size_t mtu, bool inbound);
VpnControlPacket(VpnPacket&& move_from);
VpnPacketTLV first_tlv();
};
/** A packet sent through the VPN tunnel. */
class VpnDataPacket: public VpnPacket {
public:
VpnDataPacket(size_t mtu, bool inbound);
VpnDataPacket(VpnPacket&& move_from);
static VpnDataPacket create(VpnPacket& packet);
/** Try to parse the packet as IPv6, return `false` upon failure.
* Does not reparse if _ipv6_parsed, unless reparse is true
*/
bool parse_as_ipv6(bool reparse=false);
bool ipv6_parsed() const { return _ipv6_parsed; }
const IPv6Header& get_ipv6_header() const { return _ipv6_header; }
private:
bool _ipv6_parsed;
IPv6Header _ipv6_header;
};
/** Base class for a TLV contained in a VpnPacket */
class VpnPacketTLV {
public:
VpnPacketTLV(VpnControlPacket& packet, size_t payload_offset);
VpnPacketTLV(const VpnPacketTLV& other);
enum PayloadType {
PAYLOAD_TYPE_UNDEF, ///< Undefined packet type
PAYLOAD_TYPE_LOSS_REPORT, ///< Loss report
PAYLOAD_TYPE_REMB, ///< Receiver Estimated Maximum Bitrate
PAYLOAD_TYPE_RTTQ, ///< RTT update query
PAYLOAD_TYPE_RTTA, ///< RTT update answer
};
static VpnPacketTLV create(
VpnControlPacket& packet,
PayloadType type=PAYLOAD_TYPE_UNDEF);
const VpnControlPacket& get_packet() const { return _packet; }
VpnControlPacket& get_packet() { return _packet; }
/// Get the next TLV in this packet.
VpnPacketTLV next_tlv();
/// Point this object to the next TLV (ie. `next_tlv` in place)
void seek_next_tlv();
/// Check whether the current TLV is past the packet's end
bool past_the_end() const {
return _tlv_pos >= _packet.get_payload_size(); }
/// 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
+ VpnControlPacket::TLV_HEADER_BYTES; }
/// Get a pointer to the payload
char* get_payload() {
return _packet.get_payload() + _tlv_pos
+ VpnControlPacket::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 the total available raw data space
uint16_t get_payload_space() const;
/// 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() + VpnControlPacket::TLV_HEADER_BYTES; }
/// Set the current raw data size
void set_data_size(uint16_t size) {
set_payload_size(size + VpnControlPacket::TLV_HEADER_BYTES); }
/// Get this TLV's type
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(PayloadType type);
protected:
VpnControlPacket& _packet;
size_t _tlv_pos;
};
class VpnTlvLossReport: public VpnPacketTLV {
public:
VpnTlvLossReport(VpnControlPacket& packet, size_t payload_offset);
VpnTlvLossReport(const VpnPacketTLV& other);
static VpnTlvLossReport create(VpnControlPacket& packet);
uint32_t get_report_seqno() const;
void set_report_seqno(uint32_t seqno);
uint32_t get_losses() const;
void set_losses(uint32_t losses);
private:
static const uint32_t REP_SEQNO_POS, REP_LOSS_POS;
};
class VpnTlvRTTQ: public VpnPacketTLV {
public:
VpnTlvRTTQ(VpnControlPacket& packet, size_t payload_offset);
VpnTlvRTTQ(const VpnPacketTLV& other);
static VpnTlvRTTQ create(VpnControlPacket& packet);
};
class VpnTlvRTTA: public VpnPacketTLV {
public:
VpnTlvRTTA(VpnControlPacket& packet, size_t payload_offset);
VpnTlvRTTA(const VpnPacketTLV& other);
static VpnTlvRTTA create(VpnControlPacket& packet);
uint32_t get_exp_ts() const;
void set_exp_ts(uint32_t ts);
uint32_t get_recv_ts() const;
void set_recv_ts(uint32_t ts);
private:
static const uint32_t EXP_TS_POS, RECV_TS_POS;
};