Skip to content

Commit

Permalink
fixup: some ipv6 stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
hulthe committed Jan 23, 2025
1 parent 1737025 commit fbe3d58
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 27 deletions.
2 changes: 1 addition & 1 deletion leak-checker/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ anyhow.workspace = true
socket2 = { workspace = true, features = ["all"] }
pnet_packet.workspace = true
pretty_env_logger = "0.5.0"
tokio = { workspace = true, features = ["macros", "time", "rt", "sync", "net"] }
tokio = { workspace = true, features = ["macros", "time", "rt", "sync", "net", "process"] }
futures.workspace = true
serde = { workspace = true, features = ["derive"] }
clap = { workspace = true, features = ["derive"] }
Expand Down
75 changes: 51 additions & 24 deletions leak-checker/src/traceroute/unix/linux.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,43 @@
use std::io::{self, IoSliceMut};
use std::os::fd::{AsRawFd, RawFd};
use std::{net::IpAddr, time::Duration};
use std::{
ffi::c_int,
io::{self, IoSliceMut},
net::IpAddr,
os::fd::{AsRawFd, RawFd},
time::Duration,
};

use anyhow::{anyhow, Context};
use nix::errno::Errno;
use nix::sys::socket::{
recvmsg, setsockopt, sockopt::Ipv4RecvErr, ControlMessageOwned, MsgFlags, SockaddrIn,
SockaddrIn6, SockaddrLike,
use nix::{
cmsg_space,
errno::Errno,
libc,
sys::socket::{
recvmsg, setsockopt,
sockopt::{Ipv4RecvErr, Ipv4Ttl, Ipv6RecvErr, Ipv6Ttl},
ControlMessageOwned, MsgFlags, SockaddrIn, SockaddrIn6, SockaddrLike,
},
};
use pnet_packet::{
icmp::{time_exceeded::IcmpCodes, IcmpCode, IcmpType, IcmpTypes},
icmpv6::{Icmpv6Code, Icmpv6Type, Icmpv6Types},
};
use nix::{cmsg_space, libc};
use pnet_packet::icmp::time_exceeded::IcmpCodes;
use pnet_packet::icmp::{IcmpCode, IcmpType, IcmpTypes};
use pnet_packet::icmpv6::{Icmpv6Code, Icmpv6Type, Icmpv6Types};
use socket2::Socket;
use tokio::time::{sleep, Instant};

use crate::traceroute::unix::parse_icmp_probe;
use crate::traceroute::{TracerouteOpt, RECV_GRACE_TIME};
use crate::{util::Ip, Interface, LeakInfo, LeakStatus};
use crate::{
traceroute::{unix::parse_icmp_probe, TracerouteOpt, RECV_GRACE_TIME},
util::Ip,
Interface, LeakInfo, LeakStatus,
};

use super::{AsyncIcmpSocket, Traceroute};

pub struct TracerouteLinux;

pub struct AsyncIcmpSocketImpl(tokio::net::UdpSocket);
pub struct AsyncIcmpSocketImpl {
ip_version: Ip,
inner: tokio::net::UdpSocket,
}

impl Traceroute for TracerouteLinux {
type AsyncIcmpSocket = AsyncIcmpSocketImpl;
Expand All @@ -38,34 +52,47 @@ impl Traceroute for TracerouteLinux {
}

impl AsyncIcmpSocket for AsyncIcmpSocketImpl {
fn from_socket2(socket: Socket) -> anyhow::Result<Self> {
fn from_socket2(socket: Socket, ip_version: Ip) -> anyhow::Result<Self> {
// IP_RECVERR tells Linux to pass any error packets received over ICMP to us through `recvmsg` control messages.
setsockopt(&socket, Ipv4RecvErr, &true).context("Failed to set IP_RECVERR")?;
match ip_version {
Ip::V4(_) => {
setsockopt(&socket, Ipv4RecvErr, &true).context("Failed to set IP_RECVERR")?
}
Ip::V6(_) => {
setsockopt(&socket, Ipv6RecvErr, &true).context("Failed to set IPV6_RECVERR")?
}
}

let std_socket = std::net::UdpSocket::from(socket);
let tokio_socket = tokio::net::UdpSocket::from_std(std_socket).unwrap();
Ok(AsyncIcmpSocketImpl(tokio_socket))
Ok(AsyncIcmpSocketImpl {
ip_version,
inner: tokio_socket,
})
}

fn set_ttl(&self, ttl: u32) -> anyhow::Result<()> {
self.0
.set_ttl(ttl)
.context("Failed to set TTL value for socket")
let ttl = ttl as c_int;
match self.ip_version {
Ip::V4(_) => setsockopt(&self.inner, Ipv4Ttl, &ttl),
Ip::V6(_) => setsockopt(&self.inner, Ipv6Ttl, &ttl),
}
.context("Failed to set TTL value for socket")
}

async fn send_to(&self, packet: &[u8], destination: impl Into<IpAddr>) -> io::Result<usize> {
self.0.send_to(packet, (destination.into(), 0)).await
self.inner.send_to(packet, (destination.into(), 0)).await
}

async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, IpAddr)> {
self.0
self.inner
.recv_from(buf)
.await
.map(|(n, source)| (n, source.ip()))
}

async fn recv_ttl_responses(&self, opt: &TracerouteOpt) -> anyhow::Result<LeakStatus> {
recv_ttl_responses(opt.destination, &opt.interface, &self.0).await
recv_ttl_responses(opt.destination, &opt.interface, &self.inner).await
}
}

Expand Down
4 changes: 2 additions & 2 deletions leak-checker/src/traceroute/unix/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ pub trait Traceroute {
}

pub trait AsyncIcmpSocket: Sized {
fn from_socket2(socket: socket2::Socket) -> anyhow::Result<Self>;
fn from_socket2(socket: socket2::Socket, ip_version: Ip) -> anyhow::Result<Self>;

fn set_ttl(&self, ttl: u32) -> anyhow::Result<()>;

Expand Down Expand Up @@ -94,7 +94,7 @@ pub async fn try_run_leak_test<Impl: Traceroute>(

Impl::bind_socket_to_interface(&icmp_socket, &opt.interface, ip_version)?;

let icmp_socket = Impl::AsyncIcmpSocket::from_socket2(icmp_socket)?;
let icmp_socket = Impl::AsyncIcmpSocket::from_socket2(icmp_socket, ip_version)?;

let send_probes = async {
if opt.icmp {
Expand Down
3 changes: 3 additions & 0 deletions leak-checker/src/traceroute/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ pub async fn traceroute_using_ping(opt: &TracerouteOpt) -> anyhow::Result<LeakSt
// No "TTL expired" means we did not receive any TimeExceeded replies.
return Ok(None);
}

// NOTE: for IPv6, ping outputs the incorrect address here.
// No way to work around that unfortunately.
let (ip, ..) = stdout
.split_once("Reply from ")
.and_then(|(.., s)| s.split_once(": TTL expired"))
Expand Down

0 comments on commit fbe3d58

Please sign in to comment.