Skip to content

Commit

Permalink
feat(core): NAT detection for IPv4/udp/dublin (#1104)
Browse files Browse the repository at this point in the history
  • Loading branch information
fujiapple852 committed Jul 22, 2024
1 parent 2bc00f9 commit 5d01adc
Show file tree
Hide file tree
Showing 13 changed files with 450 additions and 81 deletions.
2 changes: 1 addition & 1 deletion crates/trippy-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ pub use probe::{
Extension, Extensions, IcmpPacketType, MplsLabelStack, MplsLabelStackMember, Probe,
ProbeComplete, ProbeStatus, UnknownExtension,
};
pub use state::{Hop, State};
pub use state::{Hop, NatStatus, State};
pub use strategy::{CompletionReason, Round, Strategy};
pub use tracer::Tracer;
pub use types::{
Expand Down
1 change: 1 addition & 0 deletions crates/trippy-core/src/net/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ impl<S: Socket> Channel<S> {
&mut self.recv_socket,
self.protocol,
self.icmp_extension_mode,
self.payload_pattern,
),
IpAddr::V6(_) => ipv6::recv_icmp_probe(
&mut self.recv_socket,
Expand Down
170 changes: 143 additions & 27 deletions crates/trippy-core/src/net/ipv4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,12 +210,18 @@ pub fn recv_icmp_probe<S: Socket>(
recv_socket: &mut S,
protocol: Protocol,
icmp_extension_mode: IcmpExtensionParseMode,
payload_pattern: PayloadPattern,
) -> Result<Option<Response>> {
let mut buf = [0_u8; MAX_PACKET_SIZE];
match recv_socket.read(&mut buf) {
Ok(bytes_read) => {
let ipv4 = Ipv4Packet::new_view(&buf[..bytes_read])?;
Ok(extract_probe_resp(protocol, icmp_extension_mode, &ipv4)?)
Ok(extract_probe_resp(
protocol,
icmp_extension_mode,
payload_pattern,
&ipv4,
)?)
}
Err(err) => match err.kind() {
ErrorKind::WouldBlock => Ok(None),
Expand Down Expand Up @@ -303,6 +309,28 @@ fn make_udp_packet<'a>(
Ok(udp)
}

/// Calculate the expected checksum for a UDP packet.
pub fn calc_udp_checksum(
src_addr: Ipv4Addr,
dest_addr: Ipv4Addr,
src_port: Port,
dest_port: Port,
payload_size: u16,
payload_pattern: PayloadPattern,
) -> Result<u16> {
let mut udp_buf = [0_u8; MAX_UDP_PACKET_BUF];
let payload = &[payload_pattern.0; MAX_UDP_PAYLOAD_BUF][0..usize::from(payload_size)];
let udp = make_udp_packet(
&mut udp_buf,
src_addr,
dest_addr,
src_port.0,
dest_port.0,
payload,
)?;
Ok(udp.get_checksum())
}

/// Create an `Ipv4Packet`.
#[allow(clippy::too_many_arguments)]
fn make_ipv4_packet<'a>(
Expand Down Expand Up @@ -348,6 +376,7 @@ const fn udp_payload_size(packet_size: usize) -> usize {
fn extract_probe_resp(
protocol: Protocol,
icmp_extension_mode: IcmpExtensionParseMode,
payload_pattern: PayloadPattern,
ipv4: &Ipv4Packet<'_>,
) -> Result<Option<Response>> {
let recv = SystemTime::now();
Expand All @@ -370,7 +399,7 @@ fn extract_probe_resp(
(ipv4, None)
}
};
extract_probe_resp_seq(&nested_ipv4, protocol)?.map(|resp_seq| {
extract_probe_resp_seq(&nested_ipv4, protocol, payload_pattern)?.map(|resp_seq| {
Response::TimeExceeded(
ResponseData::new(recv, src, resp_seq),
IcmpPacketCode(icmp_code.0),
Expand All @@ -390,7 +419,7 @@ fn extract_probe_resp(
}
IcmpExtensionParseMode::Disabled => None,
};
extract_probe_resp_seq(&nested_ipv4, protocol)?.map(|resp_seq| {
extract_probe_resp_seq(&nested_ipv4, protocol, payload_pattern)?.map(|resp_seq| {
Response::DestinationUnreachable(
ResponseData::new(recv, src, resp_seq),
IcmpPacketCode(icmp_code.0),
Expand Down Expand Up @@ -419,6 +448,7 @@ fn extract_probe_resp(
fn extract_probe_resp_seq(
ipv4: &Ipv4Packet<'_>,
protocol: Protocol,
payload_pattern: PayloadPattern,
) -> Result<Option<ResponseSeq>> {
Ok(match (protocol, ipv4.get_protocol()) {
(Protocol::Icmp, IpProtocol::Icmp) => {
Expand All @@ -430,14 +460,23 @@ fn extract_probe_resp_seq(
)))
}
(Protocol::Udp, IpProtocol::Udp) => {
let (src_port, dest_port, checksum, identifier, payload_length) =
let (src_port, dest_port, actual_checksum, identifier, payload_length) =
extract_udp_packet(ipv4)?;
let expected_checksum = calc_udp_checksum(
ipv4.get_source(),
ipv4.get_destination(),
Port(src_port),
Port(dest_port),
payload_length,
payload_pattern,
)?;
Some(ResponseSeq::Udp(ResponseSeqUdp::new(
identifier,
IpAddr::V4(ipv4.get_destination()),
src_port,
dest_port,
checksum,
expected_checksum,
actual_checksum,
payload_length,
false,
)))
Expand Down Expand Up @@ -1036,6 +1075,7 @@ mod tests {
&mut mocket,
Protocol::Icmp,
IcmpExtensionParseMode::Disabled,
PayloadPattern(0x00),
)?
.unwrap();

Expand Down Expand Up @@ -1086,6 +1126,7 @@ mod tests {
&mut mocket,
Protocol::Icmp,
IcmpExtensionParseMode::Disabled,
PayloadPattern(0x00),
)?
.unwrap();

Expand Down Expand Up @@ -1135,6 +1176,7 @@ mod tests {
&mut mocket,
Protocol::Icmp,
IcmpExtensionParseMode::Disabled,
PayloadPattern(0x00),
)?
.unwrap();

Expand Down Expand Up @@ -1180,8 +1222,13 @@ mod tests {
.expect_read()
.times(1)
.returning(mocket_read!(expected_read_buf));
let resp =
recv_icmp_probe(&mut mocket, Protocol::Udp, IcmpExtensionParseMode::Disabled)?.unwrap();
let resp = recv_icmp_probe(
&mut mocket,
Protocol::Udp,
IcmpExtensionParseMode::Disabled,
PayloadPattern(0x00),
)?
.unwrap();

let Response::TimeExceeded(
ResponseData {
Expand All @@ -1192,7 +1239,8 @@ mod tests {
dest_addr,
src_port,
dest_port,
checksum,
expected_udp_checksum,
actual_udp_checksum,
payload_len,
has_magic,
}),
Expand All @@ -1212,7 +1260,8 @@ mod tests {
);
assert_eq!(31829, src_port);
assert_eq!(33030, dest_port);
assert_eq!(58571, checksum);
assert_eq!(58571, expected_udp_checksum);
assert_eq!(58571, actual_udp_checksum);
assert_eq!(56, payload_len);
assert!(!has_magic);
assert_eq!(IcmpPacketCode(0), icmp_code);
Expand All @@ -1238,8 +1287,13 @@ mod tests {
.expect_read()
.times(1)
.returning(mocket_read!(expected_read_buf));
let resp =
recv_icmp_probe(&mut mocket, Protocol::Udp, IcmpExtensionParseMode::Disabled)?.unwrap();
let resp = recv_icmp_probe(
&mut mocket,
Protocol::Udp,
IcmpExtensionParseMode::Disabled,
PayloadPattern(0x00),
)?
.unwrap();

let Response::DestinationUnreachable(
ResponseData {
Expand All @@ -1250,7 +1304,8 @@ mod tests {
dest_addr,
src_port,
dest_port,
checksum,
expected_udp_checksum,
actual_udp_checksum,
payload_len,
has_magic,
}),
Expand All @@ -1270,7 +1325,8 @@ mod tests {
);
assert_eq!(32779, src_port);
assert_eq!(33010, dest_port);
assert_eq!(10913, checksum);
assert_eq!(10913, expected_udp_checksum);
assert_eq!(10913, actual_udp_checksum);
assert_eq!(56, payload_len);
assert!(!has_magic);
assert_eq!(IcmpPacketCode(10), icmp_code);
Expand All @@ -1295,8 +1351,13 @@ mod tests {
.expect_read()
.times(1)
.returning(mocket_read!(expected_read_buf));
let resp =
recv_icmp_probe(&mut mocket, Protocol::Tcp, IcmpExtensionParseMode::Disabled)?.unwrap();
let resp = recv_icmp_probe(
&mut mocket,
Protocol::Tcp,
IcmpExtensionParseMode::Disabled,
PayloadPattern(0x00),
)?
.unwrap();

let Response::TimeExceeded(
ResponseData {
Expand Down Expand Up @@ -1347,8 +1408,13 @@ mod tests {
.expect_read()
.times(1)
.returning(mocket_read!(expected_read_buf));
let resp =
recv_icmp_probe(&mut mocket, Protocol::Tcp, IcmpExtensionParseMode::Disabled)?.unwrap();
let resp = recv_icmp_probe(
&mut mocket,
Protocol::Tcp,
IcmpExtensionParseMode::Disabled,
PayloadPattern(0x00),
)?
.unwrap();

let Response::DestinationUnreachable(
ResponseData {
Expand Down Expand Up @@ -1397,11 +1463,26 @@ mod tests {
.expect_read()
.times(3)
.returning(mocket_read!(expected_read_buf));
let resp = recv_icmp_probe(&mut mocket, Protocol::Icmp, IcmpExtensionParseMode::Enabled)?;
let resp = recv_icmp_probe(
&mut mocket,
Protocol::Icmp,
IcmpExtensionParseMode::Enabled,
PayloadPattern(0x00),
)?;
assert!(resp.is_some());
let resp = recv_icmp_probe(&mut mocket, Protocol::Udp, IcmpExtensionParseMode::Enabled)?;
let resp = recv_icmp_probe(
&mut mocket,
Protocol::Udp,
IcmpExtensionParseMode::Enabled,
PayloadPattern(0x00),
)?;
assert!(resp.is_none());
let resp = recv_icmp_probe(&mut mocket, Protocol::Tcp, IcmpExtensionParseMode::Enabled)?;
let resp = recv_icmp_probe(
&mut mocket,
Protocol::Tcp,
IcmpExtensionParseMode::Enabled,
PayloadPattern(0x00),
)?;
assert!(resp.is_none());
Ok(())
}
Expand All @@ -1424,11 +1505,26 @@ mod tests {
.expect_read()
.times(3)
.returning(mocket_read!(expected_read_buf));
let resp = recv_icmp_probe(&mut mocket, Protocol::Udp, IcmpExtensionParseMode::Enabled)?;
let resp = recv_icmp_probe(
&mut mocket,
Protocol::Udp,
IcmpExtensionParseMode::Enabled,
PayloadPattern(0x00),
)?;
assert!(resp.is_some());
let resp = recv_icmp_probe(&mut mocket, Protocol::Icmp, IcmpExtensionParseMode::Enabled)?;
let resp = recv_icmp_probe(
&mut mocket,
Protocol::Icmp,
IcmpExtensionParseMode::Enabled,
PayloadPattern(0x00),
)?;
assert!(resp.is_none());
let resp = recv_icmp_probe(&mut mocket, Protocol::Tcp, IcmpExtensionParseMode::Enabled)?;
let resp = recv_icmp_probe(
&mut mocket,
Protocol::Tcp,
IcmpExtensionParseMode::Enabled,
PayloadPattern(0x00),
)?;
assert!(resp.is_none());
Ok(())
}
Expand All @@ -1450,11 +1546,26 @@ mod tests {
.expect_read()
.times(3)
.returning(mocket_read!(expected_read_buf));
let resp = recv_icmp_probe(&mut mocket, Protocol::Tcp, IcmpExtensionParseMode::Enabled)?;
let resp = recv_icmp_probe(
&mut mocket,
Protocol::Tcp,
IcmpExtensionParseMode::Enabled,
PayloadPattern(0x00),
)?;
assert!(resp.is_some());
let resp = recv_icmp_probe(&mut mocket, Protocol::Icmp, IcmpExtensionParseMode::Enabled)?;
let resp = recv_icmp_probe(
&mut mocket,
Protocol::Icmp,
IcmpExtensionParseMode::Enabled,
PayloadPattern(0x00),
)?;
assert!(resp.is_none());
let resp = recv_icmp_probe(&mut mocket, Protocol::Udp, IcmpExtensionParseMode::Enabled)?;
let resp = recv_icmp_probe(
&mut mocket,
Protocol::Udp,
IcmpExtensionParseMode::Enabled,
PayloadPattern(0x00),
)?;
assert!(resp.is_none());
Ok(())
}
Expand Down Expand Up @@ -1584,7 +1695,12 @@ mod tests {
.expect_read()
.times(1)
.returning(mocket_read!(expected_read_buf));
let resp = recv_icmp_probe(&mut mocket, Protocol::Udp, IcmpExtensionParseMode::Enabled)?;
let resp = recv_icmp_probe(
&mut mocket,
Protocol::Udp,
IcmpExtensionParseMode::Enabled,
PayloadPattern(0x00),
)?;
assert!(resp.is_none());
Ok(())
}
Expand Down
Loading

0 comments on commit 5d01adc

Please sign in to comment.