Skip to content

Commit

Permalink
feat: add support for paris ecmp strategy for ipv4/udp (#542)
Browse files Browse the repository at this point in the history
  • Loading branch information
fujiapple852 committed May 5, 2023
1 parent 685d479 commit 1202e2a
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 19 deletions.
16 changes: 12 additions & 4 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1649,9 +1649,12 @@ impl TryFrom<(Args, u16)> for TrippyConfig {
};
let multipath_strategy = match (multipath_strategy_cfg, addr_family) {
(MultipathStrategyConfig::Classic, _) => Ok(MultipathStrategy::Classic),
(MultipathStrategyConfig::Paris, _) => {
Err(anyhow!("Paris multipath strategy not implemented yet!"))
(MultipathStrategyConfig::Paris, TracerAddrFamily::Ipv4) => {
Ok(MultipathStrategy::Paris)
}
(MultipathStrategyConfig::Paris, TracerAddrFamily::Ipv6) => Err(anyhow!(
"Paris multipath strategy not implemented for IPv6 yet!"
)),
(MultipathStrategyConfig::Dublin, TracerAddrFamily::Ipv4) => {
Ok(MultipathStrategy::Dublin)
}
Expand All @@ -1669,13 +1672,18 @@ impl TryFrom<(Args, u16)> for TrippyConfig {
(TracerProtocol::Tcp, None, None, _) => PortDirection::new_fixed_dest(80),
(TracerProtocol::Tcp, Some(src), None, _) => PortDirection::new_fixed_src(src),
(_, None, Some(dest), _) => PortDirection::new_fixed_dest(dest),
(TracerProtocol::Udp, Some(src), Some(dest), MultipathStrategyConfig::Dublin) => {
(
TracerProtocol::Udp,
Some(src),
Some(dest),
MultipathStrategyConfig::Dublin | MultipathStrategyConfig::Paris,
) => {
validate_source_port(src)?;
PortDirection::new_fixed_both(src, dest)
}
(_, Some(_), Some(_), _) => {
return Err(anyhow!(
"only one of source-port and target-port may be fixed (except IPv4/udp protocol with dublin strategy)"
"only one of source-port and target-port may be fixed (except IPv4/udp protocol with dublin or paris strategy)"
));
}
};
Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ fn make_channel_config(
target_addr,
args.packet_size,
args.payload_pattern,
args.multipath_strategy,
args.tos,
args.read_timeout,
args.min_round_duration,
Expand Down
6 changes: 5 additions & 1 deletion src/tracing/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,13 @@ impl Display for TracerProtocol {
}

/// The [Equal-cost Multi-Path](https://en.wikipedia.org/wiki/Equal-cost_multi-path_routing) routing strategy.
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
pub enum MultipathStrategy {
/// The src or dest port is used to store the sequence number.
///
/// This does _not_ allow fixing both the src and dest port and so `PortDirection::Both` and `SequenceField::Port`
/// are mutually exclusive.
#[default]
Classic,
/// The UDP `checksum` field is used to store the sequence number.
///
Expand Down Expand Up @@ -152,6 +153,7 @@ pub struct TracerChannelConfig {
pub target_addr: IpAddr,
pub packet_size: PacketSize,
pub payload_pattern: PayloadPattern,
pub multipath_strategy: MultipathStrategy,
pub tos: TypeOfService,
pub read_timeout: Duration,
pub tcp_connect_timeout: Duration,
Expand All @@ -167,6 +169,7 @@ impl TracerChannelConfig {
target_addr: IpAddr,
packet_size: u16,
payload_pattern: u8,
multipath_strategy: MultipathStrategy,
tos: u8,
read_timeout: Duration,
tcp_connect_timeout: Duration,
Expand All @@ -178,6 +181,7 @@ impl TracerChannelConfig {
target_addr,
packet_size: PacketSize(packet_size),
payload_pattern: PayloadPattern(payload_pattern),
multipath_strategy,
tos: TypeOfService(tos),
read_timeout,
tcp_connect_timeout,
Expand Down
5 changes: 4 additions & 1 deletion src/tracing/net/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::tracing::net::socket::TracerSocket as _;
use crate::tracing::net::{ipv4, ipv6, platform, Network};
use crate::tracing::probe::ProbeResponse;
use crate::tracing::types::{PacketSize, PayloadPattern, Sequence, TypeOfService};
use crate::tracing::{Probe, TracerChannelConfig, TracerProtocol};
use crate::tracing::{MultipathStrategy, Probe, TracerChannelConfig, TracerProtocol};
use arrayvec::ArrayVec;
use itertools::Itertools;
use std::net::IpAddr;
Expand All @@ -24,6 +24,7 @@ pub struct TracerChannel {
dest_addr: IpAddr,
packet_size: PacketSize,
payload_pattern: PayloadPattern,
multipath_strategy: MultipathStrategy,
tos: TypeOfService,
read_timeout: Duration,
tcp_connect_timeout: Duration,
Expand Down Expand Up @@ -56,6 +57,7 @@ impl TracerChannel {
dest_addr: config.target_addr,
packet_size: config.packet_size,
payload_pattern: config.payload_pattern,
multipath_strategy: config.multipath_strategy,
tos: config.tos,
read_timeout: config.read_timeout,
tcp_connect_timeout: config.tcp_connect_timeout,
Expand Down Expand Up @@ -122,6 +124,7 @@ impl TracerChannel {
dest_addr,
self.packet_size,
self.payload_pattern,
self.multipath_strategy,
self.ipv4_length_order,
),
(IpAddr::V6(src_addr), IpAddr::V6(dest_addr)) => ipv6::dispatch_udp_probe(
Expand Down
44 changes: 32 additions & 12 deletions src/tracing/net/ipv4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use crate::tracing::probe::{
};
use crate::tracing::types::{PacketSize, PayloadPattern, Sequence, TraceId, TypeOfService};
use crate::tracing::util::Required;
use crate::tracing::{Probe, TracerProtocol};
use crate::tracing::{MultipathStrategy, Probe, TracerProtocol};
use std::io::ErrorKind;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::time::SystemTime;
Expand Down Expand Up @@ -88,6 +88,7 @@ pub fn dispatch_udp_probe(
dest_addr: Ipv4Addr,
packet_size: PacketSize,
payload_pattern: PayloadPattern,
multipath_strategy: MultipathStrategy,
ipv4_byte_order: platform::PlatformIpv4FieldByteOrder,
) -> TraceResult<()> {
let mut ipv4_buf = [0_u8; MAX_PACKET_SIZE];
Expand All @@ -96,15 +97,26 @@ pub fn dispatch_udp_probe(
if packet_size > MAX_PACKET_SIZE {
return Err(TracerError::InvalidPacketSize(packet_size));
}
let udp = make_udp_packet(
let mut payload_buffer = [0_u8; MAX_UDP_PAYLOAD_BUF];
let payload = if multipath_strategy == MultipathStrategy::Paris {
payload_buffer.as_mut_slice()[0..2].copy_from_slice(&probe.sequence.0.to_be_bytes());
&payload_buffer[..2]
} else {
let payload_size = udp_payload_size(packet_size);
payload_buffer.as_mut_slice()[0..payload_size].fill(payload_pattern.0);
&payload_buffer[..payload_size]
};
let mut udp = make_udp_packet(
&mut udp_buf,
src_addr,
dest_addr,
probe.src_port.0,
probe.dest_port.0,
udp_payload_size(packet_size),
payload_pattern,
payload,
)?;
if matches!(multipath_strategy, MultipathStrategy::Paris) {
swap_checksum_and_payload(&mut udp);
}
let ipv4 = make_ipv4_packet(
&mut ipv4_buf,
ipv4_byte_order,
Expand All @@ -120,6 +132,16 @@ pub fn dispatch_udp_probe(
Ok(())
}

/// Swap the checksum and payload values.
///
/// Assumes that the payload is 2 bytes in length.
fn swap_checksum_and_payload(udp: &mut UdpPacket<'_>) {
let checksum = udp.get_checksum().to_be_bytes();
let payload = u16::from_be_bytes(core::array::from_fn(|i| udp.payload()[i]));
udp.set_checksum(payload);
udp.set_payload(&checksum);
}

pub fn dispatch_tcp_probe(
probe: Probe,
src_addr: Ipv4Addr,
Expand Down Expand Up @@ -234,22 +256,20 @@ fn make_echo_request_icmp_packet(
}

/// Create a `UdpPacket`
fn make_udp_packet(
udp_buf: &mut [u8],
fn make_udp_packet<'a>(
udp_buf: &'a mut [u8],
src_addr: Ipv4Addr,
dest_addr: Ipv4Addr,
src_port: u16,
dest_port: u16,
payload_size: usize,
payload_pattern: PayloadPattern,
) -> TraceResult<UdpPacket<'_>> {
let udp_payload_buf = [payload_pattern.0; MAX_UDP_PAYLOAD_BUF];
let udp_packet_size = UdpPacket::minimum_packet_size() + payload_size;
payload: &'_ [u8],
) -> TraceResult<UdpPacket<'a>> {
let udp_packet_size = UdpPacket::minimum_packet_size() + payload.len();
let mut udp = UdpPacket::new(&mut udp_buf[..udp_packet_size]).req()?;
udp.set_source(src_port);
udp.set_destination(dest_port);
udp.set_length(udp_packet_size as u16);
udp.set_payload(&udp_payload_buf[..payload_size]);
udp.set_payload(payload);
udp.set_checksum(udp_ipv4_checksum(udp.packet(), src_addr, dest_addr));
Ok(udp)
}
Expand Down
1 change: 1 addition & 0 deletions src/tracing/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ pub struct Probe {
}

impl Probe {
#[allow(clippy::too_many_arguments)]
#[must_use]
pub const fn new(
sequence: Sequence,
Expand Down
10 changes: 9 additions & 1 deletion src/tracing/tracer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,15 @@ mod state {
unimplemented!()
}
},
MultipathStrategy::Paris => unimplemented!(),
MultipathStrategy::Paris => {
match self.config.port_direction {
PortDirection::FixedBoth(src_port, dest_port) => {
(Port(src_port.0), Port(dest_port.0), TraceId(0))
}
// TODO
_ => panic!(),
}
}
MultipathStrategy::Dublin => {
let round_port = ((self.config.initial_sequence.0 as usize + self.round.0)
% usize::from(u16::MAX)) as u16;
Expand Down

0 comments on commit 1202e2a

Please sign in to comment.