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

refactor: move platform specific code to platform specific modules #337

Merged
merged 1 commit into from
Nov 4, 2022
Merged
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
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ name = "trip"

# Library dependencies
socket2 = { version = "0.4.7", features = [ "all" ] }
nix = { version = "0.25.0", default-features = false, features = [ "user", "poll", "net" ] }
thiserror = "1.0.32"
derive_more = "0.99.17"
arrayvec = "0.7.2"
Expand All @@ -46,5 +45,9 @@ comfy-table = "6.1.1"
[target.'cfg(target_os = "linux")'.dependencies]
caps = "0.5.4"

# Library dependancies (Unix)
[target.'cfg(target_family = "unix")'.dependencies]
nix = { version = "0.25.0", default-features = false, features = [ "user", "poll", "net" ] }

[dev-dependencies]
rand = "0.8.5"
55 changes: 11 additions & 44 deletions src/tracing/net/channel.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::tracing::error::TracerError::InvalidSourceAddr;
use crate::tracing::error::{TraceResult, TracerError};
use crate::tracing::net::platform::PlatformIpv4FieldByteOrder;
use crate::tracing::net::{ipv4, ipv6, Network};
use crate::tracing::net::{ipv4, ipv6, platform, Network};
use crate::tracing::probe::ProbeResponse;
use crate::tracing::types::{PacketSize, PayloadPattern, Port, Sequence, TraceId, TypeOfService};
use crate::tracing::util::Required;
Expand All @@ -10,11 +10,8 @@ use crate::tracing::{
};
use arrayvec::ArrayVec;
use itertools::Itertools;
use nix::sys::select::FdSet;
use nix::sys::time::{TimeVal, TimeValLike};
use socket2::{Domain, Protocol, SockAddr, Socket, Type};
use std::net::{IpAddr, SocketAddr};
use std::os::unix::io::AsRawFd;
use std::time::{Duration, SystemTime};

