Compare commits
6 commits
Author | SHA1 | Date | |
---|---|---|---|
46473a060e | |||
ecc996f3f0 | |||
7c1e5562f2 | |||
b012375427 | |||
8f5d3b9805 | |||
99f14b84c1 |
18 changed files with 244 additions and 288 deletions
|
@ -5,21 +5,22 @@
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <cstdlib>
|
#include <stdlib.h>
|
||||||
#include <cstring>
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "TunDevice.hpp"
|
#include "TunDevice.hpp"
|
||||||
|
|
||||||
static const size_t TUN_MTU = 1500; // TODO determine this cleanly
|
static const size_t TUN_MTU = 1500; // TODO determine this cleanly
|
||||||
|
|
||||||
TunDevice::TunDevice(const std::string& dev)
|
TunDevice::TunDevice(const char* dev)
|
||||||
{
|
{
|
||||||
struct ifreq ifr;
|
struct ifreq ifr;
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
if( (fd = open("/dev/net/tun", O_RDWR)) < 0 ) {
|
if( (fd = open("/dev/net/tun", O_RDWR)) < 0 ) {
|
||||||
throw TunDevice::InitializationError(
|
perror("Tun device: cannot open /dev/net/tun: ");
|
||||||
"Cannot open /dev/net/tun", errno, true);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&ifr, 0, sizeof(ifr));
|
memset(&ifr, 0, sizeof(ifr));
|
||||||
|
@ -30,16 +31,18 @@ TunDevice::TunDevice(const std::string& dev)
|
||||||
* IFF_NO_PI - Do not provide packet information
|
* IFF_NO_PI - Do not provide packet information
|
||||||
*/
|
*/
|
||||||
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
|
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
|
||||||
if(!dev.empty()) {
|
if(dev != nullptr) {
|
||||||
if(dev.size() >= IFNAMSIZ - 2)
|
if(strlen(dev) >= IFNAMSIZ - 2) {
|
||||||
throw TunDevice::InitializationError("Device name is too long.");
|
fprintf(stderr, "Tun device: device name is too long.\n");
|
||||||
strncpy(ifr.ifr_name, dev.c_str(), IFNAMSIZ-1);
|
exit(1);
|
||||||
|
}
|
||||||
|
strncpy(ifr.ifr_name, dev, IFNAMSIZ-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ioctl(fd, TUNSETIFF, (void *) &ifr) < 0){
|
if(ioctl(fd, TUNSETIFF, (void *) &ifr) < 0){
|
||||||
close(fd);
|
close(fd);
|
||||||
throw TunDevice::InitializationError(
|
perror("Tun device: tunnel interface failed [TUNSETIFF]: ");
|
||||||
"Tunnel interface failed [TUNSETIFF]", errno, true);
|
exit(1);
|
||||||
}
|
}
|
||||||
_dev_name = ifr.ifr_name;
|
_dev_name = ifr.ifr_name;
|
||||||
_fd = fd;
|
_fd = fd;
|
||||||
|
@ -50,16 +53,16 @@ TunDevice::TunDevice(const std::string& dev)
|
||||||
if(ioctl(sockfd, SIOCGIFFLAGS, (void*) &ifr) < 0) {
|
if(ioctl(sockfd, SIOCGIFFLAGS, (void*) &ifr) < 0) {
|
||||||
close(fd);
|
close(fd);
|
||||||
close(sockfd);
|
close(sockfd);
|
||||||
throw TunDevice::InitializationError(
|
perror("Tun device: could not get tunnel interface flags: ");
|
||||||
"Could not get tunnel interface flags", errno, true);
|
exit(1);
|
||||||
}
|
}
|
||||||
ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
|
ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
|
||||||
|
|
||||||
if(ioctl(sockfd, SIOCSIFFLAGS, (void*) &ifr) < 0) {
|
if(ioctl(sockfd, SIOCSIFFLAGS, (void*) &ifr) < 0) {
|
||||||
close(fd);
|
close(fd);
|
||||||
close(sockfd);
|
close(sockfd);
|
||||||
throw TunDevice::InitializationError(
|
perror("Tun device: could not bring tunnel interface up: ");
|
||||||
"Could not bring tunnel interface up", errno, true);
|
exit(1);
|
||||||
}
|
}
|
||||||
close(sockfd);
|
close(sockfd);
|
||||||
|
|
||||||
|
@ -77,7 +80,7 @@ uint16_t TunDevice::get_mtu() const {
|
||||||
|
|
||||||
struct ifreq ifr;
|
struct ifreq ifr;
|
||||||
ifr.ifr_addr.sa_family = AF_INET6;
|
ifr.ifr_addr.sa_family = AF_INET6;
|
||||||
strncpy(ifr.ifr_name, _dev_name.c_str(), sizeof(ifr.ifr_name)-1);
|
strncpy(ifr.ifr_name, _dev_name, sizeof(ifr.ifr_name)-1);
|
||||||
if (ioctl(sockfd, SIOCGIFMTU, (caddr_t)&ifr) < 0)
|
if (ioctl(sockfd, SIOCGIFMTU, (caddr_t)&ifr) < 0)
|
||||||
return 0;
|
return 0;
|
||||||
close(sockfd);
|
close(sockfd);
|
||||||
|
@ -89,7 +92,7 @@ bool TunDevice::set_mtu(uint16_t mtu) {
|
||||||
|
|
||||||
struct ifreq ifr;
|
struct ifreq ifr;
|
||||||
ifr.ifr_addr.sa_family = AF_INET6;
|
ifr.ifr_addr.sa_family = AF_INET6;
|
||||||
strncpy(ifr.ifr_name, _dev_name.c_str(), sizeof(ifr.ifr_name)-1);
|
strncpy(ifr.ifr_name, _dev_name, sizeof(ifr.ifr_name)-1);
|
||||||
ifr.ifr_mtu = mtu;
|
ifr.ifr_mtu = mtu;
|
||||||
if (ioctl(sockfd, SIOCSIFMTU, (caddr_t)&ifr) < 0)
|
if (ioctl(sockfd, SIOCSIFMTU, (caddr_t)&ifr) < 0)
|
||||||
return false;
|
return false;
|
||||||
|
@ -102,8 +105,8 @@ size_t TunDevice::poll_packet(char* read_buffer, size_t buf_size, int timeout) {
|
||||||
if(poll_rc < 0) {
|
if(poll_rc < 0) {
|
||||||
if(errno == EINTR) // Interrupt.
|
if(errno == EINTR) // Interrupt.
|
||||||
return 0;
|
return 0;
|
||||||
throw TunDevice::NetError(
|
perror("Tun device: error polling from interface: ");
|
||||||
"Error polling from interface", errno, true);
|
exit(1);
|
||||||
}
|
}
|
||||||
else if(poll_rc == 0 || (_poll_fd.revents & POLLIN) == 0) {
|
else if(poll_rc == 0 || (_poll_fd.revents & POLLIN) == 0) {
|
||||||
// Nothing to read
|
// Nothing to read
|
||||||
|
@ -116,8 +119,8 @@ size_t TunDevice::poll_packet(char* read_buffer, size_t buf_size, int timeout) {
|
||||||
size_t TunDevice::read(char* read_buffer, size_t buf_size) {
|
size_t TunDevice::read(char* read_buffer, size_t buf_size) {
|
||||||
int nread = ::read(_fd, read_buffer, buf_size);
|
int nread = ::read(_fd, read_buffer, buf_size);
|
||||||
if(nread < 0) {
|
if(nread < 0) {
|
||||||
throw TunDevice::NetError(
|
perror("Tun device: error reading from interface: ");
|
||||||
"Error reading from interface", errno, true);
|
exit(1);
|
||||||
}
|
}
|
||||||
_last_read_size = nread;
|
_last_read_size = nread;
|
||||||
return _last_read_size;
|
return _last_read_size;
|
||||||
|
@ -126,8 +129,8 @@ size_t TunDevice::read(char* read_buffer, size_t buf_size) {
|
||||||
size_t TunDevice::write(const char* data, size_t len) {
|
size_t TunDevice::write(const char* data, size_t len) {
|
||||||
int nwritten = ::write(_fd, data, len);
|
int nwritten = ::write(_fd, data, len);
|
||||||
if(nwritten < 0) {
|
if(nwritten < 0) {
|
||||||
throw TunDevice::NetError(
|
perror("Tun device: error writing to interface: ");
|
||||||
"Error writing to interface: ", errno, true);
|
exit(1);
|
||||||
}
|
}
|
||||||
return nwritten;
|
return nwritten;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +1,14 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include "util.hpp"
|
#include "util.hpp"
|
||||||
|
|
||||||
class TunDevice {
|
class TunDevice {
|
||||||
public:
|
public:
|
||||||
class InitializationError : public MsgException {
|
TunDevice(const char* dev);
|
||||||
public:
|
|
||||||
InitializationError(
|
|
||||||
const std::string& msg,
|
|
||||||
int code=0,
|
|
||||||
bool is_perror=false)
|
|
||||||
: MsgException(msg, code, is_perror) {}
|
|
||||||
};
|
|
||||||
class NetError : public MsgException {
|
|
||||||
public:
|
|
||||||
NetError(
|
|
||||||
const std::string& msg,
|
|
||||||
int code=0,
|
|
||||||
bool is_perror=false)
|
|
||||||
: MsgException(msg, code, is_perror) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
TunDevice(const std::string& dev);
|
|
||||||
~TunDevice();
|
~TunDevice();
|
||||||
|
|
||||||
const std::string& get_dev_name() const { return _dev_name; }
|
const char* get_dev_name() const { return _dev_name; }
|
||||||
int get_fd() const { return _fd; }
|
int get_fd() const { return _fd; }
|
||||||
|
|
||||||
/** Get the interface's MTU */
|
/** Get the interface's MTU */
|
||||||
|
@ -48,7 +30,7 @@ class TunDevice {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int _fd;
|
int _fd;
|
||||||
std::string _dev_name;
|
char*_dev_name;
|
||||||
struct pollfd _poll_fd;
|
struct pollfd _poll_fd;
|
||||||
size_t _last_read_size;
|
size_t _last_read_size;
|
||||||
};
|
};
|
||||||
|
|
46
UdpVpn.cpp
46
UdpVpn.cpp
|
@ -1,6 +1,5 @@
|
||||||
#include "UdpVpn.hpp"
|
#include "UdpVpn.hpp"
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
@ -10,6 +9,7 @@
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "ip_header.hpp"
|
#include "ip_header.hpp"
|
||||||
|
|
||||||
|
@ -19,12 +19,16 @@ UdpVpn::UdpVpn()
|
||||||
: _stopped(false), _dump_requested(false), _vpn_mtu(VPN_MTU),
|
: _stopped(false), _dump_requested(false), _vpn_mtu(VPN_MTU),
|
||||||
_tun_dev("cvpn%d"), _peer(nullptr)
|
_tun_dev("cvpn%d"), _peer(nullptr)
|
||||||
{
|
{
|
||||||
_last_tick = _last_control_sent =
|
clock_gettime(CLOCK_MONOTONIC, &_last_tick);
|
||||||
std::chrono::steady_clock::now() - std::chrono::seconds(1);
|
_last_tick.tv_sec--;
|
||||||
|
memcpy(&_last_control_sent, &_last_tick, sizeof(struct timespec));
|
||||||
|
|
||||||
_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);
|
||||||
if(_socket < 0)
|
if(_socket < 0) {
|
||||||
throw UdpVpn::InitializationError("Cannot create socket", errno, true);
|
perror("UdpVpn: cannot create socket: ");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UdpVpn::~UdpVpn() {
|
UdpVpn::~UdpVpn() {
|
||||||
|
@ -57,23 +61,19 @@ void UdpVpn::run() {
|
||||||
if(rc < 0) {
|
if(rc < 0) {
|
||||||
if(errno == EINTR) // Interrupt.
|
if(errno == EINTR) // Interrupt.
|
||||||
continue;
|
continue;
|
||||||
throw UdpVpn::NetError(
|
perror("UdpVpn: error polling from interface: ");
|
||||||
"Error polling from interface", errno, true);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ## Check periodic actions
|
// ## Check periodic actions
|
||||||
if(_peer) {
|
if(_peer) {
|
||||||
if(std::chrono::steady_clock::now() - _last_control_sent
|
if(timespec_us_ellapsed(_last_control_sent) > 1000*1000) {
|
||||||
> std::chrono::milliseconds(100))
|
|
||||||
{
|
|
||||||
if(_peer->send_control_packet())
|
if(_peer->send_control_packet())
|
||||||
_last_control_sent = std::chrono::steady_clock::now();
|
clock_gettime(CLOCK_MONOTONIC, &_last_control_sent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(std::chrono::steady_clock::now() - _last_tick
|
if(timespec_us_ellapsed(_last_tick) > 50*1000) {
|
||||||
> std::chrono::milliseconds(50))
|
clock_gettime(CLOCK_MONOTONIC, &_last_tick);
|
||||||
{
|
|
||||||
_last_tick = std::chrono::steady_clock::now();
|
|
||||||
if(_peer)
|
if(_peer)
|
||||||
_peer->tick();
|
_peer->tick();
|
||||||
}
|
}
|
||||||
|
@ -127,8 +127,10 @@ size_t UdpVpn::read_from_udp(char* buffer, size_t len,
|
||||||
nread = recvfrom(_socket, buffer, len, 0,
|
nread = recvfrom(_socket, buffer, len, 0,
|
||||||
(struct sockaddr*) &peer_addr, &peer_addr_len);
|
(struct sockaddr*) &peer_addr, &peer_addr_len);
|
||||||
|
|
||||||
if(nread < 0)
|
if(nread < 0) {
|
||||||
throw UdpVpn::NetError("Cannot receive datagram", errno, true);
|
perror("UdpVpn: cannot receive datagram: ");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
if(nread == 0)
|
if(nread == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -173,7 +175,7 @@ void UdpVpn::receive_from_tun() {
|
||||||
debugf("Dropping packet: no peer yet.\n");
|
debugf("Dropping packet: no peer yet.\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
packet.set_peer(_peer.get());
|
packet.set_peer(_peer);
|
||||||
|
|
||||||
kdebugf("Transmitting %s -> %s, size %d\n",
|
kdebugf("Transmitting %s -> %s, size %d\n",
|
||||||
format_address(packet.get_ipv6_header().source.s6_addr),
|
format_address(packet.get_ipv6_header().source.s6_addr),
|
||||||
|
@ -192,7 +194,7 @@ void UdpVpn::receive_from_udp() {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// 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);
|
||||||
|
|
||||||
if(packet.is_control()) {
|
if(packet.is_control()) {
|
||||||
VpnControlPacket ctrl_packet(std::move(packet));
|
VpnControlPacket ctrl_packet(std::move(packet));
|
||||||
|
@ -257,12 +259,10 @@ void UdpVpn::dump_state() const {
|
||||||
round(_peer->get_loss_logger().get_loss_rate() * 100));
|
round(_peer->get_loss_logger().get_loss_rate() * 100));
|
||||||
printf("Packet loss rate (outbound): %.0lf%%\n",
|
printf("Packet loss rate (outbound): %.0lf%%\n",
|
||||||
round(_peer->get_loss_reports().loss_rate() * 100));
|
round(_peer->get_loss_reports().loss_rate() * 100));
|
||||||
printf("RTT: %.02lf ms avg, %.02lf ms last [last updated: %lu ms ago]\n",
|
printf("RTT: %.02lf ms avg, %.02lf ms last [last updated: %u ms ago]\n",
|
||||||
(double)_peer->get_rtt().avg_rtt() / 1e3,
|
(double)_peer->get_rtt().avg_rtt() / 1e3,
|
||||||
(double)_peer->get_rtt().cur_rtt() / 1e3,
|
(double)_peer->get_rtt().cur_rtt() / 1e3,
|
||||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
timespec_us_ellapsed(_peer->get_rtt().get_last_update()) / 1000
|
||||||
std::chrono::steady_clock::now()
|
|
||||||
- _peer->get_rtt().get_last_update()).count()
|
|
||||||
);
|
);
|
||||||
printf("Total bytes transmitted: %s\n",
|
printf("Total bytes transmitted: %s\n",
|
||||||
human_readable_unit(_peer->get_tot_bytes_sent(), "B"));
|
human_readable_unit(_peer->get_tot_bytes_sent(), "B"));
|
||||||
|
|
22
UdpVpn.hpp
22
UdpVpn.hpp
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
#include "util.hpp"
|
#include "util.hpp"
|
||||||
#include "TunDevice.hpp"
|
#include "TunDevice.hpp"
|
||||||
|
@ -12,23 +13,6 @@
|
||||||
|
|
||||||
class UdpVpn {
|
class UdpVpn {
|
||||||
public:
|
public:
|
||||||
class InitializationError : public MsgException {
|
|
||||||
public:
|
|
||||||
InitializationError(
|
|
||||||
const std::string& msg,
|
|
||||||
int code=0,
|
|
||||||
bool is_perror=false)
|
|
||||||
: MsgException(msg, code, is_perror) {}
|
|
||||||
};
|
|
||||||
class NetError : public MsgException {
|
|
||||||
public:
|
|
||||||
NetError(
|
|
||||||
const std::string& msg,
|
|
||||||
int code=0,
|
|
||||||
bool is_perror=false)
|
|
||||||
: MsgException(msg, code, is_perror) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
UdpVpn();
|
UdpVpn();
|
||||||
virtual ~UdpVpn();
|
virtual ~UdpVpn();
|
||||||
|
|
||||||
|
@ -71,9 +55,9 @@ class UdpVpn {
|
||||||
size_t _vpn_mtu;
|
size_t _vpn_mtu;
|
||||||
|
|
||||||
TunDevice _tun_dev;
|
TunDevice _tun_dev;
|
||||||
std::unique_ptr<VpnPeer> _peer;
|
VpnPeer* _peer;
|
||||||
|
|
||||||
std::chrono::steady_clock::time_point
|
struct timespec
|
||||||
_last_control_sent, /**< A control is offered to be sent approx.
|
_last_control_sent, /**< A control is offered to be sent approx.
|
||||||
every 100ms */
|
every 100ms */
|
||||||
_last_tick /**< A tick occurs approx. each 50ms */;
|
_last_tick /**< A tick occurs approx. each 50ms */;
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
#include <cstring>
|
#include <string.h>
|
||||||
|
|
||||||
#include "UdpVpnClient.hpp"
|
#include "UdpVpnClient.hpp"
|
||||||
#include "ip_header.hpp"
|
#include "ip_header.hpp"
|
||||||
|
|
||||||
UdpVpnClient::UdpVpnClient(const struct sockaddr_in6& server) : UdpVpn() {
|
UdpVpnClient::UdpVpnClient(const struct sockaddr_in6& server) : UdpVpn() {
|
||||||
_peer = std::make_unique<VpnPeer>(this, server, in6addr_any);
|
_peer = new VpnPeer(this, server, in6addr_any);
|
||||||
|
}
|
||||||
|
|
||||||
|
UdpVpnClient::~UdpVpnClient() {
|
||||||
|
delete _peer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UdpVpnClient::acquire_peer(
|
void UdpVpnClient::acquire_peer(
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
class UdpVpnClient: public UdpVpn {
|
class UdpVpnClient: public UdpVpn {
|
||||||
public:
|
public:
|
||||||
UdpVpnClient(const struct sockaddr_in6& server);
|
UdpVpnClient(const struct sockaddr_in6& server);
|
||||||
|
~UdpVpnClient();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void acquire_peer(
|
virtual void acquire_peer(
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include <cstring>
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "UdpVpnServer.hpp"
|
#include "UdpVpnServer.hpp"
|
||||||
#include "ip_header.hpp"
|
#include "ip_header.hpp"
|
||||||
|
@ -15,6 +16,11 @@ UdpVpnServer::UdpVpnServer(const struct in6_addr& bind_addr6, in_port_t port)
|
||||||
bind(bind_addr6, port);
|
bind(bind_addr6, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UdpVpnServer::~UdpVpnServer() {
|
||||||
|
if(_peer != nullptr)
|
||||||
|
delete _peer;
|
||||||
|
}
|
||||||
|
|
||||||
void UdpVpnServer::acquire_peer(
|
void UdpVpnServer::acquire_peer(
|
||||||
VpnDataPacket& packet,
|
VpnDataPacket& packet,
|
||||||
const sockaddr_in6& peer_ext_addr)
|
const sockaddr_in6& peer_ext_addr)
|
||||||
|
@ -26,9 +32,9 @@ void UdpVpnServer::acquire_peer(
|
||||||
if(!packet.parse_as_ipv6())
|
if(!packet.parse_as_ipv6())
|
||||||
return;
|
return;
|
||||||
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 = new VpnPeer(this, peer_ext_addr, peer_inner_addr);
|
||||||
|
|
||||||
packet.set_peer(_peer.get());
|
packet.set_peer(_peer);
|
||||||
|
|
||||||
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),
|
||||||
|
@ -46,7 +52,8 @@ void UdpVpnServer::bind(const struct in6_addr& bind_addr6, in_port_t port) {
|
||||||
rc = ::bind(
|
rc = ::bind(
|
||||||
_socket, (const struct sockaddr*)&_bind_addr, sizeof(_bind_addr));
|
_socket, (const struct sockaddr*)&_bind_addr, sizeof(_bind_addr));
|
||||||
if(rc < 0) {
|
if(rc < 0) {
|
||||||
throw UdpVpn::InitializationError("Cannot bind socket", errno, true);
|
perror("UdpVpn: cannot bind socket: ");
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
debugf("> Listening on port %d\n", port);
|
debugf("> Listening on port %d\n", port);
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "UdpVpn.hpp"
|
#include "UdpVpn.hpp"
|
||||||
|
|
||||||
class UdpVpnServer: public UdpVpn {
|
class UdpVpnServer: public UdpVpn {
|
||||||
public:
|
public:
|
||||||
UdpVpnServer(in_port_t port);
|
UdpVpnServer(in_port_t port);
|
||||||
UdpVpnServer(const struct in6_addr& bind_addr6, in_port_t port);
|
UdpVpnServer(const struct in6_addr& bind_addr6, in_port_t port);
|
||||||
|
|
||||||
|
~UdpVpnServer();
|
||||||
protected:
|
protected:
|
||||||
virtual void acquire_peer(
|
virtual void acquire_peer(
|
||||||
VpnDataPacket& packet,
|
VpnDataPacket& packet,
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
#include "VpnPacket.hpp"
|
#include "VpnPacket.hpp"
|
||||||
#include "VpnPeer.hpp"
|
#include "VpnPeer.hpp"
|
||||||
|
|
||||||
#include <chrono>
|
#include <string.h>
|
||||||
#include <cstring>
|
#include <stdio.h>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
const size_t VpnPacket::VPN_HEADER_BYTES = 8;
|
const size_t VpnPacket::VPN_HEADER_BYTES = 8;
|
||||||
const size_t VpnControlPacket::TLV_HEADER_BYTES = 3;
|
const size_t VpnControlPacket::TLV_HEADER_BYTES = 3;
|
||||||
|
@ -20,10 +21,12 @@ VpnPacket::VpnPacket(size_t mtu, bool inbound)
|
||||||
: _peer(nullptr), _inbound(inbound), _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 = std::unique_ptr<char[]>(new char[mtu - OUTER_HEADERS_BYTES]);
|
_data = (char*) malloc(sizeof(char) * (mtu - OUTER_HEADERS_BYTES));
|
||||||
}
|
}
|
||||||
|
|
||||||
VpnPacket::~VpnPacket() {}
|
VpnPacket::~VpnPacket() {
|
||||||
|
free(_data);
|
||||||
|
}
|
||||||
|
|
||||||
VpnPacket::VpnPacket(VpnPacket&& move_from) :
|
VpnPacket::VpnPacket(VpnPacket&& move_from) :
|
||||||
_peer(move_from._peer),
|
_peer(move_from._peer),
|
||||||
|
@ -45,49 +48,43 @@ void VpnPacket::set_peer(VpnPeer* peer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t VpnPacket::get_seqno() const {
|
uint32_t VpnPacket::get_seqno() const {
|
||||||
return ntohl(*(uint32_t*)(_data.get() + DATA_SEQNO_POS));
|
return ntohl(*(uint32_t*)(_data + DATA_SEQNO_POS));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t VpnPacket::get_sending_timestamp() const {
|
uint32_t VpnPacket::get_sending_timestamp() const {
|
||||||
return ntohl(
|
return ntohl(
|
||||||
*(uint32_t*)(_data.get() + DATA_TIMESTAMP_POS) & 0x7fffffffUL
|
*(uint32_t*)(_data + DATA_TIMESTAMP_POS) & 0x7fffffffUL
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VpnPacket::is_control() const {
|
bool VpnPacket::is_control() const {
|
||||||
return *(unsigned char*)(_data.get() + DATA_CTRLBIT_POS) & 0x80;
|
return *(unsigned char*)(_data + DATA_CTRLBIT_POS) & 0x80;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VpnPacket::set_control(bool is_control) {
|
void VpnPacket::set_control(bool is_control) {
|
||||||
unsigned char* ctrl_field =
|
unsigned char* ctrl_field =
|
||||||
(unsigned char*) (_data.get() + DATA_CTRLBIT_POS);
|
(unsigned char*) (_data + DATA_CTRLBIT_POS);
|
||||||
*ctrl_field &= 0x7f;
|
*ctrl_field &= 0x7f;
|
||||||
if(is_control)
|
if(is_control)
|
||||||
*ctrl_field |= 0x80;
|
*ctrl_field |= 0x80;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VpnPacket::prepare_for_sending() {
|
void VpnPacket::prepare_for_sending() {
|
||||||
uint32_t* ts_field = (uint32_t*) (_data.get() + DATA_TIMESTAMP_POS);
|
uint32_t* ts_field = (uint32_t*) (_data + DATA_TIMESTAMP_POS);
|
||||||
*ts_field &= htonl(0x80000000UL);
|
*ts_field &= htonl(0x80000000UL);
|
||||||
*(uint32_t*)(_data.get() + DATA_SEQNO_POS) = htonl(next_seqno());
|
*(uint32_t*)(_data + DATA_SEQNO_POS) = htonl(next_seqno());
|
||||||
*ts_field |=
|
*ts_field |= htonl(to_us_timestamp(current_us_timestamp()));
|
||||||
htonl(to_us_timestamp(
|
|
||||||
std::chrono::time_point_cast<std::chrono::microseconds>(
|
|
||||||
std::chrono::steady_clock::now()
|
|
||||||
).time_since_epoch().count())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VpnPacket::upon_reception() {
|
void VpnPacket::upon_reception() {
|
||||||
_reception_timestamp = to_us_timestamp(
|
_reception_timestamp = to_us_timestamp(current_us_timestamp());
|
||||||
std::chrono::time_point_cast<std::chrono::microseconds>(
|
|
||||||
std::chrono::steady_clock::now()
|
|
||||||
).time_since_epoch().count());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t VpnPacket::next_seqno() {
|
uint32_t VpnPacket::next_seqno() {
|
||||||
if(!_peer)
|
if(!_peer) {
|
||||||
throw PeerNotSet();
|
fprintf(stderr, "ERROR: trying to get seqno without peer.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
return _peer->next_seqno();
|
return _peer->next_seqno();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
|
|
||||||
/** A packet to be transmitted or received over the VPN socket */
|
/** A packet to be transmitted or received over the VPN socket */
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <stdlib.h>
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "ip_header.hpp"
|
#include "ip_header.hpp"
|
||||||
|
|
||||||
|
@ -50,7 +49,6 @@ class VpnPacketTLV;
|
||||||
class VpnPacket {
|
class VpnPacket {
|
||||||
public:
|
public:
|
||||||
static const size_t VPN_HEADER_BYTES;
|
static const size_t VPN_HEADER_BYTES;
|
||||||
class PeerNotSet: public std::exception {};
|
|
||||||
|
|
||||||
VpnPacket(size_t mtu, bool inbound);
|
VpnPacket(size_t mtu, bool inbound);
|
||||||
~VpnPacket();
|
~VpnPacket();
|
||||||
|
@ -74,9 +72,9 @@ class VpnPacket {
|
||||||
|
|
||||||
/// Get a pointer to the packet payload (const version)
|
/// Get a pointer to the packet payload (const version)
|
||||||
const char* get_payload() const {
|
const char* get_payload() const {
|
||||||
return _data.get() + VPN_HEADER_BYTES; }
|
return _data + VPN_HEADER_BYTES; }
|
||||||
/// Get a pointer to the packet payload
|
/// Get a pointer to the packet payload
|
||||||
char* get_payload() { return _data.get() + VPN_HEADER_BYTES; }
|
char* get_payload() { return _data + 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
|
||||||
|
@ -94,9 +92,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.get(); }
|
const char* get_data() const { return _data; }
|
||||||
/// Get a pointer to the full packet data
|
/// Get a pointer to the full packet data
|
||||||
char* get_data() { return _data.get(); }
|
char* get_data() { return _data; }
|
||||||
/// 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
|
||||||
|
@ -134,7 +132,7 @@ class VpnPacket {
|
||||||
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?
|
bool _inbound; ///< is the packet received or sent?
|
||||||
|
|
||||||
std::unique_ptr<char[]> _data;
|
char* _data;
|
||||||
size_t _data_space, _data_size;
|
size_t _data_space, _data_size;
|
||||||
|
|
||||||
uint32_t _reception_timestamp;
|
uint32_t _reception_timestamp;
|
||||||
|
|
66
VpnPeer.cpp
66
VpnPeer.cpp
|
@ -1,9 +1,9 @@
|
||||||
#include "UdpVpn.hpp"
|
#include "UdpVpn.hpp"
|
||||||
#include "congestion_control.hpp"
|
#include "congestion_control.hpp"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <stdio.h>
|
||||||
#include <cstring>
|
#include <stdint.h>
|
||||||
#include <functional>
|
#include <string.h>
|
||||||
|
|
||||||
const double RTTLogger::EXP_AVG_FACTOR = 0.75;
|
const double RTTLogger::EXP_AVG_FACTOR = 0.75;
|
||||||
const unsigned int RTTLogger::BASE_UPDATE_DELAY = 1000; // ms
|
const unsigned int RTTLogger::BASE_UPDATE_DELAY = 1000; // ms
|
||||||
|
@ -12,12 +12,18 @@ VpnPeer::VpnPeer(UdpVpn* vpn, const sockaddr_in6& ext_addr,
|
||||||
const in6_addr& int_addr)
|
const in6_addr& int_addr)
|
||||||
: _vpn(vpn), _ext_addr(ext_addr), _int_addr(int_addr), _next_send_seqno(0),
|
: _vpn(vpn), _ext_addr(ext_addr), _int_addr(int_addr), _next_send_seqno(0),
|
||||||
_tot_bytes_sent(0), _prev_tot_bytes_sent(0),
|
_tot_bytes_sent(0), _prev_tot_bytes_sent(0),
|
||||||
_congestion_controller(*this)
|
_congestion_controller(*this),
|
||||||
|
_next_control_packet(nullptr)
|
||||||
{
|
{
|
||||||
_prev_tick_time = std::chrono::steady_clock::now();
|
clock_gettime(CLOCK_MONOTONIC, &_prev_tick_time);
|
||||||
cycle_next_control();
|
cycle_next_control();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VpnPeer::~VpnPeer() {
|
||||||
|
if(_next_control_packet != nullptr)
|
||||||
|
delete _next_control_packet;
|
||||||
|
}
|
||||||
|
|
||||||
void VpnPeer::make_loss_report() {
|
void VpnPeer::make_loss_report() {
|
||||||
VpnTlvLossReport report = VpnTlvLossReport::create(*_next_control_packet);
|
VpnTlvLossReport report = VpnTlvLossReport::create(*_next_control_packet);
|
||||||
report.set_report_seqno(_packet_loss.get_cur_seqno());
|
report.set_report_seqno(_packet_loss.get_cur_seqno());
|
||||||
|
@ -25,8 +31,9 @@ void VpnPeer::make_loss_report() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void VpnPeer::cycle_next_control() {
|
void VpnPeer::cycle_next_control() {
|
||||||
_next_control_packet =
|
if(_next_control_packet != nullptr)
|
||||||
std::make_unique<VpnControlPacket>(_vpn->get_mtu(), false);
|
delete _next_control_packet;
|
||||||
|
_next_control_packet = new VpnControlPacket(_vpn->get_mtu(), false);
|
||||||
_next_control_packet->set_peer(this);
|
_next_control_packet->set_peer(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,13 +42,13 @@ void VpnPeer::set_int_addr(const in6_addr& int_addr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void VpnPeer::tick() {
|
void VpnPeer::tick() {
|
||||||
auto cur_time = std::chrono::steady_clock::now();
|
struct timespec cur_time;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &cur_time);
|
||||||
|
|
||||||
// Compute byte rate
|
// Compute byte rate
|
||||||
_outbound_byte_rate = /* byte per second */
|
_outbound_byte_rate = /* byte per second */
|
||||||
(double)(_tot_bytes_sent - _prev_tot_bytes_sent)
|
(double)(_tot_bytes_sent - _prev_tot_bytes_sent)
|
||||||
/ (double)(std::chrono::duration_cast<std::chrono::microseconds>(
|
/ timespec_us_diff(cur_time, _prev_tick_time)
|
||||||
cur_time - _prev_tick_time).count())
|
|
||||||
* 1e6;
|
* 1e6;
|
||||||
|
|
||||||
_prev_tot_bytes_sent = _tot_bytes_sent;
|
_prev_tot_bytes_sent = _tot_bytes_sent;
|
||||||
|
@ -63,8 +70,10 @@ size_t VpnPeer::write(const char* data, size_t len) {
|
||||||
|
|
||||||
nsent = sendto(_vpn->get_socket_fd(), data, len, MSG_CONFIRM,
|
nsent = sendto(_vpn->get_socket_fd(), data, len, MSG_CONFIRM,
|
||||||
(const struct sockaddr*) &_ext_addr, sizeof(_ext_addr));
|
(const struct sockaddr*) &_ext_addr, sizeof(_ext_addr));
|
||||||
if(nsent < 0)
|
if(nsent < 0) {
|
||||||
throw NetError("Could not send UDP packet", errno, true);
|
perror("Could not send UDP packet: ");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
_tot_bytes_sent += nsent;
|
_tot_bytes_sent += nsent;
|
||||||
|
|
||||||
|
@ -110,27 +119,32 @@ void PacketLossLogger::log_packet(uint32_t seqno) {
|
||||||
if(diff == 1) {
|
if(diff == 1) {
|
||||||
_cur_seqno++;
|
_cur_seqno++;
|
||||||
maybe_start_window();
|
maybe_start_window();
|
||||||
while(_received_ahead.test((_cur_seqno + 1) % PACKET_LOST_AFTER)) {
|
while(_received_ahead & (1 << (_cur_seqno + 1 % PACKET_LOST_AFTER))) {
|
||||||
_cur_seqno++;
|
_cur_seqno++;
|
||||||
_received_ahead.reset(_cur_seqno % PACKET_LOST_AFTER);
|
_received_ahead &= ~(1 << (_cur_seqno + 1 % PACKET_LOST_AFTER));
|
||||||
maybe_start_window();
|
maybe_start_window();
|
||||||
}
|
}
|
||||||
} else if(LIKELY(diff > 1)) {
|
} else if(LIKELY(diff > 1)) {
|
||||||
if(diff < PACKET_LOST_AFTER)
|
if(diff < PACKET_LOST_AFTER)
|
||||||
_received_ahead.set(seqno % PACKET_LOST_AFTER);
|
_received_ahead |= (1 << (seqno % PACKET_LOST_AFTER));
|
||||||
else if(diff < PACKET_LOSS_WINDOW) {
|
else if(diff < PACKET_LOSS_WINDOW) {
|
||||||
// Packet too much forwards -- consider _cur_seqno lost
|
// Packet too much forwards -- consider _cur_seqno lost
|
||||||
if(_cur_seqno % PACKET_LOSS_WINDOW > seqno % PACKET_LOSS_WINDOW) {
|
if(_cur_seqno % PACKET_LOSS_WINDOW > seqno % PACKET_LOSS_WINDOW) {
|
||||||
// This loss crosses a window border
|
// This loss crosses a window border
|
||||||
for(int offs=0; offs < PACKET_LOST_AFTER; ++offs) {
|
for(int offs=0; offs < PACKET_LOST_AFTER; ++offs) {
|
||||||
maybe_start_window(offs);
|
maybe_start_window(offs);
|
||||||
if(!_received_ahead[(_cur_seqno + offs) % PACKET_LOST_AFTER])
|
if((_received_ahead & (1 << ((_cur_seqno + offs) % PACKET_LOST_AFTER))) == 0)
|
||||||
_tot_losses++;
|
_tot_losses++;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_tot_losses += PACKET_LOST_AFTER - _received_ahead.count();
|
_tot_losses += PACKET_LOST_AFTER;
|
||||||
|
while(_received_ahead > 0) {
|
||||||
|
if(_received_ahead & 1)
|
||||||
|
_tot_losses--;
|
||||||
|
_received_ahead >>= 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_received_ahead.reset();
|
_received_ahead = 0;
|
||||||
_cur_seqno = seqno;
|
_cur_seqno = seqno;
|
||||||
} else
|
} else
|
||||||
reboot(); // This is a huge gap -- reboot
|
reboot(); // This is a huge gap -- reboot
|
||||||
|
@ -142,7 +156,7 @@ void PacketLossLogger::log_packet(uint32_t seqno) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PacketLossLogger::reboot() {
|
void PacketLossLogger::reboot() {
|
||||||
_received_ahead.reset();
|
_received_ahead = 0;
|
||||||
// _tot_losses unchanged
|
// _tot_losses unchanged
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,12 +167,9 @@ void PacketLossLogger::maybe_start_window(int offs) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RTTLogger::RTTLogger() :
|
RTTLogger::RTTLogger() : _avg_rtt(0), _cur_rtt(0) {
|
||||||
_avg_rtt(0), _cur_rtt(0),
|
clock_gettime(CLOCK_MONOTONIC, &_last_update);
|
||||||
_last_update(std::chrono::steady_clock::time_point::min())
|
_last_update.tv_sec--;
|
||||||
{
|
|
||||||
_last_update =
|
|
||||||
std::chrono::steady_clock::now() - std::chrono::seconds(1);
|
|
||||||
// This avoids situations where both peers try to renew their RTT at the
|
// This avoids situations where both peers try to renew their RTT at the
|
||||||
// same time.
|
// same time.
|
||||||
_update_delay = BASE_UPDATE_DELAY + rand() % 100 - 50;
|
_update_delay = BASE_UPDATE_DELAY + rand() % 100 - 50;
|
||||||
|
@ -177,12 +188,11 @@ void RTTLogger::log(const VpnTlvRTTA& rtt_answer) {
|
||||||
_avg_rtt = (1 - EXP_AVG_FACTOR) * _avg_rtt
|
_avg_rtt = (1 - EXP_AVG_FACTOR) * _avg_rtt
|
||||||
+ EXP_AVG_FACTOR * _cur_rtt;
|
+ EXP_AVG_FACTOR * _cur_rtt;
|
||||||
|
|
||||||
_last_update = std::chrono::steady_clock::now();
|
clock_gettime(CLOCK_MONOTONIC, &_last_update);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RTTLogger::update_due(bool soon) const {
|
bool RTTLogger::update_due(bool soon) const {
|
||||||
unsigned int ms_since_last_update =
|
unsigned int ms_since_last_update =
|
||||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
timespec_us_ellapsed(_last_update) / 1000;
|
||||||
std::chrono::steady_clock::now() - _last_update).count();
|
|
||||||
return ms_since_last_update >= (soon ? _update_delay/2 : _update_delay);
|
return ms_since_last_update >= (soon ? _update_delay/2 : _update_delay);
|
||||||
}
|
}
|
||||||
|
|
26
VpnPeer.hpp
26
VpnPeer.hpp
|
@ -3,8 +3,7 @@
|
||||||
/** A peer of a bound (server) instance of UdpVpn */
|
/** A peer of a bound (server) instance of UdpVpn */
|
||||||
|
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <bitset>
|
#include <time.h>
|
||||||
#include <chrono>
|
|
||||||
#include "util.hpp"
|
#include "util.hpp"
|
||||||
#include "VpnPacket.hpp"
|
#include "VpnPacket.hpp"
|
||||||
#include "congestion_control.hpp"
|
#include "congestion_control.hpp"
|
||||||
|
@ -22,7 +21,7 @@ class PacketLossLogger {
|
||||||
/ (double)PACKET_LOSS_WINDOW;
|
/ (double)PACKET_LOSS_WINDOW;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::bitset<PACKET_LOST_AFTER> get_received_ahead() const {
|
uint8_t get_received_ahead() const {
|
||||||
return _received_ahead;
|
return _received_ahead;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +34,7 @@ class PacketLossLogger {
|
||||||
/// roll loss window values if `_cur_seqno + offs` is a window start.
|
/// roll loss window values if `_cur_seqno + offs` is a window start.
|
||||||
void maybe_start_window(int offs=0);
|
void maybe_start_window(int offs=0);
|
||||||
|
|
||||||
std::bitset<PACKET_LOST_AFTER> _received_ahead;
|
uint8_t _received_ahead; // Warning: must have >= bits than PACKET_LOSS_AFTER
|
||||||
uint32_t _cur_seqno;
|
uint32_t _cur_seqno;
|
||||||
unsigned int _tot_losses;
|
unsigned int _tot_losses;
|
||||||
unsigned int _last_window_losses, _win_start_losses;
|
unsigned int _last_window_losses, _win_start_losses;
|
||||||
|
@ -47,7 +46,7 @@ class RTTLogger {
|
||||||
RTTLogger();
|
RTTLogger();
|
||||||
uint32_t avg_rtt() const { return _avg_rtt; }
|
uint32_t avg_rtt() const { return _avg_rtt; }
|
||||||
uint32_t cur_rtt() const { return _cur_rtt; }
|
uint32_t cur_rtt() const { return _cur_rtt; }
|
||||||
std::chrono::steady_clock::time_point get_last_update() const {
|
struct timespec get_last_update() const {
|
||||||
return _last_update;
|
return _last_update;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,20 +63,11 @@ class RTTLogger {
|
||||||
|
|
||||||
unsigned int _update_delay; // in ms
|
unsigned int _update_delay; // in ms
|
||||||
static const unsigned int BASE_UPDATE_DELAY; // in ms
|
static const unsigned int BASE_UPDATE_DELAY; // in ms
|
||||||
std::chrono::steady_clock::time_point _last_update;
|
struct timespec _last_update;
|
||||||
};
|
};
|
||||||
|
|
||||||
class VpnPeer {
|
class VpnPeer {
|
||||||
public:
|
public:
|
||||||
class NetError : public MsgException {
|
|
||||||
public:
|
|
||||||
NetError(
|
|
||||||
const std::string& msg,
|
|
||||||
int code=0,
|
|
||||||
bool is_perror=false)
|
|
||||||
: MsgException(msg, code, is_perror) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Logs the loss reports sent by the remote peer
|
/// Logs the loss reports sent by the remote peer
|
||||||
struct LossReports {
|
struct LossReports {
|
||||||
uint32_t prev_seqno, last_seqno;
|
uint32_t prev_seqno, last_seqno;
|
||||||
|
@ -92,6 +82,8 @@ class VpnPeer {
|
||||||
VpnPeer(UdpVpn* vpn, const sockaddr_in6& ext_addr,
|
VpnPeer(UdpVpn* vpn, const sockaddr_in6& ext_addr,
|
||||||
const in6_addr& int_addr);
|
const in6_addr& int_addr);
|
||||||
|
|
||||||
|
~VpnPeer();
|
||||||
|
|
||||||
const sockaddr_in6& get_ext_addr() const { return _ext_addr; }
|
const sockaddr_in6& get_ext_addr() const { return _ext_addr; }
|
||||||
const in6_addr& get_int_addr() const { return _int_addr; }
|
const in6_addr& get_int_addr() const { return _int_addr; }
|
||||||
|
|
||||||
|
@ -139,12 +131,12 @@ class VpnPeer {
|
||||||
uint64_t _tot_bytes_sent, _prev_tot_bytes_sent;
|
uint64_t _tot_bytes_sent, _prev_tot_bytes_sent;
|
||||||
double _outbound_byte_rate;
|
double _outbound_byte_rate;
|
||||||
|
|
||||||
std::chrono::steady_clock::time_point _prev_tick_time;
|
struct timespec _prev_tick_time;
|
||||||
|
|
||||||
PacketLossLogger _packet_loss;
|
PacketLossLogger _packet_loss;
|
||||||
LossReports _loss_reports;
|
LossReports _loss_reports;
|
||||||
RTTLogger _rtt;
|
RTTLogger _rtt;
|
||||||
CongestionController _congestion_controller;
|
CongestionController _congestion_controller;
|
||||||
|
|
||||||
std::unique_ptr<VpnControlPacket> _next_control_packet;
|
VpnControlPacket* _next_control_packet;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "congestion_control.hpp"
|
#include "congestion_control.hpp"
|
||||||
#include "VpnPeer.hpp"
|
#include "VpnPeer.hpp"
|
||||||
|
#include "util.hpp"
|
||||||
|
|
||||||
CongestionController::CongestionController(const VpnPeer& peer):
|
CongestionController::CongestionController(const VpnPeer& peer):
|
||||||
_peer(peer)
|
_peer(peer)
|
||||||
|
@ -7,8 +8,8 @@ CongestionController::CongestionController(const VpnPeer& peer):
|
||||||
_last_seqno = _peer.get_loss_logger().get_cur_seqno();
|
_last_seqno = _peer.get_loss_logger().get_cur_seqno();
|
||||||
_loss_based.bandwidth = 3e5; // 300kBps seems a good value to start with
|
_loss_based.bandwidth = 3e5; // 300kBps seems a good value to start with
|
||||||
_loss_based.prev_tot_sent = _peer.get_tot_bytes_sent();
|
_loss_based.prev_tot_sent = _peer.get_tot_bytes_sent();
|
||||||
_last_bucket_update = _loss_based.prev_time =
|
clock_gettime(CLOCK_MONOTONIC, &_last_bucket_update);
|
||||||
std::chrono::steady_clock::now();
|
clock_gettime(CLOCK_MONOTONIC, &_loss_based.prev_time);
|
||||||
update_params();
|
update_params();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,12 +21,14 @@ void CongestionController::update_lossbased() {
|
||||||
|
|
||||||
double loss_rate = (double)delta_losses / (double)delta_seqno;
|
double loss_rate = (double)delta_losses / (double)delta_seqno;
|
||||||
|
|
||||||
auto cur_time = std::chrono::steady_clock::now();
|
struct timespec cur_time;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &cur_time);
|
||||||
|
|
||||||
uint64_t cur_sent = _peer.get_tot_bytes_sent();
|
uint64_t cur_sent = _peer.get_tot_bytes_sent();
|
||||||
|
|
||||||
double instant_bandwidth = /* byte per second */
|
double instant_bandwidth = /* byte per second */
|
||||||
(double)(cur_sent - _loss_based.prev_tot_sent)
|
(double)(cur_sent - _loss_based.prev_tot_sent)
|
||||||
/ (double)(std::chrono::duration_cast<std::chrono::microseconds>(
|
/ (double)(timespec_us_diff(cur_time, _loss_based.prev_time))
|
||||||
cur_time - _loss_based.prev_time).count())
|
|
||||||
* 1e6;
|
* 1e6;
|
||||||
|
|
||||||
_loss_based.prev_time = cur_time;
|
_loss_based.prev_time = cur_time;
|
||||||
|
@ -44,5 +47,6 @@ void CongestionController::update_lossbased() {
|
||||||
void CongestionController::update_params() {
|
void CongestionController::update_params() {
|
||||||
_bandwidth = _loss_based.bandwidth; // meant to integrate other controllers
|
_bandwidth = _loss_based.bandwidth; // meant to integrate other controllers
|
||||||
_bucket_max_level = _bandwidth * _peer.get_rtt().avg_rtt();
|
_bucket_max_level = _bandwidth * _peer.get_rtt().avg_rtt();
|
||||||
_bucket_level = std::min(_bucket_level, _bucket_max_level);
|
if(_bucket_level > _bucket_max_level)
|
||||||
|
_bucket_level = _bucket_max_level;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <chrono>
|
#include <time.h>
|
||||||
|
|
||||||
class VpnPeer;
|
class VpnPeer;
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ class CongestionController {
|
||||||
struct LossBased {
|
struct LossBased {
|
||||||
uint64_t bandwidth; // bytes per second
|
uint64_t bandwidth; // bytes per second
|
||||||
uint64_t prev_tot_sent; // tot bytes sent during last update
|
uint64_t prev_tot_sent; // tot bytes sent during last update
|
||||||
std::chrono::steady_clock::time_point prev_time; // last update time
|
struct timespec prev_time; // last update time
|
||||||
};
|
};
|
||||||
|
|
||||||
CongestionController(const VpnPeer& peer);
|
CongestionController(const VpnPeer& peer);
|
||||||
|
@ -34,6 +34,6 @@ class CongestionController {
|
||||||
|
|
||||||
uint64_t _bucket_level, ///< implements a leaky bucket
|
uint64_t _bucket_level, ///< implements a leaky bucket
|
||||||
_bucket_max_level; // bandwidth * RTT
|
_bucket_max_level; // bandwidth * RTT
|
||||||
std::chrono::steady_clock::time_point _last_bucket_update;
|
struct timespec _last_bucket_update;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include <cstring>
|
#include <string.h>
|
||||||
|
|
||||||
#include "ip_header.hpp"
|
#include "ip_header.hpp"
|
||||||
#include "util.hpp"
|
#include "util.hpp"
|
||||||
|
|
73
main.cpp
73
main.cpp
|
@ -1,5 +1,5 @@
|
||||||
#include <cstdio>
|
#include <stdio.h>
|
||||||
#include <cstdlib>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
@ -100,52 +100,41 @@ int main(int argc, char** argv) {
|
||||||
signal(SIGINT, stop_sig_handler);
|
signal(SIGINT, stop_sig_handler);
|
||||||
signal(SIGUSR1, dump_sig_handler);
|
signal(SIGUSR1, dump_sig_handler);
|
||||||
|
|
||||||
try {
|
if(program_options.listen && program_options.has_peer) {
|
||||||
if(program_options.listen && program_options.has_peer) {
|
fprintf(stderr,
|
||||||
|
"ERROR: Cannot be a server and a client at the same time "
|
||||||
|
"-- provide either -l or -s.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if(program_options.listen) {
|
||||||
|
vpn_instance = new UdpVpnServer(
|
||||||
|
program_options.bind_addr, program_options.bind_port);
|
||||||
|
} else if(program_options.has_peer) {
|
||||||
|
if(program_options.server_port == 0) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"ERROR: Cannot be a server and a client at the same time "
|
"ERROR: A client instance must be given a server port "
|
||||||
"-- provide either -l or -s.\n");
|
"-- please provide -p.\n");
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if(program_options.listen) {
|
|
||||||
vpn_instance = new UdpVpnServer(
|
|
||||||
program_options.bind_addr, program_options.bind_port);
|
|
||||||
} else if(program_options.has_peer) {
|
|
||||||
if(program_options.server_port == 0) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"ERROR: A client instance must be given a server port "
|
|
||||||
"-- please provide -p.\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sockaddr_in6 server_addr;
|
|
||||||
server_addr.sin6_family = AF_INET6; memcpy(&server_addr.sin6_addr, &program_options.server_addr,
|
|
||||||
sizeof(server_addr.sin6_addr));
|
|
||||||
server_addr.sin6_port = htons(program_options.server_port);
|
|
||||||
|
|
||||||
vpn_instance = new UdpVpnClient(server_addr);
|
|
||||||
} else {
|
|
||||||
fprintf(stderr,
|
|
||||||
"ERROR: Must be either a server or a client "
|
|
||||||
"-- provide either -l or -s.\n");
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Starting to listen...\n");
|
struct sockaddr_in6 server_addr;
|
||||||
vpn_instance->run();
|
server_addr.sin6_family = AF_INET6; memcpy(&server_addr.sin6_addr, &program_options.server_addr,
|
||||||
|
sizeof(server_addr.sin6_addr));
|
||||||
|
server_addr.sin6_port = htons(program_options.server_port);
|
||||||
|
|
||||||
delete vpn_instance;
|
vpn_instance = new UdpVpnClient(server_addr);
|
||||||
|
} else {
|
||||||
printf("Shutting down.\n");
|
fprintf(stderr,
|
||||||
} catch(const TunDevice::InitializationError& exn) {
|
"ERROR: Must be either a server or a client "
|
||||||
fprintf(stderr, "TUN INIT ERROR: %s\n", exn.what());
|
"-- provide either -l or -s.\n");
|
||||||
} catch(const TunDevice::NetError& exn) {
|
return 1;
|
||||||
fprintf(stderr, "TUN NET ERROR: %s\n", exn.what());
|
|
||||||
} catch(const UdpVpn::InitializationError& exn) {
|
|
||||||
fprintf(stderr, "VPN INIT ERROR: %s\n", exn.what());
|
|
||||||
} catch(const UdpVpn::NetError& exn) {
|
|
||||||
fprintf(stderr, "VPN NET ERROR: %s\n", exn.what());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printf("Starting to listen...\n");
|
||||||
|
vpn_instance->run();
|
||||||
|
|
||||||
|
delete vpn_instance;
|
||||||
|
|
||||||
|
printf("Shutting down.\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
70
util.cpp
70
util.cpp
|
@ -1,7 +1,8 @@
|
||||||
#include <cstdio>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
#include "util.hpp"
|
#include "util.hpp"
|
||||||
|
|
||||||
|
@ -53,40 +54,39 @@ human_readable_unit(double value, const char* unit) {
|
||||||
return buf[cur_buf];
|
return buf[cur_buf];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
namespace std {
|
timespec_diff(const struct timespec t1, const struct timespec t2,
|
||||||
size_t hash<in6_addr>::operator() (const in6_addr& addr) const {
|
struct timespec* result)
|
||||||
size_t out_hash = 0;
|
|
||||||
for(int i=0; i < 4; ++i) {
|
|
||||||
uint32_t value;
|
|
||||||
memcpy((unsigned char*)(&value),
|
|
||||||
addr.s6_addr + 4*i,
|
|
||||||
4);
|
|
||||||
out_hash ^= (std::hash<uint32_t>{}(value) << 1);
|
|
||||||
}
|
|
||||||
return out_hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool equal_to<in6_addr>::operator()(
|
|
||||||
const in6_addr& lhs, const in6_addr& rhs) const
|
|
||||||
{
|
{
|
||||||
return memcmp(lhs.s6_addr, rhs.s6_addr, sizeof(lhs.s6_addr)) == 0;
|
result->tv_nsec = t1.tv_nsec - t2.tv_nsec;
|
||||||
}
|
result->tv_sec = t1.tv_sec - t2.tv_sec;
|
||||||
}
|
if(result->tv_nsec < 0) {
|
||||||
|
result->tv_nsec += 1000*1000*1000;
|
||||||
MsgException::MsgException(const std::string& msg, int code, bool is_perror)
|
result->tv_sec--;
|
||||||
: _msg(msg), _code(code)
|
|
||||||
{
|
|
||||||
_what = _msg;
|
|
||||||
|
|
||||||
if(_code != 0) {
|
|
||||||
if(is_perror) {
|
|
||||||
_what += ": ";
|
|
||||||
_what += strerror(errno);
|
|
||||||
}
|
|
||||||
|
|
||||||
char remainder[20];
|
|
||||||
sprintf(remainder, " (code %d)", _code);
|
|
||||||
_what += remainder;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t timespec_us_diff(const struct timespec t1, const struct timespec t2) {
|
||||||
|
long nsec_diff = t1.tv_nsec - t2.tv_nsec;
|
||||||
|
time_t sec_diff = t1.tv_sec - t2.tv_sec;
|
||||||
|
if(nsec_diff < 0) {
|
||||||
|
nsec_diff += 1000*1000*1000;
|
||||||
|
sec_diff--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(sec_diff > MAX_USEC_DIFF)
|
||||||
|
sec_diff = MAX_USEC_DIFF;
|
||||||
|
return (nsec_diff / 1000) + (sec_diff * 1000*1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t timespec_us_ellapsed(const struct timespec ref) {
|
||||||
|
struct timespec now;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
return timespec_us_diff(now, ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t current_us_timestamp() {
|
||||||
|
struct timespec now;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
return (now.tv_sec * 1000*1000) + (now.tv_nsec / 1000);
|
||||||
|
}
|
||||||
|
|
48
util.hpp
48
util.hpp
|
@ -1,7 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <exception>
|
|
||||||
#include <string>
|
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
|
||||||
/* Debugging -- taken from babeld */
|
/* Debugging -- taken from babeld */
|
||||||
|
@ -58,36 +56,24 @@ inline uint32_t to_us_timestamp(uint32_t clock_output) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** in6_addr hash & equality */
|
/** Max seconds that can be be represented in a microseconds timestamp, on 31
|
||||||
namespace std {
|
* bits. */
|
||||||
template<>
|
const uint32_t MAX_USEC_DIFF = ((1U<<31) - 1) / (1000*1000) - 1;
|
||||||
class hash<in6_addr> {
|
|
||||||
public:
|
|
||||||
size_t operator()(const in6_addr& addr) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<>
|
|
||||||
class equal_to<in6_addr> {
|
|
||||||
public:
|
|
||||||
bool operator()(const in6_addr& lhs, const in6_addr& rhs) const;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
/** Get a `struct timespec` difference, ie `t1 - t2`, as a new `struct
|
||||||
|
* timespec`. Assumes `t1 >= t2`. */
|
||||||
|
void timespec_diff(
|
||||||
|
const struct timespec t1, const struct timespec t2,
|
||||||
|
struct timespec* result);
|
||||||
|
|
||||||
/** MsgException -- an exception bearing a passed explanation message
|
/** Get a `struct timespec` difference, ie `t1 - t2`, expressed in
|
||||||
*
|
* microseconds. Assumes `t1 >= t2`. Capped to MAX_USEC_DIFF seconds. */
|
||||||
* If `is_perror` is true, then the `strerror` corresponding message is appened
|
uint32_t timespec_us_diff(const struct timespec t1, const struct timespec t2);
|
||||||
* to the message in `what()`.
|
|
||||||
*/
|
|
||||||
class MsgException : public std::exception {
|
|
||||||
public:
|
|
||||||
MsgException(const std::string& msg, int code=0, bool is_perror=false);
|
|
||||||
int errcode() const noexcept { return _code; }
|
|
||||||
const char* what() const noexcept { return _what.c_str(); };
|
|
||||||
|
|
||||||
private:
|
/** Get the number of microseconds ellapsed since a `struct timespec`, based on
|
||||||
std::string _msg;
|
* the monotonic clock */
|
||||||
int _code;
|
uint32_t timespec_us_ellapsed(const struct timespec ref);
|
||||||
std::string _what;
|
|
||||||
};
|
/** Get the current timestamp, in microseconds */
|
||||||
|
uint32_t current_us_timestamp();
|
||||||
|
|
Loading…
Reference in a new issue