Measure real-time outbound throughput

This commit is contained in:
Théophile Bastian 2020-07-03 18:03:26 +02:00
parent 7b5ffa4d46
commit c23741bea3
8 changed files with 67 additions and 7 deletions

View File

@ -19,7 +19,7 @@ UdpVpn::UdpVpn()
: _stopped(false), _dump_requested(false), _vpn_mtu(VPN_MTU),
_tun_dev("cvpn%d"), _peer(nullptr)
{
_last_control_sent =
_last_tick = _last_control_sent =
std::chrono::steady_clock::now() - std::chrono::seconds(1);
_tun_dev.set_mtu(VpnPacket::get_tunnelled_mtu(_vpn_mtu));
_socket = socket(AF_INET6, SOCK_DGRAM, 0);
@ -52,7 +52,7 @@ void UdpVpn::run() {
_dump_requested = false;
}
rc = poll(poll_fds, nfds, 100); // timeout every 100ms
rc = poll(poll_fds, nfds, 10); // timeout every 10ms
if(rc < 0) {
if(errno == EINTR) // Interrupt.
@ -70,6 +70,13 @@ void UdpVpn::run() {
_last_control_sent = std::chrono::steady_clock::now();
}
}
if(std::chrono::steady_clock::now() - _last_tick
> std::chrono::milliseconds(50))
{
_last_tick = std::chrono::steady_clock::now();
if(_peer)
_peer->tick();
}
if(rc == 0) // Nothing to read -- timeout
continue;
@ -257,6 +264,10 @@ void UdpVpn::dump_state() const {
std::chrono::steady_clock::now()
- _peer->get_rtt().get_last_update()).count()
);
printf("Total bytes transmitted: %s\n",
human_readable_unit(_peer->get_tot_bytes_sent(), "B"));
printf("Current outbound byte rate: %s\n",
human_readable_unit(_peer->get_outbound_byte_rate(), "Bps"));
printf("Available bandwidth:\n\t%s [loss based controller]\n",
human_readable_unit(congest_ctrl.get_lossbased_bandwidth(), "B")
);

View File

@ -73,5 +73,8 @@ class UdpVpn {
TunDevice _tun_dev;
std::unique_ptr<VpnPeer> _peer;
std::chrono::steady_clock::time_point _last_control_sent;
std::chrono::steady_clock::time_point
_last_control_sent, /**< A control is offered to be sent approx.
every 100ms */
_last_tick /**< A tick occurs approx. each 50ms */;
};

View File