/// The maximum size of the IP packet we allow.
Expand Down Expand Up @@ -196,7 +193,7 @@ impl TracerChannel {

/// Generate a `ProbeResponse` for the next available ICMP packet, if any
fn recv_icmp_probe(&mut self) -> TraceResult<Option<ProbeResponse>> {
if is_readable(&self.recv_socket, self.read_timeout)? {
if platform::is_readable(&self.recv_socket, self.read_timeout)? {
match self.addr_family {
TracerAddrFamily::Ipv4 => ipv4::recv_icmp_probe(
&mut self.recv_socket,
Expand All @@ -222,7 +219,7 @@ impl TracerChannel {
let found_index = self
.tcp_probes
.iter()
.find_position(|&probe| is_writable(&probe.socket).unwrap_or_default())
.find_position(|&probe| platform::is_writable(&probe.socket).unwrap_or_default())
.map(|(i, _)| i);
if let Some(i) = found_index {
let probe = self.tcp_probes.remove(i);
Expand All @@ -249,36 +246,6 @@ impl TcpProbe {
}
}

/// Returns true if the socket becomes readable before the timeout, false otherwise.
fn is_readable(sock: &Socket, timeout: Duration) -> TraceResult<bool> {
let mut read = FdSet::new();
read.insert(sock.as_raw_fd());
let readable = nix::sys::select::select(
None,
Some(&mut read),
None,
None,
Some(&mut TimeVal::milliseconds(timeout.as_millis() as i64)),
)
.map_err(|err| TracerError::IoError(std::io::Error::from(err)))?;
Ok(readable == 1)
}

/// Returns true if the socket is currently writeable, false otherwise.
fn is_writable(sock: &Socket) -> TraceResult<bool> {
let mut write = FdSet::new();
write.insert(sock.as_raw_fd());
let writable = nix::sys::select::select(
None,
None,
Some(&mut write),
None,
Some(&mut TimeVal::zero()),
)
.map_err(|err| TracerError::IoError(std::io::Error::from(err)))?;
Ok(writable == 1)
}

/// Validate, Lookup or discover the source `IpAddr`.
fn make_src_addr(
source_addr: Option<IpAddr>,
Expand All @@ -302,8 +269,8 @@ fn make_src_addr(
/// Lookup the address for a named interface.
fn lookup_interface_addr(addr_family: TracerAddrFamily, name: &str) -> TraceResult<IpAddr> {
match addr_family {
TracerAddrFamily::Ipv4 => ipv4::lookup_interface_addr(name),
TracerAddrFamily::Ipv6 => ipv6::lookup_interface_addr(name),
TracerAddrFamily::Ipv4 => platform::lookup_interface_addr_ipv4(name),
TracerAddrFamily::Ipv6 => platform::lookup_interface_addr_ipv6(name),
}
}

Expand Down Expand Up @@ -341,23 +308,23 @@ fn udp_socket_for_addr_family(addr_family: TracerAddrFamily) -> TraceResult<Sock
/// Make a socket for sending raw `ICMP` packets.
fn make_icmp_send_socket(addr_family: TracerAddrFamily) -> TraceResult<Socket> {
match addr_family {
TracerAddrFamily::Ipv4 => ipv4::make_icmp_send_socket(),
TracerAddrFamily::Ipv6 => ipv6::make_icmp_send_socket(),
TracerAddrFamily::Ipv4 => platform::make_icmp_send_socket_ipv4(),
TracerAddrFamily::Ipv6 => platform::make_icmp_send_socket_ipv6(),
}
}

/// Make a socket for sending `UDP` packets.
fn make_udp_send_socket(addr_family: TracerAddrFamily) -> TraceResult<Socket> {
match addr_family {
TracerAddrFamily::Ipv4 => ipv4::make_udp_send_socket(),
TracerAddrFamily::Ipv6 => ipv6::make_udp_send_socket(),
TracerAddrFamily::Ipv4 => platform::make_udp_send_socket_ipv4(),
TracerAddrFamily::Ipv6 => platform::make_udp_send_socket_ipv6(),
}
}

/// Make a socket for receiving raw `ICMP` packets.
fn make_recv_socket(addr_family: TracerAddrFamily) -> TraceResult<Socket> {
match addr_family {
TracerAddrFamily::Ipv4 => ipv4::make_recv_socket(),
TracerAddrFamily::Ipv6 => ipv6::make_recv_socket(),
TracerAddrFamily::Ipv4 => platform::make_recv_socket_ipv4(),
TracerAddrFamily::Ipv6 => platform::make_recv_socket_ipv6(),
}
}
50 changes: 5 additions & 45 deletions src/tracing/net/ipv4.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::tracing::error::TracerError::AddressNotAvailable;
use crate::tracing::error::{TraceResult, TracerError};
use crate::tracing::net::channel::MAX_PACKET_SIZE;
use crate::tracing::net::platform;
use crate::tracing::net::platform::PlatformIpv4FieldByteOrder;
use crate::tracing::packet::checksum::{icmp_ipv4_checksum, udp_ipv4_checksum};
use crate::tracing::packet::icmpv4::destination_unreachable::DestinationUnreachablePacket;
Expand All @@ -16,9 +17,7 @@ use crate::tracing::probe::{ProbeResponse, ProbeResponseData, TcpProbeResponseDa
use crate::tracing::types::{PacketSize, PayloadPattern, Sequence, TraceId, TypeOfService};
use crate::tracing::util::Required;
use crate::tracing::{MultipathStrategy, PortDirection, Probe, TracerProtocol};
use nix::libc::IPPROTO_RAW;
use nix::sys::socket::{AddressFamily, SockaddrLike};
use socket2::{Domain, Protocol, SockAddr, Socket, Type};
use socket2::{SockAddr, Socket};
use std::io::{ErrorKind, Read};
use std::net::{IpAddr, Ipv4Addr, Shutdown, SocketAddr};
use std::time::SystemTime;
Expand All @@ -40,36 +39,6 @@ const MAX_ICMP_PAYLOAD_BUF: usize = MAX_ICMP_PACKET_BUF - IcmpPacket::minimum_pa
/// 0100 0000 0000 0000
const DONT_FRAGMENT: u16 = 0x4000;

pub fn lookup_interface_addr(name: &str) -> TraceResult<IpAddr> {
nix::ifaddrs::getifaddrs()
.map_err(|_| TracerError::UnknownInterface(name.to_string()))?
.into_iter()
.find_map(|ia| {
ia.address.and_then(|addr| match addr.family() {
Some(AddressFamily::Inet) if ia.interface_name == name => addr
.as_sockaddr_in()
.map(|sock_addr| IpAddr::V4(Ipv4Addr::from(sock_addr.ip()))),
_ => None,
})
})
.ok_or_else(|| TracerError::UnknownInterface(name.to_string()))
}

pub fn make_icmp_send_socket() -> TraceResult<Socket> {
make_raw_socket()
}

pub fn make_udp_send_socket() -> TraceResult<Socket> {
make_raw_socket()
}

pub fn make_recv_socket() -> TraceResult<Socket> {
let socket = Socket::new(Domain::IPV4, Type::RAW, Some(Protocol::ICMPV4))?;
socket.set_nonblocking(true)?;
socket.set_header_included(true)?;
Ok(socket)
}

#[allow(clippy::too_many_arguments)]
pub fn dispatch_icmp_probe(
icmp_send_socket: &mut Socket,
Expand Down Expand Up @@ -186,9 +155,7 @@ pub fn dispatch_tcp_probe(
PortDirection::FixedDest(dest_port) => (probe.sequence.0, dest_port.0),
PortDirection::FixedBoth(_, _) | PortDirection::None => unimplemented!(),
};
let socket = Socket::new(Domain::IPV4, Type::STREAM, Some(Protocol::TCP))?;
socket.set_nonblocking(true)?;
socket.set_reuse_port(true)?;
let socket = platform::make_stream_socket_ipv4()?;
let local_addr = SocketAddr::new(IpAddr::V4(src_addr), src_port);
socket.bind(&SockAddr::from(local_addr))?;
socket.set_ttl(u32::from(probe.ttl.0))?;
Expand All @@ -198,7 +165,7 @@ pub fn dispatch_tcp_probe(
Ok(_) => {}
Err(err) => {
if let Some(code) = err.raw_os_error() {
if nix::Error::from_i32(code) != nix::Error::EINPROGRESS {
if platform::is_in_progress_error(code) {
return match err.kind() {
ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable => {
Err(AddressNotAvailable(local_addr))
Expand Down Expand Up @@ -255,7 +222,7 @@ pub fn recv_tcp_socket(
}
Some(err) => {
if let Some(code) = err.raw_os_error() {
if nix::Error::from_i32(code) == nix::Error::ECONNREFUSED {
if platform::is_conn_refused_error(code) {
return Ok(Some(ProbeResponse::TcpRefused(TcpProbeResponseData::new(
SystemTime::now(),
dest_addr,
Expand All @@ -268,13 +235,6 @@ pub fn recv_tcp_socket(
Ok(None)
}

fn make_raw_socket() -> TraceResult<Socket> {
let socket = Socket::new(Domain::IPV4, Type::RAW, Some(Protocol::from(IPPROTO_RAW)))?;
socket.set_nonblocking(true)?;
socket.set_header_included(true)?;
Ok(socket)
}

/// Create an ICMP `EchoRequest` packet.
fn make_echo_request_icmp_packet(
icmp_buf: &mut [u8],
Expand Down
45 changes: 5 additions & 40 deletions src/tracing/net/ipv6.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::tracing::error::TracerError::AddressNotAvailable;
use crate::tracing::error::{TraceResult, TracerError};
use crate::tracing::net::channel::MAX_PACKET_SIZE;
use crate::tracing::net::platform;
use crate::tracing::packet::checksum::{icmp_ipv6_checksum, udp_ipv6_checksum};
use crate::tracing::packet::icmpv6::destination_unreachable::DestinationUnreachablePacket;
use crate::tracing::packet::icmpv6::echo_reply::EchoReplyPacket;
Expand All @@ -14,8 +15,7 @@ use crate::tracing::probe::{ProbeResponse, ProbeResponseData, TcpProbeResponseDa
use crate::tracing::types::{PacketSize, PayloadPattern, Sequence, TraceId};
use crate::tracing::util::Required;
use crate::tracing::{PortDirection, Probe, TracerProtocol};
use nix::sys::socket::{AddressFamily, SockaddrLike};
use socket2::{Domain, Protocol, SockAddr, Socket, Type};
use socket2::{SockAddr, Socket};
use std::io::ErrorKind;
use std::net::{IpAddr, Ipv6Addr, Shutdown, SocketAddr};
use std::time::SystemTime;
Expand All @@ -32,39 +32,6 @@ const MAX_ICMP_PACKET_BUF: usize = MAX_PACKET_SIZE - Ipv6Packet::minimum_packet_
/// The maximum size of ICMP payload we allow.
const MAX_ICMP_PAYLOAD_BUF: usize = MAX_ICMP_PACKET_BUF - IcmpPacket::minimum_packet_size();

pub fn lookup_interface_addr(name: &str) -> TraceResult<IpAddr> {
nix::ifaddrs::getifaddrs()
.map_err(|_| TracerError::UnknownInterface(name.to_string()))?
.into_iter()
.find_map(|ia| {
ia.address.and_then(|addr| match addr.family() {
Some(AddressFamily::Inet6) if ia.interface_name == name => addr
.as_sockaddr_in6()
.map(|sock_addr| IpAddr::V6(sock_addr.ip())),
_ => None,
})
})
.ok_or_else(|| TracerError::UnknownInterface(name.to_string()))
}

pub fn make_icmp_send_socket() -> TraceResult<Socket> {
let socket = Socket::new(Domain::IPV6, Type::RAW, Some(Protocol::ICMPV6))?;
socket.set_nonblocking(true)?;
Ok(socket)
}

pub fn make_udp_send_socket() -> TraceResult<Socket> {
let socket = Socket::new(Domain::IPV6, Type::RAW, Some(Protocol::UDP))?;
socket.set_nonblocking(true)?;
Ok(socket)
}

pub fn make_recv_socket() -> TraceResult<Socket> {
let socket = Socket::new(Domain::IPV6, Type::RAW, Some(Protocol::ICMPV6))?;
socket.set_nonblocking(true)?;
Ok(socket)
}

pub fn dispatch_icmp_probe(
icmp_send_socket: &mut Socket,
probe: Probe,
Expand Down Expand Up @@ -147,9 +114,7 @@ pub fn dispatch_tcp_probe(
PortDirection::FixedDest(dest_port) => (probe.sequence.0, dest_port.0),
PortDirection::FixedBoth(_, _) | PortDirection::None => unimplemented!(),
};
let socket = Socket::new(Domain::IPV6, Type::STREAM, Some(Protocol::TCP))?;
socket.set_nonblocking(true)?;
socket.set_reuse_port(true)?;
let socket = platform::make_stream_socket_ipv6()?;
let local_addr = SocketAddr::new(IpAddr::V6(src_addr), src_port);
socket.bind(&SockAddr::from(local_addr))?;
socket.set_unicast_hops_v6(u32::from(probe.ttl.0))?;
Expand All @@ -158,7 +123,7 @@ pub fn dispatch_tcp_probe(
Ok(_) => {}
Err(err) => {
if let Some(code) = err.raw_os_error() {
if nix::Error::from_i32(code) != nix::Error::EINPROGRESS {
if platform::is_in_progress_error(code) {
return match err.kind() {
ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable => {
Err(AddressNotAvailable(local_addr))
Expand Down Expand Up @@ -210,7 +175,7 @@ pub fn recv_tcp_socket(
}
Some(err) => {
if let Some(code) = err.raw_os_error() {
if nix::Error::from_i32(code) == nix::Error::ECONNREFUSED {
if platform::is_conn_refused_error(code) {
return Ok(Some(ProbeResponse::TcpRefused(TcpProbeResponseData::new(
SystemTime::now(),
dest_addr,
Expand Down
Loading