use crate::tracing::error::TraceResult;
use std::net::IpAddr;
#[cfg(all(unix, not(target_os = "linux")))]
const TEST_PACKET_LENGTH: u16 = 256;
#[derive(Debug, Copy, Clone)]
pub enum PlatformIpv4FieldByteOrder {
#[cfg(all(unix, not(target_os = "linux")))]
Host,
Network,
}
impl PlatformIpv4FieldByteOrder {
#[cfg(all(unix, not(target_os = "linux")))]
pub fn for_address(addr: IpAddr) -> TraceResult<Self> {
use crate::tracing::error::TracerError;
let addr = match addr {
IpAddr::V4(addr) => addr,
IpAddr::V6(_) => return Ok(Self::Network),
};
match test_send_local_ip4_packet(addr, TEST_PACKET_LENGTH) {
Ok(_) => Ok(Self::Network),
Err(TracerError::IoError(io)) if io.kind() == std::io::ErrorKind::InvalidInput => {
match test_send_local_ip4_packet(addr, TEST_PACKET_LENGTH.swap_bytes()) {
Ok(_) => Ok(Self::Host),
Err(err) => Err(err),
}
}
Err(err) => Err(err),
}
}
#[cfg(target_os = "linux")]
#[allow(clippy::unnecessary_wraps)]
pub fn for_address(_src_addr: IpAddr) -> TraceResult<Self> {
Ok(Self::Network)
}
pub fn adjust_length(self, ipv4_total_length: u16) -> u16 {
match self {
#[cfg(all(unix, not(target_os = "linux")))]
Self::Host => ipv4_total_length.swap_bytes(),
Self::Network => ipv4_total_length,
}
}
}
#[cfg(all(unix, not(target_os = "linux")))]
fn test_send_local_ip4_packet(
src_addr: std::net::Ipv4Addr,
total_length: u16,
) -> TraceResult<usize> {
use crate::tracing::util::Required;
let mut buf = [0_u8; TEST_PACKET_LENGTH as usize];
let mut ipv4 = crate::tracing::packet::ipv4::Ipv4Packet::new(&mut buf).req()?;
ipv4.set_version(4);
ipv4.set_header_length(5);
ipv4.set_protocol(crate::tracing::packet::IpProtocol::Icmp);
ipv4.set_ttl(255);
ipv4.set_source(src_addr);
ipv4.set_destination(std::net::Ipv4Addr::LOCALHOST);
ipv4.set_total_length(total_length);
let probe_socket = socket2::Socket::new(
socket2::Domain::IPV4,
socket2::Type::RAW,
Some(socket2::Protocol::from(nix::libc::IPPROTO_RAW)),
)?;
probe_socket.set_header_included(true)?;
let remote_addr = std::net::SocketAddr::new(IpAddr::V4(std::net::Ipv4Addr::LOCALHOST), 0);
Ok(probe_socket.send_to(ipv4.packet(), &socket2::SockAddr::from(remote_addr))?)
}