@ -11,8 +11,10 @@ const unsigned int RTTLogger::BASE_UPDATE_DELAY = 1000; // ms
VpnPeer::VpnPeer(UdpVpn* vpn, const sockaddr_in6& ext_addr,
const in6_addr& int_addr)
: _vpn(vpn), _ext_addr(ext_addr), _int_addr(int_addr), _next_send_seqno(0),
_tot_bytes_sent(0), _prev_tot_bytes_sent(0),
_congestion_controller(*this)
{
_prev_tick_time = std::chrono::steady_clock::now();
cycle_next_control();
}
@ -32,6 +34,20 @@ void VpnPeer::set_int_addr(const in6_addr& int_addr) {
memcpy(&_int_addr, &int_addr, sizeof(_int_addr));
}
void VpnPeer::tick() {
auto cur_time = std::chrono::steady_clock::now();
// Compute byte rate
_outbound_byte_rate = /* byte per second */
(double)(_tot_bytes_sent - _prev_tot_bytes_sent)
/ (double)(std::chrono::duration_cast<std::chrono::microseconds>(
cur_time - _prev_tick_time).count())
* 1e6;
_prev_tot_bytes_sent = _tot_bytes_sent;
_prev_tick_time = cur_time;
}
void VpnPeer::log_loss_report(const VpnTlvLossReport& loss_rep) {
_loss_reports.prev_seqno = _loss_reports.last_seqno;
_loss_reports.prev_losses = _loss_reports.last_losses;
@ -50,6 +66,8 @@ size_t VpnPeer::write(const char* data, size_t len) {
if(nsent < 0)
throw NetError("Could not send UDP packet", errno, true);
_tot_bytes_sent += nsent;
return (size_t) nsent;
}

View File

@ -103,6 +103,8 @@ class VpnPeer {
const CongestionController& get_congestion_controller() const {
return _congestion_controller; }
void tick(); ///< periodic actions, triggered every UdpVpn tick
void log_rtta(const VpnTlvRTTA& rtta) { _rtt.log(rtta); }
void log_loss_report(const VpnTlvLossReport& loss_rep);
@ -112,6 +114,9 @@ class VpnPeer {
uint32_t peek_next_seqno() const { return _next_send_seqno; }
uint32_t next_seqno() { return _next_send_seqno++; }
uint64_t get_tot_bytes_sent() const { return _tot_bytes_sent; }
double get_outbound_byte_rate() const { return _outbound_byte_rate; }
void got_inbound_packet(const VpnPacket& packet);
/* === Control protocol === */
@ -131,6 +136,11 @@ class VpnPeer {
in6_addr _int_addr;
uint32_t _next_send_seqno;
uint64_t _tot_bytes_sent, _prev_tot_bytes_sent;
double _outbound_byte_rate;
std::chrono::steady_clock::time_point _prev_tick_time;
PacketLossLogger _packet_loss;
LossReports _loss_reports;
RTTLogger _rtt;

View File

@ -6,6 +6,8 @@ CongestionController::CongestionController(const VpnPeer& peer):
{
_last_seqno = _peer.get_loss_logger().get_cur_seqno();
_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_time = std::chrono::steady_clock::now();
}
void CongestionController::update_lossbased() {
@ -16,8 +18,21 @@ void CongestionController::update_lossbased() {
double loss_rate = (double)delta_losses / (double)delta_seqno;
if(loss_rate < 0.02) // FIXME only if the bandwidth is used
_loss_based.bandwidth *= 1.05;
auto cur_time = std::chrono::steady_clock::now();
uint64_t cur_sent = _peer.get_tot_bytes_sent();
double instant_bandwidth = /* byte per second */
(double)(cur_sent - _loss_based.prev_tot_sent)
/ (double)(std::chrono::duration_cast<std::chrono::microseconds>(
cur_time - _loss_based.prev_time).count())
* 1e6;
_loss_based.prev_time = cur_time;
_loss_based.prev_tot_sent = cur_sent;
if(loss_rate < 0.02) {
if(instant_bandwidth > 0.75 * _loss_based.bandwidth)
_loss_based.bandwidth *= 1.05;
}
else if(loss_rate >= 0.1)
_loss_based.bandwidth *= (1 - 0.5 * loss_rate);
}

View File

@ -1,5 +1,6 @@
#pragma once
#include <stdint.h>
#include <chrono>
class VpnPeer;
@ -7,6 +8,8 @@ class CongestionController {
public:
struct LossBased {
uint64_t bandwidth; // bytes per second
uint64_t prev_tot_sent; // tot bytes sent during last update
std::chrono::steady_clock::time_point prev_time; // last update time
};
CongestionController(const VpnPeer& peer);

View File

@ -34,7 +34,7 @@ format_address(const unsigned char *address)
}
const char*
human_readable_unit(size_t value, const char* unit) {
human_readable_unit(double value, const char* unit) {
static char buf[4][24];
static int cur_buf = 0;
static const char MULTIPLIERS[] = {' ', 'k', 'M', 'G', 'T'};

View File

@ -48,7 +48,7 @@ void do_debugf(int level, const char *format, ...);
const char* format_address(const unsigned char* address);
/** turns a value into a human-readable one, eg. "21.2 kB" */
const char* human_readable_unit(size_t value, const char* unit);
const char* human_readable_unit(double value, const char* unit);
/** remove the upper bit from a microsecond timestamp, to conform with the