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 <string.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
#include "ip_header.hpp"
|
#include "ip_header.hpp"
|
||||||
|
|
||||||
static const size_t VPN_MTU = 1460; // TODO determine this -- issue #3
|
static const size_t VPN_MTU = 1460; // TODO determine this -- issue #3
|
||||||
|
|
||||||
UdpVpn::UdpVpn()
|
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));
|
_tun_dev.set_mtu(VpnPacket::get_tunnelled_mtu(_vpn_mtu));
|
||||||
_socket = socket(AF_INET6, SOCK_DGRAM, 0);
|
_socket = socket(AF_INET6, SOCK_DGRAM, 0);
|
||||||
|
@ -42,6 +44,11 @@ void UdpVpn::run() {
|
||||||
poll_fds[1].events = POLLIN;
|
poll_fds[1].events = POLLIN;
|
||||||
|
|
||||||
while(!_stopped) {
|
while(!_stopped) {
|
||||||
|
if(_dump_requested) {
|
||||||
|
dump_state();
|
||||||
|
_dump_requested = false;
|
||||||
|
}
|
||||||
|
|
||||||
rc = poll(poll_fds, nfds, -1);
|
rc = poll(poll_fds, nfds, -1);
|
||||||
|
|
||||||
if(rc < 0) {
|
if(rc < 0) {
|
||||||
|
@ -75,7 +82,7 @@ size_t UdpVpn::read_from_tun(char* buffer, size_t len) {
|
||||||
return _tun_dev.read(buffer, 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 payload_space = packet.get_payload_space();
|
||||||
size_t nread = read_from_tun(packet.get_payload(), payload_space);
|
size_t nread = read_from_tun(packet.get_payload(), payload_space);
|
||||||
packet.set_payload_size(nread);
|
packet.set_payload_size(nread);
|
||||||
|
@ -136,9 +143,8 @@ size_t UdpVpn::transmit_to_peer(VpnPacket& packet) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void UdpVpn::receive_from_tun() {
|
void UdpVpn::receive_from_tun() {
|
||||||
VpnPacket packet(_vpn_mtu);
|
VpnDataPacket packet(_vpn_mtu, false);
|
||||||
TunnelledPacket tunnelled = TunnelledPacket::create(packet);
|
size_t nread = read_from_tun(packet);
|
||||||
size_t nread = read_from_tun(tunnelled);
|
|
||||||
if(nread == 0)
|
if(nread == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -149,8 +155,8 @@ void UdpVpn::receive_from_tun() {
|
||||||
packet.set_peer(_peer.get());
|
packet.set_peer(_peer.get());
|
||||||
|
|
||||||
kdebugf("Transmitting %s -> %s, size %d\n",
|
kdebugf("Transmitting %s -> %s, size %d\n",
|
||||||
format_address(tunnelled.get_ipv6_header().source.s6_addr),
|
format_address(packet.get_ipv6_header().source.s6_addr),
|
||||||
format_address(tunnelled.get_ipv6_header().dest.s6_addr),
|
format_address(packet.get_ipv6_header().dest.s6_addr),
|
||||||
nread);
|
nread);
|
||||||
|
|
||||||
packet.prepare_for_sending();
|
packet.prepare_for_sending();
|
||||||
|
@ -158,7 +164,7 @@ void UdpVpn::receive_from_tun() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void UdpVpn::receive_from_udp() {
|
void UdpVpn::receive_from_udp() {
|
||||||
VpnPacket packet(_vpn_mtu);
|
VpnPacket packet(_vpn_mtu, true);
|
||||||
sockaddr_in6 peer_ext_addr;
|
sockaddr_in6 peer_ext_addr;
|
||||||
size_t nread = read_from_udp(packet, peer_ext_addr);
|
size_t nread = read_from_udp(packet, peer_ext_addr);
|
||||||
if(nread == 0)
|
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.
|
// If we don't have a peer yet -- we're just setting the peer to nullptr.
|
||||||
packet.set_peer(_peer.get());
|
packet.set_peer(_peer.get());
|
||||||
|
|
||||||
for(VpnPacketTLV tlv=packet.first_tlv();
|
if(packet.is_control()) {
|
||||||
!tlv.past_the_end();
|
VpnControlPacket ctrl_packet(std::move(packet));
|
||||||
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;
|
|
||||||
|
|
||||||
case VpnPacket::PAYLOAD_TYPE_UNDEF:
|
for(VpnPacketTLV tlv=ctrl_packet.first_tlv();
|
||||||
default:
|
!tlv.past_the_end();
|
||||||
debugf("#%d+%lu: ignoring TLV with bad type %d.\n",
|
tlv.seek_next_tlv())
|
||||||
packet.get_seqno(), tlv.get_offset(),
|
{
|
||||||
tlv.get_type());
|
switch(tlv.get_type()) {
|
||||||
break;
|
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
|
// Reinject into tun
|
||||||
kdebugf("Reinjecting tunnelled packet of size %d\n",
|
kdebugf("Reinjecting tunnelled packet of size %d [#%ld, TS=%ld μs]\n",
|
||||||
packet.get_payload_size());
|
packet.get_payload_size(),
|
||||||
|
packet.get_seqno(),
|
||||||
|
packet.get_sending_timestamp());
|
||||||
_tun_dev.write(packet.get_payload(), packet.get_payload_size());
|
_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.
|
// Stop the server. Can be called from an interrupt.
|
||||||
void stop() { _stopped = true; }
|
void stop() { _stopped = true; }
|
||||||
|
|
||||||
|
// A state dump has been requested
|
||||||
|
void dump_requested() { _dump_requested = true; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void acquire_peer(
|
virtual void acquire_peer(
|
||||||
TunnelledPacket& packet,
|
VpnDataPacket& packet,
|
||||||
const sockaddr_in6& peer_ext_addr) = 0;
|
const sockaddr_in6& peer_ext_addr) = 0;
|
||||||
|
|
||||||
size_t read_from_tun(char* buffer, size_t len);
|
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(char* buffer, size_t len, sockaddr_in6& peer_addr);
|
||||||
size_t read_from_udp(VpnPacket& packet, 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_tun();
|
||||||
void receive_from_udp();
|
void receive_from_udp();
|
||||||
|
|
||||||
void receive_tunnelled_tlv(TunnelledPacket& packet);
|
void receive_tunnelled_tlv(VpnDataPacket& packet);
|
||||||
|
|
||||||
|
void dump_state() const;
|
||||||
|
|
||||||
int _socket;
|
int _socket;
|
||||||
bool _stopped;
|
bool _stopped, _dump_requested;
|
||||||
|
|
||||||
size_t _vpn_mtu;
|
size_t _vpn_mtu;
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ UdpVpnClient::UdpVpnClient(const struct sockaddr_in6& server) : UdpVpn() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void UdpVpnClient::acquire_peer(
|
void UdpVpnClient::acquire_peer(
|
||||||
TunnelledPacket& packet,
|
VpnDataPacket& packet,
|
||||||
const sockaddr_in6&)
|
const sockaddr_in6&)
|
||||||
{
|
{
|
||||||
if(!packet.parse_as_ipv6())
|
if(!packet.parse_as_ipv6())
|
||||||
|
|
|
@ -8,6 +8,6 @@ class UdpVpnClient: public UdpVpn {
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void acquire_peer(
|
virtual void acquire_peer(
|
||||||
TunnelledPacket& packet,
|
VpnDataPacket& packet,
|
||||||
const sockaddr_in6& peer_ext_addr);
|
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(
|
void UdpVpnServer::acquire_peer(
|
||||||
TunnelledPacket& packet,
|
VpnDataPacket& packet,
|
||||||
const sockaddr_in6& peer_ext_addr)
|
const sockaddr_in6& peer_ext_addr)
|
||||||
{
|
{
|
||||||
if(_peer)
|
if(_peer)
|
||||||
|
@ -28,7 +28,7 @@ void UdpVpnServer::acquire_peer(
|
||||||
const in6_addr& peer_inner_addr = packet.get_ipv6_header().source;
|
const in6_addr& peer_inner_addr = packet.get_ipv6_header().source;
|
||||||
_peer = std::make_unique<VpnPeer>(this, peer_ext_addr, peer_inner_addr);
|
_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",
|
debugf("Got new peer %s:%d -- %s\n",
|
||||||
format_address(peer_ext_addr.sin6_addr.s6_addr),
|
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);
|
UdpVpnServer(const struct in6_addr& bind_addr6, in_port_t port);
|
||||||
protected:
|
protected:
|
||||||
virtual void acquire_peer(
|
virtual void acquire_peer(
|
||||||
TunnelledPacket& packet,
|
VpnDataPacket& packet,
|
||||||
const sockaddr_in6& peer_ext_addr);
|
const sockaddr_in6& peer_ext_addr);
|
||||||
|
|
||||||
void bind(const struct in6_addr& bind_addr6, in_port_t port);
|
void bind(const struct in6_addr& bind_addr6, in_port_t port);
|
||||||
|
|
119
VpnPacket.cpp
119
VpnPacket.cpp
|
@ -5,7 +5,7 @@
|
||||||
#include <cstring>
|
#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;
|
const size_t VpnControlPacket::TLV_HEADER_BYTES = 3;
|
||||||
|
|
||||||
static const size_t OUTER_HEADERS_BYTES =
|
static const size_t OUTER_HEADERS_BYTES =
|
||||||
40 /* IPv6 header */ + 8 /* UDP header */;
|
40 /* IPv6 header */ + 8 /* UDP header */;
|
||||||
|
@ -13,40 +13,67 @@ static const size_t OUTER_HEADERS_BYTES =
|
||||||
|
|
||||||
static const int
|
static const int
|
||||||
DATA_SEQNO_POS = 0,
|
DATA_SEQNO_POS = 0,
|
||||||
|
DATA_CTRLBIT_POS = 4,
|
||||||
DATA_TIMESTAMP_POS = 4;
|
DATA_TIMESTAMP_POS = 4;
|
||||||
|
|
||||||
VpnPacket::VpnPacket(size_t mtu)
|
VpnPacket::VpnPacket(size_t mtu, bool inbound)
|
||||||
: _peer(nullptr), _data_space(mtu-OUTER_HEADERS_BYTES),
|
: _peer(nullptr), _inbound(inbound), _data_space(mtu-OUTER_HEADERS_BYTES),
|
||||||
_data_size(VPN_HEADER_BYTES), _reception_timestamp(0)
|
_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() {
|
VpnPacket::~VpnPacket() {}
|
||||||
delete[] _data;
|
|
||||||
}
|
|
||||||
|
|
||||||
VpnPacketTLV VpnPacket::first_tlv() {
|
VpnPacket::VpnPacket(VpnPacket&& move_from) :
|
||||||
return VpnPacketTLV(*this, 0);
|
_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) {
|
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 {
|
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 {
|
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() {
|
void VpnPacket::prepare_for_sending() {
|
||||||
*(uint32_t*)(_data + DATA_SEQNO_POS) = next_seqno();
|
uint32_t* ts_field = (uint32_t*) (_data.get() + DATA_TIMESTAMP_POS);
|
||||||
*(uint32_t*)(_data + DATA_TIMESTAMP_POS) =
|
*ts_field &= 0x80000000UL;
|
||||||
std::chrono::time_point_cast<std::chrono::microseconds>(
|
*(uint32_t*)(_data.get() + DATA_SEQNO_POS) = htonl(next_seqno());
|
||||||
std::chrono::steady_clock::now()).time_since_epoch().count();
|
*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() {
|
void VpnPacket::upon_reception() {
|
||||||
|
@ -61,8 +88,35 @@ uint32_t VpnPacket::next_seqno() {
|
||||||
return _peer->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)
|
: _packet(packet), _tlv_pos(payload_offset)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
@ -71,10 +125,10 @@ VpnPacketTLV::VpnPacketTLV(const VpnPacketTLV& other) :
|
||||||
{}
|
{}
|
||||||
|
|
||||||
VpnPacketTLV VpnPacketTLV::create(
|
VpnPacketTLV VpnPacketTLV::create(
|
||||||
VpnPacket& packet, VpnPacket::PayloadType type)
|
VpnControlPacket& packet, VpnPacketTLV::PayloadType type)
|
||||||
{
|
{
|
||||||
VpnPacketTLV tlv = VpnPacketTLV(packet, packet.get_payload_size());
|
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();
|
char* data = tlv.get_data();
|
||||||
data[0] = type;
|
data[0] = type;
|
||||||
|
@ -89,7 +143,8 @@ VpnPacketTLV VpnPacketTLV::next_tlv() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void VpnPacketTLV::seek_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 {
|
uint16_t VpnPacketTLV::get_payload_size() const {
|
||||||
|
@ -108,25 +163,9 @@ uint16_t VpnPacketTLV::get_payload_space() const {
|
||||||
+ get_payload_size();
|
+ get_payload_size();
|
||||||
}
|
}
|
||||||
|
|
||||||
VpnPacket::PayloadType VpnPacketTLV::get_type() const {
|
VpnPacketTLV::PayloadType VpnPacketTLV::get_type() const {
|
||||||
return (VpnPacket::PayloadType)(*(uint8_t*)(get_data()));
|
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;
|
*(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] |
|
* | 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 |
|
* | Type [1B] | First payload size (B) [2B] | Payload |
|
||||||
* +---------------+-----------------------------------------------+
|
* +---------------+-----------------------------------------------+
|
||||||
|
@ -27,7 +37,7 @@
|
||||||
* | ... |
|
* | ... |
|
||||||
* +---------------------------------------------------------------+
|
* +---------------------------------------------------------------+
|
||||||
*
|
*
|
||||||
* Where
|
* Where:
|
||||||
* - Type is one of the values from PayloadType below;
|
* - Type is one of the values from PayloadType below;
|
||||||
* - Sender ID is an arbitrary value, recommended to be randomly chosen
|
* - Sender ID is an arbitrary value, recommended to be randomly chosen
|
||||||
* - Payload size is the size of the payload (excluding headers), in bytes;
|
* - Payload size is the size of the payload (excluding headers), in bytes;
|
||||||
|
@ -40,32 +50,30 @@ class VpnPacketTLV;
|
||||||
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;
|
|
||||||
|
|
||||||
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 {};
|
class PeerNotSet: public std::exception {};
|
||||||
|
|
||||||
VpnPacket(size_t mtu);
|
VpnPacket(size_t mtu, bool inbound);
|
||||||
~VpnPacket();
|
~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
|
/// Get the maximal payload space for a given tunnel MTU
|
||||||
static size_t get_tunnelled_mtu(size_t udp_mtu);
|
static size_t get_tunnelled_mtu(size_t udp_mtu);
|
||||||
|
|
||||||
/// Set packet peer -- used for sequence numbers
|
/// Set packet peer -- used for sequence numbers and loss rate
|
||||||
void set_peer(VpnPeer* peer) { _peer = peer; }
|
void set_peer(VpnPeer* peer);
|
||||||
|
|
||||||
/// Get a pointer to the packet payload (const version)
|
/// 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
|
/// 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
|
/// Get a pointer to the first free byte of the packet payload
|
||||||
char* get_next_payload() { return get_payload() + get_payload_size(); }
|
char* get_next_payload() { return get_payload() + get_payload_size(); }
|
||||||
/// Get the space allocated for the packet payload
|
/// Get the space allocated for the packet payload
|
||||||
|
@ -83,9 +91,9 @@ class VpnPacket {
|
||||||
_data_size += payload_size_increment; }
|
_data_size += payload_size_increment; }
|
||||||
|
|
||||||
/// Get a pointer to the full packet data (const version)
|
/// 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
|
/// 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
|
/// 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
|
/// Get the total current size of the packet
|
||||||
|
@ -99,6 +107,8 @@ class VpnPacket {
|
||||||
uint32_t get_sending_timestamp() const;
|
uint32_t get_sending_timestamp() const;
|
||||||
/// Get this packet's reception timestamp
|
/// Get this packet's reception timestamp
|
||||||
uint32_t get_reception_timestamp() const { return _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
|
/** 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
|
* to the time the packet is actually sent as possible, as it handles
|
||||||
|
@ -111,30 +121,69 @@ class VpnPacket {
|
||||||
*/
|
*/
|
||||||
void upon_reception();
|
void upon_reception();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void set_control(bool is_control);
|
||||||
|
|
||||||
private: // methods
|
private: // methods
|
||||||
inline uint32_t next_seqno();
|
inline uint32_t next_seqno();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VpnPeer* _peer; // raw pointer: we do not own the peer in any way
|
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;
|
size_t _data_space, _data_size;
|
||||||
|
|
||||||
uint32_t _reception_timestamp;
|
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 */
|
/** Base class for a TLV contained in a VpnPacket */
|
||||||
class VpnPacketTLV {
|
class VpnPacketTLV {
|
||||||
public:
|
public:
|
||||||
VpnPacketTLV(VpnPacket& packet, size_t payload_offset);
|
VpnPacketTLV(VpnControlPacket& packet, size_t payload_offset);
|
||||||
VpnPacketTLV(const VpnPacketTLV& other);
|
VpnPacketTLV(const VpnPacketTLV& other);
|
||||||
|
|
||||||
static VpnPacketTLV create(
|
enum PayloadType {
|
||||||
VpnPacket& packet,
|
PAYLOAD_TYPE_UNDEF, ///< Undefined packet type
|
||||||
VpnPacket::PayloadType type=VpnPacket::PAYLOAD_TYPE_UNDEF);
|
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.
|
/// Get the next TLV in this packet.
|
||||||
VpnPacketTLV next_tlv();
|
VpnPacketTLV next_tlv();
|
||||||
|
@ -150,10 +199,12 @@ class VpnPacketTLV {
|
||||||
|
|
||||||
/// Get a pointer to the payload (const version)
|
/// Get a pointer to the payload (const version)
|
||||||
const char* get_payload() const {
|
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
|
/// Get a pointer to the payload
|
||||||
char* get_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
|
/// Get the current payload size
|
||||||
uint16_t get_payload_size() const;
|
uint16_t get_payload_size() const;
|
||||||
/// Set the current payload size
|
/// Set the current payload size
|
||||||
|
@ -168,43 +219,22 @@ class VpnPacketTLV {
|
||||||
char* get_data() { return _packet.get_payload() + _tlv_pos; }
|
char* get_data() { return _packet.get_payload() + _tlv_pos; }
|
||||||
/// Get the current raw data size
|
/// Get the current raw data size
|
||||||
uint16_t get_data_size() const {
|
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
|
/// Set the current raw data size
|
||||||
void set_data_size(uint16_t 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
|
/// Get this TLV's type
|
||||||
VpnPacket::PayloadType get_type() const;
|
PayloadType get_type() const;
|
||||||
|
|
||||||
bool operator==(const VpnPacketTLV& other) const {
|
bool operator==(const VpnPacketTLV& other) const {
|
||||||
return &_packet == &other._packet && _tlv_pos == other._tlv_pos; }
|
return &_packet == &other._packet && _tlv_pos == other._tlv_pos; }
|
||||||
|
|
||||||
protected: // meth
|
protected: // meth
|
||||||
/// Set this TLV's type
|
/// Set this TLV's type
|
||||||
void set_type(VpnPacket::PayloadType type);
|
void set_type(PayloadType type);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
VpnPacket& _packet;
|
VpnControlPacket& _packet;
|
||||||
size_t _tlv_pos;
|
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());
|
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) {}
|
PacketLossLogger::PacketLossLogger() : _cur_seqno(0) {}
|
||||||
|
|
||||||
void PacketLossLogger::log_packet(uint32_t seqno) {
|
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;
|
uint32_t m_seqno = seqno % PACKET_LOSS_HISTSIZE;
|
||||||
int64_t diff = (int64_t)seqno - _cur_seqno;
|
int64_t diff = (int64_t)seqno - _cur_seqno;
|
||||||
|
|
||||||
|
|
|
@ -56,15 +56,21 @@ class VpnPeer {
|
||||||
|
|
||||||
void set_int_addr(const in6_addr& int_addr);
|
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 char* data, size_t len);
|
||||||
size_t write(const VpnPacket& packet);
|
size_t write(const VpnPacket& packet);
|
||||||
|
|
||||||
uint32_t peek_next_seqno() const { return _next_send_seqno; }
|
uint32_t peek_next_seqno() const { return _next_send_seqno; }
|
||||||
uint32_t next_seqno() { return _next_send_seqno++; }
|
uint32_t next_seqno() { return _next_send_seqno++; }
|
||||||
|
|
||||||
|
void got_inbound_packet(const VpnPacket& packet);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
UdpVpn* _vpn;
|
UdpVpn* _vpn;
|
||||||
sockaddr_in6 _ext_addr;
|
sockaddr_in6 _ext_addr;
|
||||||
in6_addr _int_addr;
|
in6_addr _int_addr;
|
||||||
uint32_t _next_send_seqno;
|
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();
|
vpn_instance->stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dump_sig_handler(int /*signal*/) {
|
||||||
|
vpn_instance->dump_requested();
|
||||||
|
}
|
||||||
|
|
||||||
bool parse_options(int argc, char** argv, ProgOptions& opts) {
|
bool parse_options(int argc, char** argv, ProgOptions& opts) {
|
||||||
int option;
|
int option;
|
||||||
memset(&opts, 0, sizeof(opts));
|
memset(&opts, 0, sizeof(opts));
|
||||||
|
@ -91,6 +95,7 @@ int main(int argc, char** argv) {
|
||||||
printf("=== END OPTIONS ==\n\n");
|
printf("=== END OPTIONS ==\n\n");
|
||||||
|
|
||||||
signal(SIGINT, stop_sig_handler);
|
signal(SIGINT, stop_sig_handler);
|
||||||
|
signal(SIGUSR1, dump_sig_handler);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if(program_options.listen && program_options.has_peer) {
|
if(program_options.listen && program_options.has_peer) {
|
||||||
|
|
Loading…
Reference in a new issue