Skip to content

Commit

Permalink
wip: add support for extension to dest_unreachable
Browse files Browse the repository at this point in the history
  • Loading branch information
fujiapple852 committed Oct 1, 2023
1 parent 501bbf1 commit d7b12a1
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 68 deletions.
128 changes: 66 additions & 62 deletions src/tracing/net/ipv4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ fn extract_probe_resp(
// we convert 24 00 to be 00 24 which is 36
// we then subtract 20 the Ipv4 header (20) and get 16
//
// we set set_header_included(true) on the ipv4 recv socket, so I guess macOS subtracts it?
// we set set_header_included(true) on the ipv4 recv socket, so I guess macOS subtracts it already?
// TODO how does this behave on other platforms?

let total_length = ipv4_byte_order.adjust_length(ipv4.get_total_length());
Expand All @@ -352,17 +352,20 @@ fn extract_probe_resp(
let payload = &ipv4.payload_new()[..total_length as usize];

let icmp_v4 = IcmpPacket::new_view(payload).req()?;

// TODO Maybe this would be a good to split the icmp extensions from the ICMP TE/DU message?

Ok(match icmp_v4.get_icmp_type() {
IcmpType::TimeExceeded => {
let packet = TimeExceededPacket::new_view(icmp_v4.packet()).req()?;
let resp_seq = extract_time_exceeded(&packet, protocol)?;
let resp_seq = extract_probe_resp_seq(packet.payload(), packet.get_length(), protocol)?;
Some(ProbeResponse::TimeExceeded(ProbeResponseData::new(
recv, src, resp_seq,
)))
}
IcmpType::DestinationUnreachable => {
let packet = DestinationUnreachablePacket::new_view(icmp_v4.packet()).req()?;
let resp_seq = extract_dest_unreachable(&packet, protocol)?;
let resp_seq = extract_probe_resp_seq(packet.payload(), packet.get_length(), protocol)?;
Some(ProbeResponse::DestinationUnreachable(
ProbeResponseData::new(recv, src, resp_seq),
))
Expand All @@ -384,98 +387,99 @@ fn extract_probe_resp(
}

#[instrument]
fn extract_time_exceeded(
time_exceeded_packet: &TimeExceededPacket<'_>,
protocol: TracerProtocol,
) -> TraceResult<ProbeResponseSeq> {
Ok(match protocol {
TracerProtocol::Icmp => {
// let payload = time_exceeded_packet.payload();
let echo_request = extract_echo_request2(time_exceeded_packet)?;
let identifier = echo_request.get_identifier();
let sequence = echo_request.get_sequence();
ProbeResponseSeq::Icmp(ProbeResponseSeqIcmp::new(identifier, sequence))
}
TracerProtocol::Udp => {
let (src_port, dest_port, checksum, identifier) =
extract_udp_packet(time_exceeded_packet.payload())?;
ProbeResponseSeq::Udp(ProbeResponseSeqUdp::new(
identifier, src_port, dest_port, checksum,
))
}
TracerProtocol::Tcp => {
let (src_port, dest_port) = extract_tcp_packet(time_exceeded_packet.payload())?;
ProbeResponseSeq::Tcp(ProbeResponseSeqTcp::new(src_port, dest_port))
}
})
}

#[instrument]
fn extract_dest_unreachable(
dest_unreachable_packet: &DestinationUnreachablePacket<'_>,
fn extract_probe_resp_seq(
icmp_payload: &[u8],
rfc4884_length: u8,
protocol: TracerProtocol,
) -> TraceResult<ProbeResponseSeq> {
Ok(match protocol {
TracerProtocol::Icmp => {
let echo_request = extract_echo_request(dest_unreachable_packet.payload())?;
let echo_request = extract_echo_request(icmp_payload, rfc4884_length)?;
let identifier = echo_request.get_identifier();
let sequence = echo_request.get_sequence();
ProbeResponseSeq::Icmp(ProbeResponseSeqIcmp::new(identifier, sequence))
}
TracerProtocol::Udp => {
let (src_port, dest_port, checksum, identifier) =
extract_udp_packet(dest_unreachable_packet.payload())?;
let (src_port, dest_port, checksum, identifier) = extract_udp_packet(icmp_payload)?;
ProbeResponseSeq::Udp(ProbeResponseSeqUdp::new(
identifier, src_port, dest_port, checksum,
))
}
TracerProtocol::Tcp => {
let (src_port, dest_port) = extract_tcp_packet(dest_unreachable_packet.payload())?;
let (src_port, dest_port) = extract_tcp_packet(icmp_payload)?;
ProbeResponseSeq::Tcp(ProbeResponseSeqTcp::new(src_port, dest_port))
}
})
}

#[instrument]
fn extract_echo_request(payload: &[u8]) -> TraceResult<EchoRequestPacket<'_>> {
let ip4 = Ipv4Packet::new_view(payload).req()?;
let header_len = usize::from(ip4.get_header_length() * 4);
let nested_icmp = &payload[header_len..];
let nested_echo = EchoRequestPacket::new_view(nested_icmp).req()?;
Ok(nested_echo)
}
// #[instrument]
// fn extract_dest_unreachable(
// icmp_payload: &[u8],
// rfc4884_length: u8,
// protocol: TracerProtocol,
// ) -> TraceResult<ProbeResponseSeq> {
// Ok(match protocol {
// TracerProtocol::Icmp => {
// let echo_request = extract_echo_request(icmp_payload, rfc4884_length)?;
// let identifier = echo_request.get_identifier();
// let sequence = echo_request.get_sequence();
// ProbeResponseSeq::Icmp(ProbeResponseSeqIcmp::new(identifier, sequence))
// }
// TracerProtocol::Udp => {
// let (src_port, dest_port, checksum, identifier) =
// extract_udp_packet(icmp_payload)?;
// ProbeResponseSeq::Udp(ProbeResponseSeqUdp::new(
// identifier, src_port, dest_port, checksum,
// ))
// }
// TracerProtocol::Tcp => {
// let (src_port, dest_port) = extract_tcp_packet(icmp_payload)?;
// ProbeResponseSeq::Tcp(ProbeResponseSeqTcp::new(src_port, dest_port))
// }
// })
// }

// #[instrument]
// fn extract_echo_request(payload: &[u8]) -> TraceResult<EchoRequestPacket<'_>> {
// let ip4 = Ipv4Packet::new_view(payload).req()?;
// let header_len = usize::from(ip4.get_header_length() * 4);
// let nested_icmp = &payload[header_len..];
// let nested_echo = EchoRequestPacket::new_view(nested_icmp).req()?;
// Ok(nested_echo)
// }

// TODO the logic to extract ICMP extensions (if any...) shouldn't be in here
// probably need a method to split a TE or DestUnreach ICMP message into nested payload + option<extension> bytes
#[instrument]
fn extract_echo_request2<'a>(
time_exceeded_packet: &'a TimeExceededPacket<'a>,
) -> TraceResult<EchoRequestPacket<'a>> {
let te_payload = time_exceeded_packet.payload();

fn extract_echo_request(
icmp_payload: &[u8],
rfc4884_length: u8,
) -> TraceResult<EchoRequestPacket<'_>> {
let nested_ipv4_header_length = {
let nested_ip4 = Ipv4Packet::new_view(te_payload).expect("foo");
let nested_ip4 = Ipv4Packet::new_view(icmp_payload).expect("foo");
usize::from(nested_ip4.get_header_length() * 4)
};

// "The length attribute represents length of the padded "original datagram" field, measured in 32-bit words."
let orig_datagram_length = usize::from(time_exceeded_packet.get_length() * 4);
let orig_datagram_length = usize::from(rfc4884_length * 4);

if orig_datagram_length > 0 {
// compliant message
println!("compliant message");

println!("te_packet={}\n", fmt_payload(te_payload));
println!("te_packet={}\n", fmt_payload(icmp_payload));
println!("orig_datagram_length={}", orig_datagram_length);

if te_payload.len() > orig_datagram_length {
if icmp_payload.len() > orig_datagram_length {
// extension case (untested): the te_payload is longer than the orig_datagram and so whatever remains must be an extension
let extension_len = te_payload.len() - orig_datagram_length;
let extension_len = icmp_payload.len() - orig_datagram_length;
println!("extension_len={}", extension_len);
let extension = &te_payload[orig_datagram_length..orig_datagram_length + extension_len];
let extension =
&icmp_payload[orig_datagram_length..orig_datagram_length + extension_len];
println!("extension={}\n", fmt_payload(extension));
}
Ok(EchoRequestPacket::new_view(
&te_payload[nested_ipv4_header_length..orig_datagram_length],
&icmp_payload[nested_ipv4_header_length..orig_datagram_length],
)
.req()?)

Expand All @@ -484,25 +488,25 @@ fn extract_echo_request2<'a>(
// not specify a length attribute, it will parse for a valid extension
// header at a fixed location, assuming a 128-octet "original datagram"
// field."
} else if orig_datagram_length == 0 && te_payload.len() > 128 {
} else if orig_datagram_length == 0 && icmp_payload.len() > 128 {
// extension present, non-compliant message
println!("extension present, non-compliant message");
println!("te_packet={}\n", fmt_payload(te_payload));
println!("te_packet={}\n", fmt_payload(icmp_payload));

let extension_len = te_payload.len() - 128;
let extension_len = icmp_payload.len() - 128;
println!("extension_len={}", extension_len);

let extension = &te_payload[128..128 + extension_len];
let extension = &icmp_payload[128..128 + extension_len];
println!("extension={}\n", fmt_payload(extension));

Ok(EchoRequestPacket::new_view(
&te_payload[nested_ipv4_header_length..te_payload.len() - extension_len],
&icmp_payload[nested_ipv4_header_length..icmp_payload.len() - extension_len],
)
.req()?)
} else {
// no extension present
println!("no extension present");
Ok(EchoRequestPacket::new_view(&te_payload[nested_ipv4_header_length..]).req()?)
Ok(EchoRequestPacket::new_view(&icmp_payload[nested_ipv4_header_length..]).req()?)
}
}

Expand Down
25 changes: 19 additions & 6 deletions src/tracing/packet/icmpv4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,7 @@ pub mod time_exceeded {
.field("icmp_type", &self.get_icmp_type())
.field("icmp_code", &self.get_icmp_code())
.field("checksum", &self.get_checksum())
.field("length", &self.get_length())
.field("payload", &fmt_payload(self.payload()))
.finish()
}
Expand Down Expand Up @@ -815,7 +816,8 @@ pub mod destination_unreachable {
const TYPE_OFFSET: usize = 0;
const CODE_OFFSET: usize = 1;
const CHECKSUM_OFFSET: usize = 2;
const UNUSED_OFFSET: usize = 4;
const LENGTH_OFFSET: usize = 5;
// const UNUSED_OFFSET: usize = 4; // TODO
const NEXT_HOP_MTU_OFFSET: usize = 6;

/// Represents an ICMP `DestinationUnreachable` packet.
Expand Down Expand Up @@ -869,9 +871,15 @@ pub mod destination_unreachable {
u16::from_be_bytes(self.buf.get_bytes(CHECKSUM_OFFSET))
}

// TODO
// #[must_use]
// pub fn get_unused(&self) -> u16 {
// u16::from_be_bytes(self.buf.get_bytes(UNUSED_OFFSET))
// }

#[must_use]
pub fn get_unused(&self) -> u16 {
u16::from_be_bytes(self.buf.get_bytes(UNUSED_OFFSET))
pub fn get_length(&self) -> u8 {
self.buf.read(LENGTH_OFFSET)
}

#[must_use]
Expand All @@ -891,8 +899,13 @@ pub mod destination_unreachable {
self.buf.set_bytes(CHECKSUM_OFFSET, val.to_be_bytes());
}

pub fn set_unused(&mut self, val: u16) {
self.buf.set_bytes(UNUSED_OFFSET, val.to_be_bytes());
// TODO
// pub fn set_unused(&mut self, val: u16) {
// self.buf.set_bytes(UNUSED_OFFSET, val.to_be_bytes());
// }

pub fn set_length(&mut self, val: u8) {
*self.buf.write(LENGTH_OFFSET) = val;
}

pub fn set_next_hop_mtu(&mut self, val: u16) {
Expand Down Expand Up @@ -922,7 +935,7 @@ pub mod destination_unreachable {
.field("icmp_type", &self.get_icmp_type())
.field("icmp_code", &self.get_icmp_code())
.field("checksum", &self.get_checksum())
.field("unused", &self.get_unused())
.field("length", &self.get_length())
.field("next_hop_mtu", &self.get_next_hop_mtu())
.field("payload", &fmt_payload(self.payload()))
.finish()
Expand Down

0 comments on commit d7b12a1

Please sign in to comment.