Skip to content

Commit

Permalink
feat: show source address and port in Tui
Browse files Browse the repository at this point in the history
  • Loading branch information
fujiapple852 committed May 16, 2022
1 parent 6531740 commit ee80770
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 60 deletions.
81 changes: 63 additions & 18 deletions src/frontend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -449,24 +449,9 @@ fn render_header<B: Backend>(f: &mut Frame<'_, B>, app: &mut TuiApp, rect: Rect)
.tui_config
.max_addrs
.map_or_else(|| String::from("auto"), |m| m.to_string());
let target = match app.tracer_config().protocol {
TracerProtocol::Icmp => {
format!(
"{} ({})",
app.tracer_config().target_hostname,
app.tracer_config().target_addr
)
}
TracerProtocol::Udp | TracerProtocol::Tcp => {
format!(
"{}:{} ({}:{})",
app.tracer_config().target_hostname,
app.tracer_config().target_port,
app.tracer_config().target_addr,
app.tracer_config().target_port
)
}
};
let source = render_source(app);
let dest = render_destination(app);
let target = format!("{} -> {}", source, dest);
let left_spans = vec![
Spans::from(vec![
Span::styled("Target: ", Style::default().add_modifier(Modifier::BOLD)),
Expand Down Expand Up @@ -503,6 +488,66 @@ fn render_header<B: Backend>(f: &mut Frame<'_, B>, app: &mut TuiApp, rect: Rect)
f.render_widget(left, rect);
}

/// Render the source address of the trace.
fn render_source(app: &mut TuiApp) -> String {
let source = match app.tracer_config().protocol {
TracerProtocol::Icmp => {
format!(
"{} ({})",
app.resolver.reverse_lookup(app.tracer_config().source_addr),
app.tracer_config().source_addr
)
}
TracerProtocol::Udp => {
format!(
"{}:{} ({}:{})",
app.resolver.reverse_lookup(app.tracer_config().source_addr),
app.tracer_config().source_port,
app.tracer_config().source_addr,
app.tracer_config().source_port,
)
}
TracerProtocol::Tcp => {
format!(
"{}:* ({}:*)",
app.resolver.reverse_lookup(app.tracer_config().source_addr),
app.tracer_config().source_addr,
)
}
};
source
}

/// Render the destination address.
fn render_destination(app: &mut TuiApp) -> String {
let dest = match app.tracer_config().protocol {
TracerProtocol::Icmp => {
format!(
"{} ({})",
app.tracer_config().target_hostname,
app.tracer_config().target_addr
)
}
TracerProtocol::Udp => {
format!(
"{}:* ({}:*)",
app.tracer_config().target_hostname,
app.tracer_config().target_addr,
)
}
TracerProtocol::Tcp => {
format!(
"{}:{} ({}:{})",
app.tracer_config().target_hostname,
app.tracer_config().target_port,
app.tracer_config().target_addr,
app.tracer_config().target_port
)
}
};
dest
}

/// Format the `DnsResolveMethod`.
fn format_dns_method(resolve_method: DnsResolveMethod) -> String {
match resolve_method {
Expand Down
109 changes: 74 additions & 35 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ use crate::caps::{drop_caps, ensure_caps};
use crate::config::{Mode, TrippyConfig};
use crate::dns::{DnsResolver, DnsResolverConfig};
use crate::frontend::TuiConfig;
use anyhow::anyhow;
use anyhow::{anyhow, Error};
use clap::Parser;
use config::Args;
use parking_lot::RwLock;
use std::net::IpAddr;
use std::sync::Arc;
use std::thread;
use std::time::Duration;
use trippy::tracing::{TracerChannel, TracerConfig, TracerProtocol};
use trippy::tracing::{TracerChannel, TracerChannelConfig, TracerConfig, TracerProtocol};

mod backend;
mod caps;
Expand All @@ -35,39 +35,56 @@ mod report;
fn main() -> anyhow::Result<()> {
let pid = u16::try_from(std::process::id() % u32::from(u16::MAX))?;
let cfg = TrippyConfig::try_from((Args::parse(), pid))?;
ensure_caps()?;
let resolver = DnsResolver::start(DnsResolverConfig::new(
cfg.dns_resolve_method,
cfg.dns_timeout,
))?;
ensure_caps()?;
let traces: Vec<_> = cfg
.targets
.iter()
.map(|target| make_trace_info(&cfg, &resolver, target))
.enumerate()
.map(|(i, target_host)| start_tracer(&cfg, target_host, pid + i as u16, &resolver))
.collect::<anyhow::Result<Vec<_>>>()?;
for (i, info) in traces.iter().enumerate() {
let trace_identifier = pid + i as u16;
let tracer_config = make_tracer_config(&cfg, info.target_addr, trace_identifier)?;
start_backend(tracer_config, info.data.clone())?;
}
drop_caps()?;
run_frontend(&cfg, resolver, traces)?;
Ok(())
}

/// Create a network channel in a thread and drop all capabilities.
fn start_backend(
tracer_config: TracerConfig,
trace_data: Arc<RwLock<Trace>>,
) -> anyhow::Result<()> {
let channel = TracerChannel::connect(&tracer_config)?;
thread::Builder::new()
.name(format!("tracer-{}", tracer_config.trace_identifier.0))
.spawn(move || {
drop_caps().expect("failed to drop capabilities in tracer thread");
backend::run_backend(&tracer_config, channel, trace_data).expect("backend failed");
})?;
Ok(())
/// Start a tracer to a given target.
fn start_tracer(
cfg: &TrippyConfig,
target_host: &str,
trace_identifier: u16,
resolver: &DnsResolver,
) -> Result<TraceInfo, Error> {
let target_addr: IpAddr = resolver
.lookup(target_host)
.map_err(|e| anyhow!("failed to resolve target: {} ({})", target_host, e))?
.into_iter()
.find(|addr| matches!(addr, IpAddr::V4(_)))
.ok_or_else(|| anyhow!("failed to find an IPv4 address for target: {}", target_host))?;
let trace_data = Arc::new(RwLock::new(Trace::new(cfg.tui_max_samples)));
let channel_config = make_channel_config(cfg, target_addr, trace_identifier);
let channel = TracerChannel::connect(&channel_config)?;
let source_addr = channel.src_addr();
let tracer_config = make_tracer_config(cfg, target_addr, trace_identifier)?;
{
let trace_data = trace_data.clone();
thread::Builder::new()
.name(format!("tracer-{}", tracer_config.trace_identifier.0))
.spawn(move || {
drop_caps().expect("failed to drop capabilities in tracer thread");
backend::run_backend(&tracer_config, channel, trace_data).expect("backend failed");
})?;
}
Ok(make_trace_info(
cfg,
trace_data,
source_addr,
target_host.to_string(),
target_addr,
))
}

/// Run the TUI, stream or report.
Expand Down Expand Up @@ -113,30 +130,46 @@ fn make_tracer_config(
)?)
}

/// Make the tracer configuration.
fn make_channel_config(
args: &TrippyConfig,
target_addr: IpAddr,
trace_identifier: u16,
) -> TracerChannelConfig {
TracerChannelConfig::new(
args.protocol,
target_addr,
trace_identifier,
args.packet_size,
args.payload_pattern,
args.source_port,
args.destination_port,
args.read_timeout,
args.min_round_duration,
)
}

/// Make the per-trace information.
fn make_trace_info(
args: &TrippyConfig,
resolver: &DnsResolver,
target: &str,
) -> anyhow::Result<TraceInfo> {
let target_addr: IpAddr = resolver
.lookup(target)
.map_err(|e| anyhow!("failed to resolve target: {} ({})", target, e))?
.into_iter()
.find(|addr| matches!(addr, IpAddr::V4(_)))
.unwrap();
let trace_data = Arc::new(RwLock::new(Trace::new(args.tui_max_samples)));
Ok(TraceInfo::new(
trace_data: Arc<RwLock<Trace>>,
source_addr: IpAddr,
target: String,
target_addr: IpAddr,
) -> TraceInfo {
TraceInfo::new(
trace_data,
target.to_string(),
source_addr,
args.source_port,
target,
target_addr,
args.destination_port,
args.protocol,
args.first_ttl,
args.max_ttl,
args.grace_duration,
args.min_round_duration,
))
)
}

/// Make the TUI configuration.
Expand All @@ -155,6 +188,8 @@ fn make_tui_config(args: &TrippyConfig) -> TuiConfig {
#[derive(Debug, Clone)]
pub struct TraceInfo {
pub data: Arc<RwLock<Trace>>,
pub source_addr: IpAddr,
pub source_port: u16,
pub target_hostname: String,
pub target_addr: IpAddr,
pub target_port: u16,
Expand All @@ -170,6 +205,8 @@ impl TraceInfo {
#[must_use]
pub fn new(
data: Arc<RwLock<Trace>>,
source_addr: IpAddr,
source_port: u16,
target_hostname: String,
target_addr: IpAddr,
target_port: u16,
Expand All @@ -181,6 +218,8 @@ impl TraceInfo {
) -> Self {
Self {
data,
source_addr,
source_port,
target_hostname,
target_addr,
target_port,
Expand Down
2 changes: 1 addition & 1 deletion src/tracing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ mod types;
mod util;

pub use config::{TracerConfig, TracerProtocol};
pub use net::TracerChannel;
pub use net::{TracerChannel, TracerChannelConfig};
pub use probe::{IcmpPacketType, Probe, ProbeStatus};
pub use tracer::{Tracer, TracerRound};
2 changes: 1 addition & 1 deletion src/tracing/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ impl Display for TracerProtocol {
}
}

/// TODO
/// Tracing algorithm configuration.
#[derive(Debug, Copy, Clone)]
pub struct TracerConfig {
pub target_addr: IpAddr,
Expand Down
52 changes: 47 additions & 5 deletions src/tracing/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::tracing::error::TracerError::AddressNotAvailable;
use crate::tracing::error::{TraceResult, TracerError};
use crate::tracing::types::{DestinationPort, PacketSize, PayloadPattern, SourcePort, TraceId};
use crate::tracing::util::Required;
use crate::tracing::{Probe, TracerConfig, TracerProtocol};
use crate::tracing::{Probe, TracerProtocol};
use arrayvec::ArrayVec;
use itertools::Itertools;
use nix::sys::select::FdSet;
Expand Down Expand Up @@ -119,25 +119,67 @@ pub struct TracerChannel {
tcp_probes: ArrayVec<TcpProbe, MAX_TCP_PROBES>,
}

/// Tracer network channel configuration.
#[derive(Debug, Copy, Clone)]
pub struct TracerChannelConfig {
protocol: TracerProtocol,
target_addr: IpAddr,
identifier: TraceId,
packet_size: PacketSize,
payload_pattern: PayloadPattern,
source_port: SourcePort,
destination_port: DestinationPort,
icmp_read_timeout: Duration,
tcp_connect_timeout: Duration,
}

impl TracerChannelConfig {
#[allow(clippy::too_many_arguments)]
#[must_use]
pub fn new(
protocol: TracerProtocol,
target_addr: IpAddr,
identifier: u16,
packet_size: u16,
payload_pattern: u8,
source_port: u16,
destination_port: u16,
icmp_read_timeout: Duration,
tcp_connect_timeout: Duration,
) -> Self {
Self {
protocol,
target_addr,
identifier: TraceId(identifier),
packet_size: PacketSize(packet_size),
payload_pattern: PayloadPattern(payload_pattern),
source_port: SourcePort(source_port),
destination_port: DestinationPort(destination_port),
icmp_read_timeout,
tcp_connect_timeout,
}
}
}

impl TracerChannel {
/// Create an `IcmpChannel`.
///
/// This operation requires the `CAP_NET_RAW` capability on Linux.
pub fn connect(config: &TracerConfig) -> TraceResult<Self> {
pub fn connect(config: &TracerChannelConfig) -> TraceResult<Self> {
let src_addr = discover_ipv4_addr(config.target_addr, config.destination_port.0)?;
let (icmp_tx, icmp_rx) = make_icmp_channel()?;
let (udp_tx, _) = make_udp_channel()?;
Ok(Self {
protocol: config.protocol,
src_addr,
dest_addr: config.target_addr,
identifier: config.trace_identifier,
identifier: config.identifier,
packet_size: config.packet_size,
payload_pattern: config.payload_pattern,
source_port: config.source_port,
destination_port: config.destination_port,
icmp_read_timeout: config.read_timeout,
tcp_connect_timeout: config.min_round_duration,
icmp_read_timeout: config.icmp_read_timeout,
tcp_connect_timeout: config.tcp_connect_timeout,
icmp_tx,
icmp_rx,
udp_tx,
Expand Down

0 comments on commit ee80770

Please sign in to comment.