Repository: fujiapple852/trippy (language: rust)

Issue:
Add last icmp packet type column



Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5c4115112..99146991f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 - Added Ubuntu PPA package ([#859](https://github.com/fujiapple852/trippy/issues/859))
 - Added `--generate-man` flags for generating [ROFF](https://en.wikipedia.org/wiki/Roff_(software)) man
   page ([#85](https://github.com/fujiapple852/trippy/issues/85))
+- Added support for last icmp packet code (`T`) column ([#1105](https://github.com/fujiapple852/trippy/issues/1105))
 
 ### Changed
 
@@ -35,7 +36,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
   tests ([#908](https://github.com/fujiapple852/trippy/issues/908))
 - Added support for last src port (`S`) and last dest port (`P`) custom
   columns ([#974](https://github.com/fujiapple852/trippy/issues/974))
-- Added support for last sequence (`Q`) custom columns ([#976](https://github.com/fujiapple852/trippy/issues/976))
+- Added support for last sequence (`Q`) custom column ([#976](https://github.com/fujiapple852/trippy/issues/976))
 - Added support for more named theme colors ([#1011](https://github.com/fujiapple852/trippy/issues/1011))
 
 ### Changed
diff --git a/README.md b/README.md
index d4b3feaea..b13788c47 100644
--- a/README.md
+++ b/README.md
@@ -849,6 +849,7 @@ configuration file.
 | `Seq`   | `Q`  | The sequence number for the last probe for the hop                                                                                                                                                                                                                                                                                                    |
 | `Sprt`  | `S`  | The source port for last probe for the hop                                                                                                                                                                                                                                                                                                            |
 | `Dprt`  | `P`  | The destination port for last probe for the hop                                                                                                                                                                                                                                                                                                       |
+| `Type`  | `T`  | The icmp packet type for last probe for the hop:<br/>- TE TimeExceeded<br/>- ER EchoReply<br/>- UR Unreachable<br/>- NA NotApplicable                                                                                                                                                                                                                 |
 
 The default columns are `holsravbwdt`.
 
diff --git a/src/backend/trace.rs b/src/backend/trace.rs
index ead456033..b1b758400 100644
--- a/src/backend/trace.rs
+++ b/src/backend/trace.rs
@@ -5,7 +5,7 @@ use std::collections::HashMap;
 use std::iter::once;
 use std::net::IpAddr;
 use std::time::Duration;
-use trippy::tracing::{Extensions, ProbeState, Round, TimeToLive, TracerRound};
+use trippy::tracing::{Extensions, IcmpPacketType, ProbeState, Round, TimeToLive, TracerRound};
 
 /// The state of all hops in a trace.
 #[derive(Debug, Clone)]
@@ -165,6 +165,8 @@ pub struct Hop {
     last_dest_port: u16,
     /// The sequence number for the last probe for this hop.
     last_sequence: u16,
+    /// The icmp packet type for the last probe for this hop.
+    last_icmp_packet_type: Option<IcmpPacketType>,
     /// The history of round trip times across the last N rounds.
     samples: Vec<Duration>,
     /// The ICMP extensions for this hop.
@@ -281,6 +283,11 @@ impl Hop {
         self.last_sequence
     }
 
+    /// The icmp packet type for the last probe for this hop.
+    pub fn last_icmp_packet_type(&self) -> Option<IcmpPacketType> {
+        self.last_icmp_packet_type
+    }
+
     /// The last N samples.
     pub fn samples(&self) -> &[Duration] {
         &self.samples
@@ -309,6 +316,7 @@ impl Default for Hop {
             last_src_port: 0_u16,
             last_dest_port: 0_u16,
             last_sequence: 0_u16,
+            last_icmp_packet_type: None,
             mean: 0f64,
             m2: 0f64,
             samples: Vec::default(),
@@ -434,6 +442,7 @@ impl TraceData {
                 hop.last_src_port = complete.src_port.0;
                 hop.last_dest_port = complete.dest_port.0;
                 hop.last_sequence = complete.sequence.0;
+                hop.last_icmp_packet_type = Some(complete.icmp_packet_type);
             }
             ProbeState::Awaited(awaited) => {
                 self.update_lowest_ttl(awaited.ttl);
diff --git a/src/config/columns.rs b/src/config/columns.rs
index e53455f90..01f81b9f1 100644
--- a/src/config/columns.rs
+++ b/src/config/columns.rs
@@ -85,6 +85,8 @@ pub enum TuiColumn {
     LastDestPort,
     /// The sequence number for the last probe for this hop.
     LastSeq,
+    /// The icmp packet type for the last probe for this hop.
+    LastIcmpPacketType,
 }
 
 impl TryFrom<char> for TuiColumn {
@@ -110,6 +112,7 @@ impl TryFrom<char> for TuiColumn {
             'S' => Ok(Self::LastSrcPort),
             'P' => Ok(Self::LastDestPort),
             'Q' => Ok(Self::LastSeq),
+            'T' => Ok(Self::LastIcmpPacketType),
             c => Err(anyhow!(format!("unknown column code: {c}"))),
         }
     }
@@ -136,6 +139,7 @@ impl Display for TuiColumn {
             Self::LastSrcPort => write!(f, "S"),
             Self::LastDestPort => write!(f, "P"),
             Self::LastSeq => write!(f, "Q"),
+            Self::LastIcmpPacketType => write!(f, "T"),
         }
     }
 }
diff --git a/src/frontend/columns.rs b/src/frontend/columns.rs
index 602c855f1..9d3831374 100644
--- a/src/frontend/columns.rs
+++ b/src/frontend/columns.rs
@@ -178,6 +178,8 @@ pub enum ColumnType {
     LastDestPort,
     /// The sequence number for the last probe for this hop.
     LastSeq,
+    /// The icmp packet type for the last probe for this hop.
+    LastIcmpPacketType,
 }
 
 impl From<ColumnType> for char {
@@ -201,6 +203,7 @@ impl From<ColumnType> for char {
             ColumnType::LastSrcPort => 'S',
             ColumnType::LastDestPort => 'P',
             ColumnType::LastSeq => 'Q',
+            ColumnType::LastIcmpPacketType => 'T',
         }
     }
 }
@@ -226,6 +229,7 @@ impl From<TuiColumn> for Column {
             TuiColumn::LastSrcPort => Self::new_shown(ColumnType::LastSrcPort),
             TuiColumn::LastDestPort => Self::new_shown(ColumnType::LastDestPort),
             TuiColumn::LastSeq => Self::new_shown(ColumnType::LastSeq),
+            TuiColumn::LastIcmpPacketType => Self::new_shown(ColumnType::LastIcmpPacketType),
         }
     }
 }
@@ -251,6 +255,7 @@ impl Display for ColumnType {
             Self::LastSrcPort => write!(f, "Sprt"),
             Self::LastDestPort => write!(f, "Dprt"),
             Self::LastSeq => write!(f, "Seq"),
+            Self::LastIcmpPacketType => write!(f, "Type"),
         }
     }
 }
@@ -278,6 +283,7 @@ impl ColumnType {
             Self::LastSrcPort => ColumnWidth::Fixed(7),
             Self::LastDestPort => ColumnWidth::Fixed(7),
             Self::LastSeq => ColumnWidth::Fixed(7),
+            Self::LastIcmpPacketType => ColumnWidth::Fixed(7),
         }
     }
 }
@@ -334,6 +340,7 @@ mod tests {
                 Column::new_hidden(ColumnType::LastSrcPort),
                 Column::new_hidden(ColumnType::LastDestPort),
                 Column::new_hidden(ColumnType::LastSeq),
+                Column::new_hidden(ColumnType::LastIcmpPacketType),
             ])
         );
     }
diff --git a/src/frontend/render/table.rs b/src/frontend/render/table.rs
index 835ddaa6a..4df6c38b0 100644
--- a/src/frontend/render/table.rs
+++ b/src/frontend/render/table.rs
@@ -13,7 +13,9 @@ use ratatui::Frame;
 use std::net::IpAddr;
 use std::rc::Rc;
 use trippy::dns::{AsInfo, DnsEntry, DnsResolver, Resolved, Resolver, Unresolved};
-use trippy::tracing::{Extension, Extensions, MplsLabelStackMember, UnknownExtension};
+use trippy::tracing::{
+    Extension, Extensions, IcmpPacketType, MplsLabelStackMember, UnknownExtension,
+};
 
 /// Render the table of data about the hops.
 ///
@@ -161,6 +163,7 @@ fn new_cell(
         ColumnType::LastSrcPort => render_port_cell(hop.last_src_port()),
         ColumnType::LastDestPort => render_port_cell(hop.last_dest_port()),
         ColumnType::LastSeq => render_usize_cell(usize::from(hop.last_sequence())),
+        ColumnType::LastIcmpPacketType => render_icmp_packet_type_cell(hop.last_icmp_packet_type()),
     }
 }
 
@@ -207,6 +210,16 @@ fn render_status_cell(hop: &Hop, is_target: bool) -> Cell<'static> {
     })
 }
 
+fn render_icmp_packet_type_cell(icmp_packet_type: Option<IcmpPacketType>) -> Cell<'static> {
+    match icmp_packet_type {
+        None => Cell::from("n/a"),
+        Some(IcmpPacketType::TimeExceeded) => Cell::from("TE"),
+        Some(IcmpPacketType::EchoReply) => Cell::from("ER"),
+        Some(IcmpPacketType::Unreachable) => Cell::from("UR"),
+        Some(IcmpPacketType::NotApplicable) => Cell::from("NA"),
+    }
+}
+
 fn render_port_cell(port: u16) -> Cell<'static> {
     if port > 0 {
         Cell::from(format!("{port}"))
diff --git a/trippy-config-sample.toml b/trippy-config-sample.toml
index f970ed4e6..be1795b90 100644
--- a/trippy-config-sample.toml
+++ b/trippy-config-sample.toml
@@ -270,6 +270,7 @@ tui-as-mode = "asn"
 #   Q - Last probe sequence number
 #   S - Last probe source port
 #   P - Last probe destination port
+#   T - Last icmp packet type
 #
 # The columns will be shown in the order specified.
 tui-custom-columns = "holsravbwdt"


Test patch: