Skip to content

Commit

Permalink
feat(net): add support for ping sockets (#101) - WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
fujiapple852 committed Oct 29, 2023
1 parent d2d2eb5 commit f077c49
Show file tree
Hide file tree
Showing 15 changed files with 311 additions and 84 deletions.
63 changes: 54 additions & 9 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ use std::str::FromStr;
use std::time::Duration;
use strum::VariantNames;
use theme::TuiThemeItem;
use trippy::tracing::{MultipathStrategy, PortDirection, TracerAddrFamily, TracerProtocol};
use trippy::tracing::{
MultipathStrategy, PortDirection, PrivilegeMode, TracerAddrFamily, TracerProtocol,
};

mod binding;
mod cmd;
Expand Down Expand Up @@ -212,6 +214,7 @@ pub struct TrippyConfig {
pub tui_theme: TuiTheme,
pub tui_bindings: TuiBindings,
pub mode: Mode,
pub privilege_mode: PrivilegeMode,
pub report_cycles: usize,
pub geoip_mmdb_file: Option<String>,
pub max_rounds: Option<usize>,
Expand All @@ -230,8 +233,8 @@ impl TryFrom<(Args, &Platform)> for TrippyConfig {
args,
&Platform {
pid,
has_privileges: is_privileged,
..
has_privileges,
needs_privileges,
},
) = data;
if args.print_tui_theme_items {
Expand Down Expand Up @@ -268,6 +271,18 @@ impl TryFrom<(Args, &Platform)> for TrippyConfig {
let cfg_file_dns = cfg_file.dns.unwrap_or_default();
let cfg_file_report = cfg_file.report.unwrap_or_default();
let mode = cfg_layer(args.mode, cfg_file_trace.mode, constants::DEFAULT_MODE);
let unprivileged = cfg_layer_bool_flag(
args.unprivileged,
cfg_file_trace.unprivileged,
constants::DEFAULT_UNPRIVILEGED,
);

let privilege_mode = if unprivileged {
PrivilegeMode::Unprivileged
} else {
PrivilegeMode::Privileged
};

let verbose = args.verbose;
let log_format = cfg_layer(
args.log_format,
Expand Down Expand Up @@ -479,8 +494,9 @@ impl TryFrom<(Args, &Platform)> for TrippyConfig {
Some(n) if n > 0 => Some(n),
_ => None,
};
validate_privilege(is_privileged)?;
validate_privilege(privilege_mode, has_privileges, needs_privileges)?;
validate_logging(mode, verbose)?;
validate_strategy(multipath_strategy, unprivileged)?;
validate_multi(mode, protocol, &args.targets)?;
validate_ttl(first_ttl, max_ttl)?;
validate_max_inflight(max_inflight)?;
Expand Down Expand Up @@ -535,6 +551,7 @@ impl TryFrom<(Args, &Platform)> for TrippyConfig {
tui_theme,
tui_bindings,
mode,
privilege_mode,
report_cycles,
geoip_mmdb_file,
max_rounds,
Expand Down Expand Up @@ -572,11 +589,26 @@ fn cfg_layer_bool_flag(fst: bool, snd: Option<bool>, default: bool) -> bool {
}
}

fn validate_privilege(is_privileged: bool) -> anyhow::Result<()> {
if is_privileged {
Ok(())
} else {
Err(anyhow!("privileges are required to use raw sockets, see https://github.com/fujiapple852/trippy#privileges"))
fn validate_privilege(
privilege_mode: PrivilegeMode,
has_privileges: bool,
needs_privileges: bool,
) -> anyhow::Result<()> {
const PRIVILEGE_URL: &str = "https://github.com/fujiapple852/trippy#privileges";
match (privilege_mode, has_privileges, needs_privileges) {
(PrivilegeMode::Privileged, true, _) | (PrivilegeMode::Unprivileged, _, false) => Ok(()),
(PrivilegeMode::Privileged, false, true) => Err(anyhow!(format!(
"privileges are required\n\nsee {} for details",
PRIVILEGE_URL
))),
(PrivilegeMode::Privileged, false, false) => Err(anyhow!(format!(
"privileges are required (hint: try adding -u to run in unprivileged mode)\n\nsee {} for details",
PRIVILEGE_URL
))),
(PrivilegeMode::Unprivileged, _, true) => Err(anyhow!(format!(
"unprivileged mode not supported on this platform\n\nsee {} for details",
PRIVILEGE_URL
))),
}
}

Expand All @@ -588,6 +620,19 @@ fn validate_logging(mode: Mode, verbose: bool) -> anyhow::Result<()> {
}
}

/// Validate the tracing strategy against the privilege mode.
fn validate_strategy(strategy: MultipathStrategy, unprivileged: bool) -> anyhow::Result<()> {
match (strategy, unprivileged) {
(MultipathStrategy::Dublin, true) => Err(anyhow!(
"Dublin tracing strategy cannot be used in unprivileged mode"
)),
(MultipathStrategy::Paris, true) => Err(anyhow!(
"Paris tracing strategy cannot be used in unprivileged mode"
)),
_ => Ok(()),
}
}

/// We only allow multiple targets to be specified for the Tui and for `Icmp` tracing.
fn validate_multi(mode: Mode, protocol: TracerProtocol, targets: &[String]) -> anyhow::Result<()> {
match (mode, protocol) {
Expand Down
4 changes: 4 additions & 0 deletions src/config/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ pub struct Args {
#[arg(value_enum, short = 'm', long)]
pub mode: Option<Mode>,

/// Trace without requiring elevated privileges
#[arg(short = 'u', long)]
pub unprivileged: bool,

/// Tracing protocol [default: icmp]
#[arg(value_enum, short = 'p', long)]
pub protocol: Option<Protocol>,
Expand Down
3 changes: 3 additions & 0 deletions src/config/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ pub const MAX_HOPS: usize = u8::MAX as usize;
/// The default value for `mode`.
pub const DEFAULT_MODE: Mode = Mode::Tui;

/// The default value for `unprivileged`.
pub const DEFAULT_UNPRIVILEGED: bool = false;

/// The default value for `log-format`.
pub const DEFAULT_LOG_FORMAT: LogFormat = LogFormat::Pretty;

Expand Down
1 change: 1 addition & 0 deletions src/config/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ pub struct ConfigFile {
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
pub struct ConfigTrippy {
pub mode: Option<Mode>,
pub unprivileged: Option<bool>,
pub log_format: Option<LogFormat>,
pub log_filter: Option<String>,
pub log_span_events: Option<LogSpanEvents>,
Expand Down
15 changes: 12 additions & 3 deletions src/frontend/render/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,22 @@ pub fn render(f: &mut Frame<'_>, app: &TuiApp, rect: Rect) {
.block(header_block.clone())
.alignment(Alignment::Right);
let protocol = match app.tracer_config().protocol {
TracerProtocol::Icmp => format!("icmp({})", app.tracer_config().addr_family),
TracerProtocol::Icmp => format!(
"icmp({}, {})",
app.tracer_config().addr_family,
app.tracer_config().privilege_mode
),
TracerProtocol::Udp => format!(
"udp({}, {})",
"udp({}, {}, {})",
app.tracer_config().addr_family,
app.tracer_config().multipath_strategy,
app.tracer_config().privilege_mode
),
TracerProtocol::Tcp => format!(
"tcp({}, {})",
app.tracer_config().addr_family,
app.tracer_config().privilege_mode
),
TracerProtocol::Tcp => format!("tcp({})", app.tracer_config().addr_family),
};
let details = if app.show_hop_details {
String::from("on")
Expand Down
7 changes: 6 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ use tracing_chrome::{ChromeLayerBuilder, FlushGuard};
use tracing_subscriber::fmt::format::FmtSpan;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use trippy::tracing::SourceAddr;
use trippy::tracing::{
MultipathStrategy, PortDirection, TracerAddrFamily, TracerChannelConfig, TracerConfig,
TracerProtocol,
};
use trippy::tracing::{PrivilegeMode, SourceAddr};

mod backend;
mod config;
Expand Down Expand Up @@ -229,6 +229,7 @@ fn make_channel_config(
target_addr: IpAddr,
) -> TracerChannelConfig {
TracerChannelConfig::new(
args.privilege_mode,
args.protocol,
args.addr_family,
source_addr,
Expand All @@ -255,6 +256,7 @@ fn make_trace_info(
source_addr,
target,
target_addr,
args.privilege_mode,
args.multipath_strategy,
args.port_direction,
args.protocol,
Expand Down Expand Up @@ -297,6 +299,7 @@ pub struct TraceInfo {
pub source_addr: IpAddr,
pub target_hostname: String,
pub target_addr: IpAddr,
pub privilege_mode: PrivilegeMode,
pub multipath_strategy: MultipathStrategy,
pub port_direction: PortDirection,
pub protocol: TracerProtocol,
Expand All @@ -323,6 +326,7 @@ impl TraceInfo {
source_addr: IpAddr,
target_hostname: String,
target_addr: IpAddr,
privilege_mode: PrivilegeMode,
multipath_strategy: MultipathStrategy,
port_direction: PortDirection,
protocol: TracerProtocol,
Expand All @@ -345,6 +349,7 @@ impl TraceInfo {
source_addr,
target_hostname,
target_addr,
privilege_mode,
multipath_strategy,
port_direction,
protocol,
Expand Down
4 changes: 2 additions & 2 deletions src/tracing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ mod util;
pub mod packet;

pub use config::{
MultipathStrategy, PortDirection, TracerAddrFamily, TracerChannelConfig, TracerConfig,
TracerProtocol,
MultipathStrategy, PortDirection, PrivilegeMode, TracerAddrFamily, TracerChannelConfig,
TracerConfig, TracerProtocol,
};
pub use net::channel::TracerChannel;
pub use net::source::SourceAddr;
Expand Down
21 changes: 21 additions & 0 deletions src/tracing/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,24 @@ use std::fmt::{Display, Formatter};
use std::net::IpAddr;
use std::time::Duration;

/// The privilege mode.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum PrivilegeMode {
/// Privileged mode.
Privileged,
/// Unprivileged mode.
Unprivileged,
}

impl Display for PrivilegeMode {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Privileged => write!(f, "privileged"),
Self::Unprivileged => write!(f, "unprivileged"),
}
}
}

/// The address family.
#[derive(Debug, Copy, Clone)]
pub enum TracerAddrFamily {
Expand Down Expand Up @@ -149,6 +167,7 @@ impl PortDirection {
/// Tracer network channel configuration.
#[derive(Debug, Clone)]
pub struct TracerChannelConfig {
pub privilege_mode: PrivilegeMode,
pub protocol: TracerProtocol,
pub addr_family: TracerAddrFamily,
pub source_addr: IpAddr,
Expand All @@ -165,6 +184,7 @@ impl TracerChannelConfig {
#[allow(clippy::too_many_arguments)]
#[must_use]
pub fn new(
privilege_mode: PrivilegeMode,
protocol: TracerProtocol,
addr_family: TracerAddrFamily,
source_addr: IpAddr,
Expand All @@ -177,6 +197,7 @@ impl TracerChannelConfig {
tcp_connect_timeout: Duration,
) -> Self {
Self {
privilege_mode,
protocol,
addr_family,
source_addr,
Expand Down
27 changes: 17 additions & 10 deletions src/tracing/net/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ use crate::tracing::net::socket::Socket;
use crate::tracing::net::{ipv4, ipv6, platform, Network};
use crate::tracing::probe::ProbeResponse;
use crate::tracing::types::{PacketSize, PayloadPattern, Sequence, TypeOfService};
use crate::tracing::{MultipathStrategy, Probe, TracerChannelConfig, TracerProtocol};
use crate::tracing::{
MultipathStrategy, PrivilegeMode, Probe, TracerChannelConfig, TracerProtocol,
};
use arrayvec::ArrayVec;
use itertools::Itertools;
use std::net::IpAddr;
Expand All @@ -18,6 +20,7 @@ const MAX_TCP_PROBES: usize = 256;

/// A channel for sending and receiving `Probe` packets.
pub struct TracerChannel<S: Socket> {
privilege_mode: PrivilegeMode,
protocol: TracerProtocol,
src_addr: IpAddr,
ipv4_length_order: platform::PlatformIpv4FieldByteOrder,
Expand Down Expand Up @@ -45,16 +48,18 @@ impl<S: Socket> TracerChannel<S> {
config.packet_size.0,
)));
}
let raw = config.privilege_mode == PrivilegeMode::Privileged;
platform::startup()?;
let ipv4_length_order =
platform::PlatformIpv4FieldByteOrder::for_address(config.source_addr)?;
let send_socket = match config.protocol {
TracerProtocol::Icmp => Some(make_icmp_send_socket(config.source_addr)?),
TracerProtocol::Udp => Some(make_udp_send_socket(config.source_addr)?),
TracerProtocol::Icmp => Some(make_icmp_send_socket(config.source_addr, raw)?),
TracerProtocol::Udp => Some(make_udp_send_socket(config.source_addr, raw)?),
TracerProtocol::Tcp => None,
};
let recv_socket = make_recv_socket(config.source_addr)?;
let recv_socket = make_recv_socket(config.source_addr, raw)?;
Ok(Self {
privilege_mode: config.privilege_mode,
protocol: config.protocol,
src_addr: config.source_addr,
ipv4_length_order,
Expand Down Expand Up @@ -137,6 +142,7 @@ impl<S: Socket> TracerChannel<S> {
probe,
src_addr,
dest_addr,
self.privilege_mode,
self.packet_size,
self.payload_pattern,
self.multipath_strategy,
Expand All @@ -149,6 +155,7 @@ impl<S: Socket> TracerChannel<S> {
probe,
src_addr,
dest_addr,
self.privilege_mode,
self.packet_size,
self.payload_pattern,
)
Expand Down Expand Up @@ -235,27 +242,27 @@ impl<S: Socket> TcpProbe<S> {

/// Make a socket for sending raw `ICMP` packets.
#[instrument]
fn make_icmp_send_socket<S: Socket>(addr: IpAddr) -> TraceResult<S> {
fn make_icmp_send_socket<S: Socket>(addr: IpAddr, raw: bool) -> TraceResult<S> {
Ok(match addr {
IpAddr::V4(_) => S::new_icmp_send_socket_ipv4(),
IpAddr::V4(_) => S::new_icmp_send_socket_ipv4(raw),
IpAddr::V6(_) => S::new_icmp_send_socket_ipv6(),
}?)
}

/// Make a socket for sending `UDP` packets.
#[instrument]
fn make_udp_send_socket<S: Socket>(addr: IpAddr) -> TraceResult<S> {
fn make_udp_send_socket<S: Socket>(addr: IpAddr, raw: bool) -> TraceResult<S> {
Ok(match addr {
IpAddr::V4(_) => S::new_udp_send_socket_ipv4(),
IpAddr::V4(_) => S::new_udp_send_socket_ipv4(raw),
IpAddr::V6(_) => S::new_udp_send_socket_ipv6(),
}?)
}

/// Make a socket for receiving raw `ICMP` packets.
#[instrument]
fn make_recv_socket<S: Socket>(addr: IpAddr) -> TraceResult<S> {
fn make_recv_socket<S: Socket>(addr: IpAddr, raw: bool) -> TraceResult<S> {
Ok(match addr {
IpAddr::V4(ipv4addr) => S::new_recv_socket_ipv4(ipv4addr),
IpAddr::V4(ipv4addr) => S::new_recv_socket_ipv4(ipv4addr, raw),
IpAddr::V6(ipv6addr) => S::new_recv_socket_ipv6(ipv6addr),
}?)
}
Loading

0 comments on commit f077c49

Please sign in to comment.