Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: calculate forward and backward loss (#860) - WIP #1288

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 50 additions & 1 deletion crates/trippy-core/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,10 @@ pub struct Hop {
total_recv: usize,
/// The total probes that failed for this hop.
total_failed: usize,
/// The total forward loss for this hop.
total_forward_lost: usize,
/// The total backward loss for this hop.
total_backward_lost: usize,
/// The total round trip time for this hop across all rounds.
total_time: Duration,
/// The round trip time for this hop in the current round.
Expand Down Expand Up @@ -238,6 +242,18 @@ impl Hop {
self.total_recv
}

/// The total number of probes with forward loss.
#[must_use]
pub const fn total_forward_loss(&self) -> usize {
self.total_forward_lost
}

/// The total number of probes with backward loss.
#[must_use]
pub const fn total_backward_loss(&self) -> usize {
self.total_backward_lost
}

/// The total number of probes that failed.
#[must_use]
pub const fn total_failed(&self) -> usize {
Expand All @@ -255,6 +271,17 @@ impl Hop {
}
}

/// The adjusted % of packets that are lost.
#[must_use]
pub fn adjusted_loss_pct(&self) -> f64 {
if self.total_sent > 0 {
let lost = self.total_forward_lost;
lost as f64 / self.total_sent as f64 * 100f64
} else {
0_f64
}
}

/// The duration of the last probe.
#[must_use]
pub fn last_ms(&self) -> Option<f64> {
Expand Down Expand Up @@ -366,6 +393,8 @@ impl Default for Hop {
addrs: IndexMap::default(),
total_sent: 0,
total_recv: 0,
total_forward_lost: 0,
total_backward_lost: 0,
total_failed: 0,
total_time: Duration::default(),
last: None,
Expand Down Expand Up @@ -491,21 +520,24 @@ mod state_updater {
use crate::{NatStatus, ProbeStatus, Round};
use std::time::Duration;

/// Update the `FlowState` from a `Round`.
/// Update the state of a `FlowState` from a `Round`.
pub(super) struct StateUpdater<'a> {
/// The state to update.
state: &'a mut FlowState,
/// The `Round` being processed.
round: &'a Round<'a>,
/// The checksum of the previous hop, if any.
prev_hop_checksum: Option<u16>,
/// Whether the previous hop had forward loss.
prev_hop_carry: bool,
}
impl<'a> StateUpdater<'a> {
pub(super) fn new(state: &'a mut FlowState, round: &'a Round<'_>) -> Self {
Self {
state,
round,
prev_hop_checksum: None,
prev_hop_carry: false,
}
}

Expand Down Expand Up @@ -586,6 +618,23 @@ mod state_updater {
state.hops[index].last_src_port = awaited.src_port.0;
state.hops[index].last_dest_port = awaited.dest_port.0;
state.hops[index].last_sequence = awaited.sequence.0;
if self.prev_hop_carry {
state.hops[index].total_backward_lost += 1;
} else if awaited.ttl.0 <= state.highest_ttl_for_round {
// TODO panicked: range end index 17 out of range for slice of length 5
let remaining =
&self.round.probes[index..usize::from(state.highest_ttl_for_round)];
if remaining.len() > 1 {
let all_awaited = remaining
.iter()
.skip(1)
.all(|p| matches!(p, ProbeStatus::Awaited(_)));
if all_awaited {
state.hops[index].total_forward_lost += 1;
self.prev_hop_carry = true;
}
}
}
}
ProbeStatus::Failed(failed) => {
state.update_lowest_ttl(failed.ttl);
Expand Down
37 changes: 37 additions & 0 deletions crates/trippy-core/tests/resources/simulation/ipv4_icmp_loss.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: IPv4/ICMP with 9 hops, 2 of which do not respond
target: 10.0.0.109
protocol: Icmp
icmp_identifier: 3
hops:
- ttl: 1
resp: !SingleHost
addr: 10.0.0.101
rtt_ms: 10
- ttl: 2
resp: NoResponse
- ttl: 3
resp: !SingleHost
addr: 10.0.0.103
rtt_ms: 20
- ttl: 4
resp: !SingleHost
addr: 10.0.0.104
rtt_ms: 20
- ttl: 5
resp: !SingleHost
addr: 10.0.0.105
rtt_ms: 20
- ttl: 6
resp: !SingleHost
addr: 10.0.0.106
rtt_ms: 20
- ttl: 7
resp: !SingleHost
addr: 10.0.0.107
rtt_ms: 20
- ttl: 8
resp: NoResponse
- ttl: 9
resp: !SingleHost
addr: 10.0.0.109
rtt_ms: 20
26 changes: 25 additions & 1 deletion crates/trippy-tui/locales/app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -837,4 +837,28 @@ column_fail:
it: "Falliti"
pt: "Falha"
zh: "失败"
sv: "Misslyckades"
sv: "Misslyckades"
column_floss:
en: "Floss"
fr: "Floss"
tr: "Floss"
it: "Floss"
pt: "Floss"
zh: "Floss"
sv: "Floss"
column_bloss:
en: "Bloss"
fr: "Bloss"
tr: "Bloss"
it: "Bloss"
pt: "Bloss"
zh: "Bloss"
sv: "Bloss"
column_aloss_pct:
en: "Aloss%"
fr: "Aloss%"
tr: "Aloss%"
it: "Aloss%"
pt: "Aloss%"
zh: "Aloss%"
sv: "Aloss%"
12 changes: 12 additions & 0 deletions crates/trippy-tui/src/config/columns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ pub enum TuiColumn {
LastNatStatus,
/// The number of probes that failed for a hop.
Failed,
/// The number of probes with forward loss.
Floss,
/// The number of probes with backward loss.
Bloss,
/// The loss percentage adjusted for back and forward loss.
AdjustedLossPct,
}

impl TryFrom<char> for TuiColumn {
Expand Down Expand Up @@ -122,6 +128,9 @@ impl TryFrom<char> for TuiColumn {
'C' => Ok(Self::LastIcmpPacketCode),
'N' => Ok(Self::LastNatStatus),
'f' => Ok(Self::Failed),
'F' => Ok(Self::Floss),
'B' => Ok(Self::Bloss),
'A' => Ok(Self::AdjustedLossPct),
c => Err(anyhow!(format!("unknown column code: {c}"))),
}
}
Expand Down Expand Up @@ -152,6 +161,9 @@ impl Display for TuiColumn {
Self::LastIcmpPacketCode => write!(f, "C"),
Self::LastNatStatus => write!(f, "N"),
Self::Failed => write!(f, "f"),
Self::Floss => write!(f, "F"),
Self::Bloss => write!(f, "B"),
Self::AdjustedLossPct => write!(f, "A"),
}
}
}
Expand Down
22 changes: 22 additions & 0 deletions crates/trippy-tui/src/frontend/columns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,12 @@ pub enum ColumnType {
LastNatStatus,
/// The number of probes that failed for a hop.
Failed,
/// The number of probes with forward loss.
Floss,
/// The number of probes with backward loss.
Bloss,
/// The loss percentage adjusted for back and forward loss.
AdjustedLossPct,
}

impl From<ColumnType> for char {
Expand Down Expand Up @@ -216,6 +222,9 @@ impl From<ColumnType> for char {
ColumnType::LastIcmpPacketCode => 'C',
ColumnType::LastNatStatus => 'N',
ColumnType::Failed => 'f',
ColumnType::Floss => 'F',
ColumnType::Bloss => 'B',
ColumnType::AdjustedLossPct => 'A',
}
}
}
Expand Down Expand Up @@ -245,6 +254,9 @@ impl From<TuiColumn> for Column {
TuiColumn::LastIcmpPacketCode => Self::new_shown(ColumnType::LastIcmpPacketCode),
TuiColumn::LastNatStatus => Self::new_shown(ColumnType::LastNatStatus),
TuiColumn::Failed => Self::new_shown(ColumnType::Failed),
TuiColumn::Floss => Self::new_shown(ColumnType::Floss),
TuiColumn::Bloss => Self::new_shown(ColumnType::Bloss),
TuiColumn::AdjustedLossPct => Self::new_shown(ColumnType::AdjustedLossPct),
}
}
}
Expand All @@ -257,6 +269,7 @@ impl Display for ColumnType {

impl ColumnType {
/// The name of the column in the current locale.
#[allow(clippy::cognitive_complexity)]
pub(self) fn name(&self) -> Cow<'_, str> {
match self {
Self::Ttl => Cow::Borrowed("#"),
Expand All @@ -281,6 +294,9 @@ impl ColumnType {
Self::LastIcmpPacketCode => t!("column_code"),
Self::LastNatStatus => t!("column_nat"),
Self::Failed => t!("column_fail"),
Self::Floss => t!("column_floss"),
Self::Bloss => t!("column_bloss"),
Self::AdjustedLossPct => t!("column_aloss_pct"),
}
}

Expand Down Expand Up @@ -319,6 +335,9 @@ impl ColumnType {
Self::LastIcmpPacketCode => ColumnWidth::Fixed(width.max(7)),
Self::LastNatStatus => ColumnWidth::Fixed(width.max(7)),
Self::Failed => ColumnWidth::Fixed(width.max(7)),
Self::Floss => ColumnWidth::Fixed(width.max(7)),
Self::Bloss => ColumnWidth::Fixed(width.max(7)),
Self::AdjustedLossPct => ColumnWidth::Fixed(width.max(8)),
}
}
}
Expand Down Expand Up @@ -379,6 +398,9 @@ mod tests {
Column::new_hidden(ColumnType::LastIcmpPacketCode),
Column::new_hidden(ColumnType::LastNatStatus),
Column::new_hidden(ColumnType::Failed),
Column::new_hidden(ColumnType::Floss),
Column::new_hidden(ColumnType::Bloss),
Column::new_hidden(ColumnType::AdjustedLossPct),
])
);
}
Expand Down
7 changes: 7 additions & 0 deletions crates/trippy-tui/src/frontend/render/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,9 @@ fn new_cell(
ColumnType::LastIcmpPacketType => render_icmp_packet_type_cell(hop.last_icmp_packet_type()),
ColumnType::LastIcmpPacketCode => render_icmp_packet_code_cell(hop.last_icmp_packet_type()),
ColumnType::LastNatStatus => render_nat_cell(hop.last_nat_status()),
ColumnType::Floss => render_usize_cell(hop.total_forward_loss()),
ColumnType::Bloss => render_usize_cell(hop.total_backward_loss()),
ColumnType::AdjustedLossPct => render_adjusted_loss_pct_cell(hop),
}
}

Expand All @@ -190,6 +193,10 @@ fn render_loss_pct_cell(hop: &Hop) -> Cell<'static> {
Cell::from(format!("{:.1}%", hop.loss_pct()))
}

fn render_adjusted_loss_pct_cell(hop: &Hop) -> Cell<'static> {
Cell::from(format!("{:.1}%", hop.adjusted_loss_pct()))
}

fn render_avg_cell(hop: &Hop) -> Cell<'static> {
Cell::from(if hop.total_recv() > 0 {
format!("{:.1}", hop.avg_ms())
Expand Down
2 changes: 2 additions & 0 deletions trippy-config-sample.toml
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,8 @@ tui-as-mode = "asn"
# C - Last icmp packet code
# N - Last NAT status
# f - Probes failed
# F = Probe forward loss
# B = Probe backward loss
#
# The columns will be shown in the order specified.
tui-custom-columns = "holsravbwdt"
Expand Down