Split data and control packets
This commit is contained in:
parent
f07f2a853d
commit
c5541d1e79
11 changed files with 237 additions and 132 deletions
73
UdpVpn.cpp
73
UdpVpn.cpp
|
@ -8,13 +8,15 @@
|
|||
#include <string.h>
|
||||
#include <poll.h>
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "ip_header.hpp"
|
||||
|
||||
static const size_t VPN_MTU = 1460; // TODO determine this -- issue #3
|
||||
|
||||
UdpVpn::UdpVpn()
|
||||
: _stopped(false), _vpn_mtu(VPN_MTU), _tun_dev("cvpn%d"), _peer(nullptr)
|
||||
: _stopped(false), _dump_requested(false), _vpn_mtu(VPN_MTU),
|
||||
_tun_dev("cvpn%d"), _peer(nullptr)
|
||||
{
|
||||
_tun_dev.set_mtu(VpnPacket::get_tunnelled_mtu(_vpn_mtu));
|
||||
_socket = socket(AF_INET6, SOCK_DGRAM, 0);
|
||||
|
@ -42,6 +44,11 @@ void UdpVpn::run() {
|
|||
poll_fds[1].events = POLLIN;
|
||||
|
||||
while(!_stopped) {
|
||||
if(_dump_requested) {
|
||||
dump_state();
|
||||
_dump_requested = false;
|
||||
}
|
||||
|
||||
rc = poll(poll_fds, nfds, -1);
|
||||
|
||||
if(rc < 0) {
|
||||
|
@ -75,7 +82,7 @@ size_t UdpVpn::read_from_tun(char* buffer, size_t len) {
|
|||
return _tun_dev.read(buffer, len);
|
||||
}
|
||||
|
||||
size_t UdpVpn::read_from_tun(TunnelledPacket& packet) {
|
||||
size_t UdpVpn::read_from_tun(VpnDataPacket& packet) {
|
||||
size_t payload_space = packet.get_payload_space();
|
||||
size_t nread = read_from_tun(packet.get_payload(), payload_space);
|
||||
packet.set_payload_size(nread);
|
||||
|
@ -136,9 +143,8 @@ size_t UdpVpn::transmit_to_peer(VpnPacket& packet) {
|
|||
}
|
||||
|
||||
void UdpVpn::receive_from_tun() {
|
||||
VpnPacket packet(_vpn_mtu);
|
||||
TunnelledPacket tunnelled = TunnelledPacket::create(packet);
|
||||
size_t nread = read_from_tun(tunnelled);
|
||||
VpnDataPacket packet(_vpn_mtu, false);
|
||||
size_t nread = read_from_tun(packet);
|
||||
if(nread == 0)
|
||||
return;
|
||||
|
||||
|
@ -149,8 +155,8 @@ void UdpVpn::receive_from_tun() {
|
|||
packet.set_peer(_peer.get());
|
||||
|
||||
kdebugf("Transmitting %s -> %s, size %d\n",
|
||||
format_address(tunnelled.get_ipv6_header().source.s6_addr),
|
||||
format_address(tunnelled.get_ipv6_header().dest.s6_addr),
|
||||
format_address(packet.get_ipv6_header().source.s6_addr),
|
||||
format_address(packet.get_ipv6_header().dest.s6_addr),
|
||||
nread);
|
||||
|
||||
packet.prepare_for_sending();
|
||||
|
@ -158,7 +164,7 @@ void UdpVpn::receive_from_tun() {
|
|||
}
|
||||
|
||||
void UdpVpn::receive_from_udp() {
|
||||
VpnPacket packet(_vpn_mtu);
|
||||
VpnPacket packet(_vpn_mtu, true);
|
||||
sockaddr_in6 peer_ext_addr;
|
||||
size_t nread = read_from_udp(packet, peer_ext_addr);
|
||||
if(nread == 0)
|
||||
|
@ -167,32 +173,41 @@ void UdpVpn::receive_from_udp() {
|
|||
// If we don't have a peer yet -- we're just setting the peer to nullptr.
|
||||
packet.set_peer(_peer.get());
|
||||
|
||||
for(VpnPacketTLV tlv=packet.first_tlv();
|
||||
!tlv.past_the_end();
|
||||
tlv.seek_next_tlv())
|
||||
{
|
||||
switch(tlv.get_type()) {
|
||||
case VpnPacket::PAYLOAD_TYPE_TUNNELLED:
|
||||
{
|
||||
TunnelledPacket tunnelled(tlv);
|
||||
acquire_peer(tunnelled, peer_ext_addr);
|
||||
receive_tunnelled_tlv(tunnelled);
|
||||
}
|
||||
break;
|
||||
if(packet.is_control()) {
|
||||
VpnControlPacket ctrl_packet(std::move(packet));
|
||||
|
||||
case VpnPacket::PAYLOAD_TYPE_UNDEF:
|
||||
default:
|
||||
debugf("#%d+%lu: ignoring TLV with bad type %d.\n",
|
||||
packet.get_seqno(), tlv.get_offset(),
|
||||
tlv.get_type());
|
||||
break;
|
||||
for(VpnPacketTLV tlv=ctrl_packet.first_tlv();
|
||||
!tlv.past_the_end();
|
||||
tlv.seek_next_tlv())
|
||||
{
|
||||
switch(tlv.get_type()) {
|
||||
case VpnPacketTLV::PAYLOAD_TYPE_UNDEF:
|
||||
default:
|
||||
debugf("#%d+%lu: ignoring TLV with bad type %d.\n",
|
||||
ctrl_packet.get_seqno(), tlv.get_offset(),
|
||||
tlv.get_type());
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
VpnDataPacket data_packet(std::move(packet));
|
||||
acquire_peer(data_packet, peer_ext_addr);
|
||||
receive_tunnelled_tlv(data_packet);
|
||||
}
|
||||
}
|
||||
|
||||
void UdpVpn::receive_tunnelled_tlv(TunnelledPacket& packet) {
|
||||
void UdpVpn::receive_tunnelled_tlv(VpnDataPacket& packet) {
|
||||
// Reinject into tun
|
||||
kdebugf("Reinjecting tunnelled packet of size %d\n",
|
||||
packet.get_payload_size());
|
||||
kdebugf("Reinjecting tunnelled packet of size %d [#%ld, TS=%ld μs]\n",
|
||||
packet.get_payload_size(),
|
||||
packet.get_seqno(),
|
||||
packet.get_sending_timestamp());
|
||||
_tun_dev.write(packet.get_payload(), packet.get_payload_size());
|
||||
}
|
||||
|
||||
void UdpVpn::dump_state() const {
|
||||
printf("====== State dump ======\n");
|
||||
printf("Packet loss rate: %.0lf%%\n",
|
||||
round(_peer->get_loss_logger().get_loss_rate() * 100));
|
||||
printf("==== End state dump ====\n");
|
||||
}
|
||||
|
|
13
UdpVpn.hpp
13
UdpVpn.hpp
|
@ -41,13 +41,16 @@ class UdpVpn {
|
|||
// Stop the server. Can be called from an interrupt.
|
||||
void stop() { _stopped = true; }
|
||||
|
||||
// A state dump has been requested
|
||||
void dump_requested() { _dump_requested = true; }
|
||||
|
||||
protected:
|
||||
virtual void acquire_peer(
|
||||
TunnelledPacket& packet,
|
||||
VpnDataPacket& packet,
|
||||
const sockaddr_in6& peer_ext_addr) = 0;
|
||||
|
||||
size_t read_from_tun(char* buffer, size_t len);
|
||||
size_t read_from_tun(TunnelledPacket& packet);
|
||||
size_t read_from_tun(VpnDataPacket& packet);
|
||||
size_t read_from_udp(char* buffer, size_t len, sockaddr_in6& peer_addr);
|
||||
size_t read_from_udp(VpnPacket& packet, sockaddr_in6& peer_addr);
|
||||
|
||||
|
@ -56,10 +59,12 @@ class UdpVpn {
|
|||
void receive_from_tun();
|
||||
void receive_from_udp();
|
||||
|
||||
void receive_tunnelled_tlv(TunnelledPacket& packet);
|
||||
void receive_tunnelled_tlv(VpnDataPacket& packet);
|
||||
|
||||
void dump_state() const;
|
||||
|
||||
int _socket;
|
||||
bool _stopped;
|
||||
bool _stopped, _dump_requested;
|
||||
|
||||
size_t _vpn_mtu;
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ UdpVpnClient::UdpVpnClient(const struct sockaddr_in6& server) : UdpVpn() {
|
|||
}
|
||||
|
||||
void UdpVpnClient::acquire_peer(
|
||||
TunnelledPacket& packet,
|
||||
VpnDataPacket& packet,
|
||||
const sockaddr_in6&)
|
||||
{
|
||||
if(!packet.parse_as_ipv6())
|
||||
|
|
|
@ -8,6 +8,6 @@ class UdpVpnClient: public UdpVpn {
|
|||
|
||||
protected:
|
||||
virtual void acquire_peer(
|
||||
TunnelledPacket& packet,
|
||||
VpnDataPacket& packet,
|
||||
const sockaddr_in6& peer_ext_addr);
|
||||
};
|
||||
|
|
|
@ -16,7 +16,7 @@ UdpVpnServer::UdpVpnServer(const struct in6_addr& bind_addr6, in_port_t port)
|
|||
}
|
||||
|
||||
void UdpVpnServer::acquire_peer(
|
||||
TunnelledPacket& packet,
|
||||
VpnDataPacket& packet,
|
||||
const sockaddr_in6& peer_ext_addr)
|
||||
{
|
||||
if(_peer)
|
||||
|
@ -28,7 +28,7 @@ void UdpVpnServer::acquire_peer(
|
|||
const in6_addr& peer_inner_addr = packet.get_ipv6_header().source;
|
||||
_peer = std::make_unique<VpnPeer>(this, peer_ext_addr, peer_inner_addr);
|
||||
|
||||
packet.get_packet().set_peer(_peer.get());
|
||||
packet.set_peer(_peer.get());
|
||||
|
||||
debugf("Got new peer %s:%d -- %s\n",
|
||||
format_address(peer_ext_addr.sin6_addr.s6_addr),
|
||||
|
|
|
@ -11,7 +11,7 @@ class UdpVpnServer: public UdpVpn {
|
|||
UdpVpnServer(const struct in6_addr& bind_addr6, in_port_t port);
|
||||
protected:
|
||||
virtual void acquire_peer(
|
||||
TunnelledPacket& packet,
|
||||
VpnDataPacket& packet,
|
||||
const sockaddr_in6& peer_ext_addr);
|
||||
|
||||
void bind(const struct in6_addr& bind_addr6, in_port_t port);
|
||||
|
|
119
VpnPacket.cpp
119
VpnPacket.cpp
|
@ -5,7 +5,7 @@
|
|||
#include <cstring>
|
||||
|
||||
const size_t VpnPacket::VPN_HEADER_BYTES = 8;
|
||||
const size_t VpnPacket::TLV_HEADER_BYTES = 3;
|
||||
const size_t VpnControlPacket::TLV_HEADER_BYTES = 3;
|
||||
|
||||
static const size_t OUTER_HEADERS_BYTES =
|
||||
40 /* IPv6 header */ + 8 /* UDP header */;
|
||||
|
@ -13,40 +13,67 @@ static const size_t OUTER_HEADERS_BYTES =
|
|||
|
||||
static const int
|
||||
DATA_SEQNO_POS = 0,
|
||||
DATA_CTRLBIT_POS = 4,
|
||||
DATA_TIMESTAMP_POS = 4;
|
||||
|
||||
VpnPacket::VpnPacket(size_t mtu)
|
||||
: _peer(nullptr), _data_space(mtu-OUTER_HEADERS_BYTES),
|
||||
VpnPacket::VpnPacket(size_t mtu, bool inbound)
|
||||
: _peer(nullptr), _inbound(inbound), _data_space(mtu-OUTER_HEADERS_BYTES),
|
||||
_data_size(VPN_HEADER_BYTES), _reception_timestamp(0)
|
||||
{
|
||||
_data = new char[mtu - OUTER_HEADERS_BYTES];
|
||||
_data = std::unique_ptr<char[]>(new char[mtu - OUTER_HEADERS_BYTES]);
|
||||
}
|
||||
|
||||
VpnPacket::~VpnPacket() {
|
||||
delete[] _data;
|
||||
}
|
||||
VpnPacket::~VpnPacket() {}
|
||||
|
||||
VpnPacketTLV VpnPacket::first_tlv() {
|
||||
return VpnPacketTLV(*this, 0);
|
||||
}
|
||||
VpnPacket::VpnPacket(VpnPacket&& move_from) :
|
||||
_peer(move_from._peer),
|
||||
_inbound(move_from._inbound),
|
||||
_data(std::move(move_from._data)),
|
||||
_data_space(move_from._data_space),
|
||||
_data_size(move_from._data_size),
|
||||
_reception_timestamp(move_from._reception_timestamp)
|
||||
{}
|
||||
|
||||
size_t VpnPacket::get_tunnelled_mtu(size_t udp_mtu) {
|
||||
return udp_mtu - OUTER_HEADERS_BYTES - VPN_HEADER_BYTES - TLV_HEADER_BYTES;
|
||||
return udp_mtu - OUTER_HEADERS_BYTES - VPN_HEADER_BYTES;
|
||||
}
|
||||
|
||||
void VpnPacket::set_peer(VpnPeer* peer) {
|
||||
_peer = peer;
|
||||
if(_peer && _inbound)
|
||||
_peer->got_inbound_packet(*this);
|
||||
}
|
||||
|
||||
uint32_t VpnPacket::get_seqno() const {
|
||||
return *(uint32_t*)(_data + DATA_SEQNO_POS);
|
||||
return ntohl(*(uint32_t*)(_data.get() + DATA_SEQNO_POS));
|
||||
}
|
||||
|
||||
uint32_t VpnPacket::get_sending_timestamp() const {
|
||||
return *(uint32_t*)(_data + DATA_TIMESTAMP_POS);
|
||||
return ntohl(
|
||||
*(uint32_t*)(_data.get() + DATA_TIMESTAMP_POS) & 0x7fffffffUL
|
||||
);
|
||||
}
|
||||
|
||||
bool VpnPacket::is_control() const {
|
||||
return *(unsigned char*)(_data.get() + DATA_CTRLBIT_POS) & 0x80;
|
||||
}
|
||||
|
||||
void VpnPacket::set_control(bool is_control) {
|
||||
unsigned char* ctrl_field =
|
||||
(unsigned char*) (_data.get() + DATA_CTRLBIT_POS);
|
||||
*ctrl_field &= 0x7f;
|
||||
if(is_control)
|
||||
*ctrl_field |= 0x80;
|
||||
}
|
||||
|
||||
void VpnPacket::prepare_for_sending() {
|
||||
*(uint32_t*)(_data + DATA_SEQNO_POS) = next_seqno();
|
||||
*(uint32_t*)(_data + DATA_TIMESTAMP_POS) =
|
||||
std::chrono::time_point_cast<std::chrono::microseconds>(
|
||||
std::chrono::steady_clock::now()).time_since_epoch().count();
|
||||
uint32_t* ts_field = (uint32_t*) (_data.get() + DATA_TIMESTAMP_POS);
|
||||
*ts_field &= 0x80000000UL;
|
||||
*(uint32_t*)(_data.get() + DATA_SEQNO_POS) = htonl(next_seqno());
|
||||
*ts_field |=
|
||||
htonl((std::chrono::time_point_cast<std::chrono::microseconds>(
|
||||
std::chrono::steady_clock::now()).time_since_epoch().count())
|
||||
& 0x7fffffffUL);
|
||||
}
|
||||
|
||||
void VpnPacket::upon_reception() {
|
||||
|
@ -61,8 +88,35 @@ uint32_t VpnPacket::next_seqno() {
|
|||
return _peer->next_seqno();
|
||||
}
|
||||
|
||||
VpnControlPacket::VpnControlPacket(size_t mtu, bool inbound)
|
||||
: VpnPacket(mtu, inbound)
|
||||
{
|
||||
set_control(true);
|
||||
}
|
||||
|
||||
VpnPacketTLV::VpnPacketTLV(VpnPacket& packet, size_t payload_offset)
|
||||
VpnControlPacket::VpnControlPacket(VpnPacket&& move_from)
|
||||
: VpnPacket(std::move(move_from))
|
||||
{}
|
||||
|
||||
VpnPacketTLV VpnControlPacket::first_tlv() {
|
||||
return VpnPacketTLV(*this, 0);
|
||||
}
|
||||
|
||||
VpnDataPacket::VpnDataPacket(size_t mtu, bool inbound)
|
||||
: VpnPacket(mtu, inbound), _ipv6_parsed(false)
|
||||
{
|
||||
set_control(false);
|
||||
}
|
||||
|
||||
VpnDataPacket::VpnDataPacket(VpnPacket&& move_from)
|
||||
: VpnPacket(std::move(move_from)), _ipv6_parsed(false)
|
||||
{}
|
||||
|
||||
bool VpnDataPacket::parse_as_ipv6() {
|
||||
return parse_ipv6_header(get_payload(), get_payload_size(), _ipv6_header);
|
||||
}
|
||||
|
||||
VpnPacketTLV::VpnPacketTLV(VpnControlPacket& packet, size_t payload_offset)
|
||||
: _packet(packet), _tlv_pos(payload_offset)
|
||||
{}
|
||||
|
||||
|
@ -71,10 +125,10 @@ VpnPacketTLV::VpnPacketTLV(const VpnPacketTLV& other) :
|
|||
{}
|
||||
|
||||
VpnPacketTLV VpnPacketTLV::create(
|
||||
VpnPacket& packet, VpnPacket::PayloadType type)
|
||||
VpnControlPacket& packet, VpnPacketTLV::PayloadType type)
|
||||
{
|
||||
VpnPacketTLV tlv = VpnPacketTLV(packet, packet.get_payload_size());
|
||||
packet.increase_payload_size(VpnPacket::TLV_HEADER_BYTES);
|
||||
packet.increase_payload_size(VpnControlPacket::TLV_HEADER_BYTES);
|
||||
|
||||
char* data = tlv.get_data();
|
||||
data[0] = type;
|
||||
|
@ -89,7 +143,8 @@ VpnPacketTLV VpnPacketTLV::next_tlv() {
|
|||
}
|
||||
|
||||
void VpnPacketTLV::seek_next_tlv() {
|
||||
_tlv_pos = _tlv_pos + VpnPacket::TLV_HEADER_BYTES + get_payload_size();
|
||||
_tlv_pos =
|
||||
_tlv_pos + VpnControlPacket::TLV_HEADER_BYTES + get_payload_size();
|
||||
}
|
||||
|
||||
uint16_t VpnPacketTLV::get_payload_size() const {
|
||||
|
@ -108,25 +163,9 @@ uint16_t VpnPacketTLV::get_payload_space() const {
|
|||
+ get_payload_size();
|
||||
}
|
||||
|
||||
VpnPacket::PayloadType VpnPacketTLV::get_type() const {
|
||||
return (VpnPacket::PayloadType)(*(uint8_t*)(get_data()));
|
||||
VpnPacketTLV::PayloadType VpnPacketTLV::get_type() const {
|
||||
return (PayloadType)(*(uint8_t*)(get_data()));
|
||||
}
|
||||
void VpnPacketTLV::set_type(VpnPacket::PayloadType type) {
|
||||
void VpnPacketTLV::set_type(VpnPacketTLV::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);
|
||||
}
|
||||
|
||||
bool TunnelledPacket::parse_as_ipv6() {
|
||||
return parse_ipv6_header(get_payload(), get_payload_size(), _ipv6_header);
|
||||
}
|
||||
|
|
138
VpnPacket.hpp
138
VpnPacket.hpp
|
@ -14,7 +14,17 @@
|
|||
* +---------------+---------------+---------------+---------------+
|
||||
* | Sequence number [4B] |
|
||||
* +---------------------------------------------------------------+
|
||||
* | Sending timestamp (μs) [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 |
|
||||
* +---------------+-----------------------------------------------+
|
||||
|
@ -27,7 +37,7 @@
|
|||
* | ... |
|
||||
* +---------------------------------------------------------------+
|
||||
*
|
||||
* Where
|
||||
* 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;
|
||||
|
@ -40,32 +50,30 @@ class VpnPacketTLV;
|
|||
class VpnPacket {
|
||||
public:
|
||||
static const size_t VPN_HEADER_BYTES;
|
||||
static const size_t TLV_HEADER_BYTES;
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
class PeerNotSet: public std::exception {};
|
||||
|
||||
VpnPacket(size_t mtu);
|
||||
VpnPacket(size_t mtu, bool inbound);
|
||||
~VpnPacket();
|
||||
|
||||
VpnPacketTLV first_tlv();
|
||||
// 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
|
||||
void set_peer(VpnPeer* peer) { _peer = peer; }
|
||||
/// Set packet peer -- used for sequence numbers and loss rate
|
||||
void set_peer(VpnPeer* peer);
|
||||
|
||||
/// 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.get() + VPN_HEADER_BYTES; }
|
||||
/// Get a pointer to the packet payload
|
||||
char* get_payload() { return _data + VPN_HEADER_BYTES; }
|
||||
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
|
||||
|
@ -83,9 +91,9 @@ class VpnPacket {
|
|||
_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(); }
|
||||
/// Get a pointer to the full packet data
|
||||
char* get_data() { return _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
|
||||
|
@ -99,6 +107,8 @@ class VpnPacket {
|
|||
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
|
||||
|
@ -111,30 +121,69 @@ class VpnPacket {
|
|||
*/
|
||||
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?
|
||||
|
||||
char* _data;
|
||||
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.
|
||||
bool parse_as_ipv6();
|
||||
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(VpnPacket& packet, size_t payload_offset);
|
||||
VpnPacketTLV(VpnControlPacket& packet, size_t payload_offset);
|
||||
VpnPacketTLV(const VpnPacketTLV& other);
|
||||
|
||||
static VpnPacketTLV create(
|
||||
VpnPacket& packet,
|
||||
VpnPacket::PayloadType type=VpnPacket::PAYLOAD_TYPE_UNDEF);
|
||||
enum PayloadType {
|
||||
PAYLOAD_TYPE_UNDEF, ///< Undefined packet type
|
||||
PAYLOAD_TYPE_RR, ///< Receiver report
|
||||
PAYLOAD_TYPE_REMB, ///< Receiver Estimated Maximum Bitrate
|
||||
};
|
||||
|
||||
const VpnPacket& get_packet() const { return _packet; }
|
||||
VpnPacket& get_packet() { return _packet; }
|
||||
|
||||
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();
|
||||
|
@ -150,10 +199,12 @@ class VpnPacketTLV {
|
|||
|
||||
/// Get a pointer to the payload (const version)
|
||||
const char* get_payload() const {
|
||||
return _packet.get_payload() + _tlv_pos + VpnPacket::TLV_HEADER_BYTES; }
|
||||
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 + VpnPacket::TLV_HEADER_BYTES; }
|
||||
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
|
||||
|
@ -168,43 +219,22 @@ class VpnPacketTLV {
|
|||
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; }
|
||||
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 + VpnPacket::TLV_HEADER_BYTES); }
|
||||
set_payload_size(size + VpnControlPacket::TLV_HEADER_BYTES); }
|
||||
|
||||
/// Get this TLV's type
|
||||
VpnPacket::PayloadType get_type() const;
|
||||
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);
|
||||
void set_type(PayloadType type);
|
||||
|
||||
protected:
|
||||
VpnPacket& _packet;
|
||||
VpnControlPacket& _packet;
|
||||
size_t _tlv_pos;
|
||||
|
||||
friend class TunnelledPacket;
|
||||
};
|
||||
|
||||
/** 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);
|
||||
|
||||
/// Try to parse the packet as IPv6, return `false` upon failure.
|
||||
bool parse_as_ipv6();
|
||||
bool ipv6_parsed() const { return _ipv6_parsed; }
|
||||
const IPv6Header& get_ipv6_header() const { return _ipv6_header; }
|
||||
|
||||
private:
|
||||
bool _ipv6_parsed;
|
||||
IPv6Header _ipv6_header;
|
||||
};
|
||||
|
|
|
@ -29,9 +29,14 @@ size_t VpnPeer::write(const VpnPacket& packet) {
|
|||
return write(packet.get_data(), packet.get_data_size());
|
||||
}
|
||||
|
||||
void VpnPeer::got_inbound_packet(const VpnPacket& packet) {
|
||||
_packet_loss.log_packet(packet.get_seqno());
|
||||
}
|
||||
|
||||
PacketLossLogger::PacketLossLogger() : _cur_seqno(0) {}
|
||||
|
||||
void PacketLossLogger::log_packet(uint32_t seqno) {
|
||||
kdebugf(">> Logging %lu (loss %lf)\n", seqno, get_loss_rate());
|
||||
uint32_t m_seqno = seqno % PACKET_LOSS_HISTSIZE;
|
||||
int64_t diff = (int64_t)seqno - _cur_seqno;
|
||||
|
||||
|
|
|
@ -56,15 +56,21 @@ class VpnPeer {
|
|||
|
||||
void set_int_addr(const in6_addr& int_addr);
|
||||
|
||||
const PacketLossLogger& get_loss_logger() { return _packet_loss; }
|
||||
|
||||
size_t write(const char* data, size_t len);
|
||||
size_t write(const VpnPacket& packet);
|
||||
|
||||
uint32_t peek_next_seqno() const { return _next_send_seqno; }
|
||||
uint32_t next_seqno() { return _next_send_seqno++; }
|
||||
|
||||
void got_inbound_packet(const VpnPacket& packet);
|
||||
|
||||
private:
|
||||
UdpVpn* _vpn;
|
||||
sockaddr_in6 _ext_addr;
|
||||
in6_addr _int_addr;
|
||||
uint32_t _next_send_seqno;
|
||||
|
||||
PacketLossLogger _packet_loss;
|
||||
};
|
||||
|
|
5
main.cpp
5
main.cpp
|
@ -27,6 +27,10 @@ void stop_sig_handler(int signal) {
|
|||
vpn_instance->stop();
|
||||
}
|
||||
|
||||
void dump_sig_handler(int /*signal*/) {
|
||||
vpn_instance->dump_requested();
|
||||
}
|
||||
|
||||
bool parse_options(int argc, char** argv, ProgOptions& opts) {
|
||||
int option;
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
|
@ -91,6 +95,7 @@ int main(int argc, char** argv) {
|
|||
printf("=== END OPTIONS ==\n\n");
|
||||
|
||||
signal(SIGINT, stop_sig_handler);
|
||||
signal(SIGUSR1, dump_sig_handler);
|
||||
|
||||
try {
|
||||
if(program_options.listen && program_options.has_peer) {
|
||||
|
|
Loading…
Reference in a new issue