#pragma once /** A peer of a bound (server) instance of UdpVpn */ #include #include #include #include "util.hpp" #include "VpnPacket.hpp" #include "congestion_control.hpp" class UdpVpn; const int PACKET_LOSS_WINDOW = 128, PACKET_LOST_AFTER = 8; class PacketLossLogger { public: PacketLossLogger(); void log_packet(uint32_t seqno); double get_loss_rate() const { return (double)(_win_start_losses - _last_window_losses) / (double)PACKET_LOSS_WINDOW; } const std::bitset get_received_ahead() const { return _received_ahead; } uint32_t get_cur_seqno() const { return _cur_seqno; } unsigned int get_tot_losses() const { return _tot_losses; } private: void reboot(); ///< completely reset the internal state /// roll loss window values if `_cur_seqno + offs` is a window start. void maybe_start_window(int offs=0); std::bitset _received_ahead; uint32_t _cur_seqno; unsigned int _tot_losses; unsigned int _last_window_losses, _win_start_losses; }; /** Round-trip time logger. All timestamps/delays are in microseconds. */ class RTTLogger { public: RTTLogger(); uint32_t avg_rtt() const { return _avg_rtt; } uint32_t cur_rtt() const { return _cur_rtt; } std::chrono::steady_clock::time_point get_last_update() const { return _last_update; } /** Checks whether an update is due. * If soon is true, divides the inter-update delay by 2. */ bool update_due(bool soon=false) const; void log(const VpnTlvRTTA& rtt_answer); private: uint32_t _avg_rtt, _cur_rtt; static const double EXP_AVG_FACTOR; unsigned int _update_delay; // in ms static const unsigned int BASE_UPDATE_DELAY; // in ms std::chrono::steady_clock::time_point _last_update; }; class VpnPeer { 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 struct LossReports { uint32_t prev_seqno, last_seqno; uint32_t prev_losses, last_losses; double loss_rate() const { return (double)(last_losses - prev_losses) / (double)(last_seqno - prev_seqno); } }; VpnPeer(UdpVpn* vpn, const sockaddr_in6& ext_addr, const in6_addr& int_addr); const sockaddr_in6& get_ext_addr() const { return _ext_addr; } const in6_addr& get_int_addr() const { return _int_addr; } void set_int_addr(const in6_addr& int_addr); const PacketLossLogger& get_loss_logger() const { return _packet_loss; } const RTTLogger& get_rtt() const { return _rtt; } const LossReports& get_loss_reports() const { return _loss_reports; } 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); 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++; } 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 === */ /// Send a control packet if there is data to be sent bool send_control_packet(); /// Append a RTTA for the given RTTQ to the next control packet void make_rtta_for(const VpnTlvRTTQ& rttq); private: // meth void make_loss_report(); ///< Add a loss report to the next control packet void cycle_next_control(); ///< Generate a fresh next control packet private: UdpVpn* _vpn; sockaddr_in6 _ext_addr; 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; CongestionController _congestion_controller; std::unique_ptr _next_control_packet; };