mirror of
https://github.com/dndx/phantun.git
synced 2025-08-29 03:17:26 +08:00
This fixes an issue when Phantun may choose a source IP different from the destination IP in the incoming packet. Closes #177
147 lines
4.7 KiB
Rust
147 lines
4.7 KiB
Rust
use neli::{
|
|
consts::{
|
|
nl::NlmF,
|
|
rtnl::{Ifa, IfaF, RtAddrFamily, RtScope, Rtm},
|
|
socket::NlFamily,
|
|
},
|
|
nl::{NlPayload, NlmsghdrBuilder},
|
|
rtnl::{IfaddrmsgBuilder, RtattrBuilder},
|
|
socket::synchronous::NlSocketHandle,
|
|
types::RtBuffer,
|
|
utils::Groups,
|
|
};
|
|
use nix::sys::socket::{CmsgIterator, ControlMessageOwned, SockaddrLike as _};
|
|
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
|
|
use tokio::net::UdpSocket;
|
|
|
|
pub fn new_udp_reuseport(local_addr: SocketAddr) -> UdpSocket {
|
|
let udp_sock = socket2::Socket::new(
|
|
if local_addr.is_ipv4() {
|
|
socket2::Domain::IPV4
|
|
} else {
|
|
socket2::Domain::IPV6
|
|
},
|
|
socket2::Type::DGRAM,
|
|
None,
|
|
)
|
|
.unwrap();
|
|
udp_sock.set_reuse_port(true).unwrap();
|
|
// from tokio-rs/mio/blob/master/src/sys/unix/net.rs
|
|
udp_sock.set_cloexec(true).unwrap();
|
|
udp_sock.set_nonblocking(true).unwrap();
|
|
|
|
// enable IP_PKTINFO/IPV6_PKTINFO delivery so we know the destination address of incoming
|
|
// packets
|
|
if local_addr.is_ipv4() {
|
|
nix::sys::socket::setsockopt(&udp_sock, nix::sys::socket::sockopt::Ipv4PacketInfo, &true)
|
|
.unwrap();
|
|
} else {
|
|
nix::sys::socket::setsockopt(
|
|
&udp_sock,
|
|
nix::sys::socket::sockopt::Ipv6RecvPacketInfo,
|
|
&true,
|
|
)
|
|
.unwrap();
|
|
}
|
|
|
|
udp_sock.bind(&socket2::SockAddr::from(local_addr)).unwrap();
|
|
let udp_sock: std::net::UdpSocket = udp_sock.into();
|
|
udp_sock.try_into().unwrap()
|
|
}
|
|
|
|
pub async fn udp_recv_pktinfo(
|
|
sock: &UdpSocket,
|
|
buf: &mut [u8],
|
|
) -> std::io::Result<(usize, std::net::SocketAddr, std::net::IpAddr)> {
|
|
use std::os::unix::io::AsRawFd;
|
|
use tokio::io::Interest;
|
|
use nix::sys::socket::cmsg_space;
|
|
|
|
sock.async_io(Interest::READABLE, || {
|
|
let control_buffer_size = std::cmp::max(
|
|
cmsg_space::<nix::libc::in_pktinfo>(),
|
|
cmsg_space::<nix::libc::in6_pktinfo>(),
|
|
);
|
|
let mut control_buffer = vec![0u8; control_buffer_size];
|
|
let iov = &mut [std::io::IoSliceMut::new(buf)];
|
|
let res = nix::sys::socket::recvmsg::<nix::sys::socket::SockaddrStorage>(
|
|
sock.as_raw_fd(),
|
|
iov,
|
|
Some(&mut control_buffer),
|
|
nix::sys::socket::MsgFlags::empty(),
|
|
)?;
|
|
|
|
let src_addr = res.address.expect("missing source address");
|
|
let src_addr: SocketAddr = {
|
|
if let Some(inaddr) = src_addr.as_sockaddr_in() {
|
|
SocketAddrV4::new(inaddr.ip(), inaddr.port()).into()
|
|
} else if let Some(in6addr) = src_addr.as_sockaddr_in6() {
|
|
SocketAddrV6::new(
|
|
in6addr.ip(),
|
|
in6addr.port(),
|
|
in6addr.flowinfo(),
|
|
in6addr.scope_id(),
|
|
)
|
|
.into()
|
|
} else {
|
|
panic!("unexpected source address family {:#?}", src_addr.family());
|
|
}
|
|
};
|
|
|
|
let dst_addr = dst_addr_from_cmsgs(res.cmsgs()?).expect("didn't receive pktinfo");
|
|
|
|
Ok((res.bytes, src_addr, dst_addr))
|
|
})
|
|
.await
|
|
}
|
|
|
|
fn dst_addr_from_cmsgs(cmsgs: CmsgIterator) -> Option<IpAddr> {
|
|
for cmsg in cmsgs {
|
|
if let ControlMessageOwned::Ipv4PacketInfo(pktinfo) = cmsg {
|
|
return Some(Ipv4Addr::from(pktinfo.ipi_addr.s_addr.to_ne_bytes()).into());
|
|
}
|
|
if let ControlMessageOwned::Ipv6PacketInfo(pktinfo) = cmsg {
|
|
return Some(Ipv6Addr::from(pktinfo.ipi6_addr.s6_addr).into());
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
pub fn assign_ipv6_address(device_name: &str, local: Ipv6Addr, peer: Ipv6Addr) {
|
|
let index = nix::net::if_::if_nametoindex(device_name).unwrap();
|
|
|
|
let rtnl = NlSocketHandle::connect(NlFamily::Route, None, Groups::empty()).unwrap();
|
|
let mut rtattrs = RtBuffer::new();
|
|
rtattrs.push(
|
|
RtattrBuilder::default()
|
|
.rta_type(Ifa::Local)
|
|
.rta_payload(&local.octets()[..])
|
|
.build()
|
|
.unwrap(),
|
|
);
|
|
rtattrs.push(
|
|
RtattrBuilder::default()
|
|
.rta_type(Ifa::Address)
|
|
.rta_payload(&peer.octets()[..])
|
|
.build()
|
|
.unwrap(),
|
|
);
|
|
|
|
let ifaddrmsg = IfaddrmsgBuilder::default()
|
|
.ifa_family(RtAddrFamily::Inet6)
|
|
.ifa_prefixlen(128)
|
|
.ifa_flags(IfaF::empty())
|
|
.ifa_scope(RtScope::Universe)
|
|
.ifa_index(index)
|
|
.rtattrs(rtattrs)
|
|
.build()
|
|
.unwrap();
|
|
let nl_header = NlmsghdrBuilder::default()
|
|
.nl_type(Rtm::Newaddr)
|
|
.nl_flags(NlmF::REQUEST)
|
|
.nl_payload(NlPayload::Payload(ifaddrmsg))
|
|
.build()
|
|
.unwrap();
|
|
rtnl.send(&nl_header).unwrap();
|
|
}
|