diff --git a/tunsafe_win32.cpp b/tunsafe_win32.cpp index 02d21ec..b9bd86b 100644 --- a/tunsafe_win32.cpp +++ b/tunsafe_win32.cpp @@ -1503,6 +1503,7 @@ static const AdvancedTextInfo ADVANCED_TEXT_INFOS[] = { {Y + 19 * 3, 66, "Handshake:"}, {Y + 19 * 4, 66, ""}, {Y + 19 * 5, 66, "Overhead:"}, + {Y + 19 * 6, 66, "Packet Loss:"}, #undef Y }; @@ -1579,6 +1580,12 @@ static const char *GetAdvancedInfoValue(char buffer[256], int i) { overhead_out_pct / 1000, overhead_out_pct % 1000); return buffer; } + case 6: { + snprintf(buffer, 256, "%.3f%% (%d packets)", + ps->lost_packets_tot ? 100.0f * (ps->lost_packets_tot - ps->lost_packets_valid) / ps->lost_packets_tot : 0.0f, + (int)(ps->lost_packets_tot - ps->lost_packets_valid)); + return buffer; + } default: return ""; } } diff --git a/wireguard.cpp b/wireguard.cpp index c1b4d58..f52c9f6 100644 --- a/wireguard.cpp +++ b/wireguard.cpp @@ -95,6 +95,11 @@ const WgProcessorStats &WireguardProcessor::GetStats() { if (peer) { stats_.endpoint = peer->endpoint_; stats_.endpoint_protocol = peer->endpoint_protocol_; + + if (peer->curr_keypair_) { + stats_.lost_packets_tot = peer->curr_keypair_->replay_detector.expected_seq_nr(); + stats_.lost_packets_valid = peer->curr_keypair_->incoming_packet_count; + } } return stats_; } @@ -434,8 +439,8 @@ add_padding: byte *write = data; uint8 tag = WG_SHORT_HEADER_BIT, inner_tag; // For every 16 incoming packets, send out an ack. - if (keypair->incoming_packet_count >= 16) { - keypair->incoming_packet_count = 0; + if ((uint32)(keypair->incoming_packet_count - keypair->send_ack_ctr) >= 16) { + keypair->send_ack_ctr = (uint32)keypair->incoming_packet_count; uint64 next_expected_packet = keypair->replay_detector.expected_seq_nr(); if (next_expected_packet < 0x10000) { WriteLE16(write -= 2, (uint16)next_expected_packet); @@ -794,7 +799,6 @@ void WireguardProcessor::HandleShortHeaderFormatPacket(uint32 tag, Packet *packe stats_.compression_wg_saved_in += 16 - (data - packet->data); keypair->send_ctr_acked = std::max(keypair->send_ctr_acked, acked_counter); - keypair->incoming_packet_count++; WgPeer::CopyEndpointToPeer_Locked(keypair, &packet->addr); @@ -836,6 +840,9 @@ void WireguardProcessor::HandleAuthenticatedDataPacket_WillUnlock(WgKeypair *key WgPeer *peer = keypair->peer; assert(peer->IsPeerLocked()); + // Remember how many incoming packets we've seen so we can approximate loss + keypair->incoming_packet_count++; + // Promote the next key to the current key when we receive a data packet, // the handshake is now complete. if (peer->CheckSwitchToNextKey_Locked(keypair)) { diff --git a/wireguard.h b/wireguard.h index 69278ed..7919bd1 100644 --- a/wireguard.h +++ b/wireguard.h @@ -44,6 +44,12 @@ struct WgProcessorStats { // Address of the endpoint IpAddr endpoint; + + // For lost packets calculation, the total # of incoming packets + uint64 lost_packets_valid; + // For lost packets calculation, the total # of incoming packets according to seqnr + uint64 lost_packets_tot; + uint8 endpoint_protocol; }; diff --git a/wireguard_proto.h b/wireguard_proto.h index b1166be..3280285 100644 --- a/wireguard_proto.h +++ b/wireguard_proto.h @@ -704,18 +704,19 @@ struct WgKeypair { // Cipher suite uint8 cipher_suite; - - // Used so we know when to send out ack packets. - uint32 incoming_packet_count; // Id of the key in my map. (MainThread) uint32 local_key_id; // Id of the key in their map uint32 remote_key_id; + // Used to send out acks. + uint32 send_ack_ctr; // The timestamp of when the key was created, to be able to expire it uint64 key_timestamp; // The highest acked send_ctr value uint64 send_ctr_acked; + // Used to detect incoming packet loss + uint64 incoming_packet_count; // Counter value for chacha20 for outgoing packets uint64 send_ctr; // The key used for chacha20 encryption