Circuit data now includes TCP retransmits. Made the tables a little less unpleasant to look at.

This commit is contained in:
Herbert Wolverson 2024-03-19 14:13:04 -05:00
parent b01ab42a3b
commit a4c8093401
7 changed files with 55 additions and 7 deletions

View File

@ -24,6 +24,9 @@ pub struct IpStats {
/// Associated TC traffic control handle.
pub tc_handle: TcHandle,
/// TCP Retransmits for this host at the current time.
pub tcp_retransmits: (u64, u64),
}
/// Represents an IP Mapping in the XDP IP to TC/CPU mapping system.

View File

@ -22,6 +22,7 @@ pub struct IpStatsWithPlan {
pub tc_handle: TcHandle,
pub circuit_id: String,
pub plan: (u32, u32),
pub tcp_retransmits: (u64, u64),
}
impl From<&IpStats> for IpStatsWithPlan {
@ -34,6 +35,7 @@ impl From<&IpStats> for IpStatsWithPlan {
tc_handle: i.tc_handle,
circuit_id: i.circuit_id.clone(),
plan: (0, 0),
tcp_retransmits: i.tcp_retransmits,
};
if !result.circuit_id.is_empty() {

View File

@ -34,6 +34,7 @@ const IpStats = {
"tc_handle": 4,
"circuit_id": 5,
"plan": 6,
"tcp_retransmits": 7,
}
const FlowTrans = {

View File

@ -228,7 +228,7 @@
function updateSiteFunnel() {
msgPackGet("/api/network_tree_summary/", (data) => {
let table = "<table class='table' style='font-size: 8pt;'>";
let table = "<table class='table table-striped' style='font-size: 8pt;'>";
for (let i = 0; i < data.length; ++i) {
let id = data[i][0];
let name = data[i][1][NetTrans.name];
@ -280,11 +280,12 @@
}
function updateNTable(target, tt) {
let html = "<table class='table'>";
html += "<thead><th>IP Address</th><th>DL ⬇️</th><th>UL ⬆️</th><th>RTT (ms)</th><th>Shaped</th></thead>";
let html = "<table class='table table-striped' style='font-size: 8pt'>";
html += "<thead><th></th><th>IP Address</th><th>DL ⬇️</th><th>UL ⬆️</th><th>RTT (ms)</th><th>TCP Retransmits</th><th>Shaped</th></thead>";
for (let i = 0; i < tt.length; i++) {
let color = color_ramp(tt[i][IpStats.median_tcp_rtt]);
html += "<tr style='background-color: " + color + "'>";
html += "<tr>";
html += "<td style='color: " + color + "'></td>";
if (tt[i][IpStats.circuit_id] != "") {
html += "<td><a class='redact' href='/circuit_queue?id=" + encodeURI(tt[i][IpStats.circuit_id]) + "'>" + redactText(tt[i][IpStats.ip_address]) + "</td>";
} else {
@ -293,6 +294,7 @@
html += "<td>" + scaleNumber(tt[i][IpStats.bits_per_second][0]) + "</td>";
html += "<td>" + scaleNumber(tt[i][IpStats.bits_per_second][1]) + "</td>";
html += "<td>" + tt[i][IpStats.median_tcp_rtt].toFixed(2) + "</td>";
html += "<td>" + tt[i][IpStats.tcp_retransmits][0] + "/" + tt[i][IpStats.tcp_retransmits][1] + "</td>";
if (tt[i].tc_handle != 0) {
html += "<td><i class='fa fa-check-circle'></i> (" + tt[i][IpStats.plan][0] + "/" + tt[i][IpStats.plan][1] + ")</td>";
} else {
@ -319,7 +321,7 @@
function updateTop10Flows() {
$.get("/api/flows/top/10/rate", data => {
let html = "<table class='table' style='font-size: 8pt'>";
let html = "<table class='table table-striped' style='font-size: 8pt'>";
html += "<thead>";
html += "<th>Protocol</th>";
html += "<th>Local IP</th>";
@ -356,7 +358,7 @@
function updateTop10Endpoints() {
$.get("/api/flows/by_country", data => {
//console.log(data);
let html = "<table class='table' style='font-size: 8pt'>";
let html = "<table class='table table-striped' style='font-size: 8pt'>";
html += "<thead>";
html += "<th>Country</th>";
html += "<th>UL ⬆️</th>";

View File

@ -202,7 +202,7 @@ fn retire_check(cycle: u64, recent_cycle: u64) -> bool {
cycle < recent_cycle + RETIRE_AFTER_SECONDS
}
type TopList = (XdpIpAddress, (u64, u64), (u64, u64), f32, TcHandle, String);
type TopList = (XdpIpAddress, (u64, u64), (u64, u64), f32, TcHandle, String, (u64, u64));
pub fn top_n(start: u32, end: u32) -> BusResponse {
let mut full_list: Vec<TopList> = {
@ -222,6 +222,7 @@ pub fn top_n(start: u32, end: u32) -> BusResponse {
te.median_latency().unwrap_or(0.0),
te.tc_handle,
te.circuit_id.as_ref().unwrap_or(&String::new()).clone(),
te.tcp_retransmits,
)
})
.collect()
@ -239,6 +240,7 @@ pub fn top_n(start: u32, end: u32) -> BusResponse {
median_rtt,
tc_handle,
circuit_id,
tcp_retransmits,
)| IpStats {
ip_address: ip.as_ip().to_string(),
circuit_id: circuit_id.clone(),
@ -246,6 +248,7 @@ pub fn top_n(start: u32, end: u32) -> BusResponse {
packets_per_second: (*packets_dn, *packets_up),
median_tcp_rtt: *median_rtt,
tc_handle: *tc_handle,
tcp_retransmits: *tcp_retransmits,
},
)
.collect();
@ -271,6 +274,7 @@ pub fn worst_n(start: u32, end: u32) -> BusResponse {
te.median_latency().unwrap_or(0.0),
te.tc_handle,
te.circuit_id.as_ref().unwrap_or(&String::new()).clone(),
te.tcp_retransmits,
)
})
.collect()
@ -288,6 +292,7 @@ pub fn worst_n(start: u32, end: u32) -> BusResponse {
median_rtt,
tc_handle,
circuit_id,
tcp_retransmits,
)| IpStats {
ip_address: ip.as_ip().to_string(),
circuit_id: circuit_id.clone(),
@ -295,6 +300,7 @@ pub fn worst_n(start: u32, end: u32) -> BusResponse {
packets_per_second: (*packets_dn, *packets_up),
median_tcp_rtt: *median_rtt,
tc_handle: *tc_handle,
tcp_retransmits: *tcp_retransmits,
},
)
.collect();
@ -320,6 +326,7 @@ pub fn best_n(start: u32, end: u32) -> BusResponse {
te.median_latency().unwrap_or(0.0),
te.tc_handle,
te.circuit_id.as_ref().unwrap_or(&String::new()).clone(),
te.tcp_retransmits,
)
})
.collect()
@ -338,6 +345,7 @@ pub fn best_n(start: u32, end: u32) -> BusResponse {
median_rtt,
tc_handle,
circuit_id,
tcp_retransmits,
)| IpStats {
ip_address: ip.as_ip().to_string(),
circuit_id: circuit_id.clone(),
@ -345,6 +353,7 @@ pub fn best_n(start: u32, end: u32) -> BusResponse {
packets_per_second: (*packets_dn, *packets_up),
median_tcp_rtt: *median_rtt,
tc_handle: *tc_handle,
tcp_retransmits: *tcp_retransmits,
},
)
.collect();
@ -493,6 +502,7 @@ pub fn all_unknown_ips() -> BusResponse {
packets_per_second: (*packets_dn, *packets_up),
median_tcp_rtt: *median_rtt,
tc_handle: *tc_handle,
tcp_retransmits: (0, 0),
},
)
.collect();

View File

@ -17,6 +17,8 @@ pub(crate) struct ThroughputEntry {
pub(crate) recent_rtt_data: [RttData; 60],
pub(crate) last_fresh_rtt_data_cycle: u64,
pub(crate) last_seen: u64, // Last seen in kernel time since boot
pub(crate) tcp_retransmits: (u64, u64),
pub(crate) last_tcp_retransmits: (u64, u64),
}
impl ThroughputEntry {

View File

@ -154,6 +154,8 @@ impl ThroughputTracker {
recent_rtt_data: [RttData::from_nanos(0); 60],
last_fresh_rtt_data_cycle: 0,
last_seen: 0,
tcp_retransmits: (0, 0),
last_tcp_retransmits: (0, 0),
};
for c in counts {
entry.bytes.0 += c.download_bytes;
@ -189,6 +191,9 @@ impl ThroughputTracker {
// should limit outliers.
let mut rtt_circuit_tracker: FxHashMap<XdpIpAddress, [Vec<RttData>; 2]> = FxHashMap::default();
// Tracker for TCP retries. We're storing these per second.
let mut tcp_retries: FxHashMap<XdpIpAddress, [u64; 2]> = FxHashMap::default();
// Track the expired keys
let mut expired_keys = Vec::new();
@ -244,6 +249,14 @@ impl ThroughputTracker {
}
}
// TCP Retries
if let Some(retries) = tcp_retries.get_mut(&key.local_ip) {
retries[0] += data.tcp_retransmits[0] as u64;
retries[1] += data.tcp_retransmits[1] as u64;
} else {
tcp_retries.insert(key.local_ip, [data.tcp_retransmits[0] as u64, data.tcp_retransmits[1] as u64]);
}
if data.end_status != 0 {
// The flow has ended. We need to remove it from the map.
expired_keys.push(key.clone());
@ -279,6 +292,21 @@ impl ThroughputTracker {
}
}
// Merge in the TCP retries
// Reset all entries in the tracker to 0
for mut circuit in self.raw_data.iter_mut() {
circuit.tcp_retransmits = (0, 0);
}
// Apply the new ones
for (local_ip, retries) in tcp_retries {
if let Some(mut tracker) = self.raw_data.get_mut(&local_ip) {
tracker.tcp_retransmits.0 = retries[0].saturating_sub(tracker.last_tcp_retransmits.0);
tracker.tcp_retransmits.1 = retries[1].saturating_sub(tracker.last_tcp_retransmits.1);
tracker.last_tcp_retransmits.0 = retries[0];
tracker.last_tcp_retransmits.1 = retries[1];
}
}
// Key Expiration
if !expired_keys.is_empty() {
for key in expired_keys.iter() {