Split data and control packets

This commit is contained in:
Théophile Bastian 2020-06-26 19:41:55 +02:00
parent f07f2a853d
commit c5541d1e79
11 changed files with 237 additions and 132 deletions

View file

@ -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();
if(packet.is_control()) {
VpnControlPacket ctrl_packet(std::move(packet));
for(VpnPacketTLV tlv=ctrl_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;
case VpnPacket::PAYLOAD_TYPE_UNDEF:
case VpnPacketTLV::PAYLOAD_TYPE_UNDEF:
default:
debugf("#%d+%lu: ignoring TLV with bad type %d.\n",
packet.get_seqno(), tlv.get_offset(),
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");
}

View file

@ -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;

View file

@ -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())

View file

@ -8,6 +8,6 @@ class UdpVpnClient: public UdpVpn {
protected:
virtual void acquire_peer(
TunnelledPacket& packet,
VpnDataPacket& packet,
const sockaddr_in6& peer_ext_addr);
};

View file

@ -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),

View file

@ -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);

View file

@ -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);
}

View file

@ -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;
};

View file

@ -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;

View file

@ -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;
};

View file

@ -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) {