Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions net_gen/src/ipv6.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright © 2025 Cloud Hypervisor Authors
//
// SPDX-License-Identifier: Apache-2.0

// bindgen /usr/include/linux/ipv6.h --no-layout-tests --constified-enum '*' --allowlist-type 'sockaddr_in6|in6_ifreq'

/* automatically generated by rust-bindgen 0.71.1 */

pub type __u8 = ::std::os::raw::c_uchar;
pub type __u16 = ::std::os::raw::c_ushort;
pub type __u32 = ::std::os::raw::c_uint;
pub type __be16 = __u16;
pub type __be32 = __u32;
#[repr(C)]
#[derive(Copy, Clone)]
pub struct in6_addr {
pub in6_u: in6_addr__bindgen_ty_1,
}
#[repr(C)]
#[derive(Copy, Clone)]
pub union in6_addr__bindgen_ty_1 {
pub u6_addr8: [__u8; 16usize],
pub u6_addr16: [__be16; 8usize],
pub u6_addr32: [__be32; 4usize],
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct sockaddr_in6 {
pub sin6_family: ::std::os::raw::c_ushort,
pub sin6_port: __be16,
pub sin6_flowinfo: __be32,
pub sin6_addr: in6_addr,
pub sin6_scope_id: __u32,
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct in6_ifreq {
pub ifr6_addr: in6_addr,
pub ifr6_prefixlen: __u32,
pub ifr6_ifindex: ::std::os::raw::c_int,
}
4 changes: 4 additions & 0 deletions net_gen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ pub mod if_tun;
// --constified-enum '*' --with-derive-default
// Name is "inn" to avoid conflicting with "in" keyword.
pub mod inn;
// generated with bindgen /usr/include/linux/ipv6.h --no-layout-tests --constified-enum '*'
// --allowlist-type 'sockaddr_in6|in6_ifreq'
pub mod ipv6;
// generated with bindgen /usr/include/linux/sockios.h --no-unstable-rust
// --constified-enum '*' --with-derive-default
pub mod sockios;
Expand All @@ -35,6 +38,7 @@ pub use if_tun::{
};
pub use iff::{ifreq, net_device_flags_IFF_UP, setsockopt, sockaddr, AF_INET};
pub use inn::sockaddr_in;
pub use ipv6::{in6_ifreq, sockaddr_in6};

pub const TUNTAP: ::std::os::raw::c_uint = 84;

Expand Down
10 changes: 8 additions & 2 deletions net_util/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ mod queue_pair;
mod tap;

use std::io::Error as IoError;
use std::net::IpAddr;
use std::os::raw::c_uint;
use std::os::unix::io::{FromRawFd, RawFd};
use std::{io, mem, net};
Expand Down Expand Up @@ -76,9 +77,14 @@ fn create_sockaddr(ip_addr: net::Ipv4Addr) -> net_gen::sockaddr {
unsafe { mem::transmute(addr_in) }
}

fn create_inet_socket() -> Result<net::UdpSocket> {
fn create_inet_socket(addr: IpAddr) -> Result<net::UdpSocket> {
let domain = match addr {
IpAddr::V4(_) => libc::AF_INET,
IpAddr::V6(_) => libc::AF_INET6,
};

// SAFETY: we check the return value.
let sock = unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) };
let sock = unsafe { libc::socket(domain, libc::SOCK_DGRAM, 0) };
if sock < 0 {
return Err(Error::CreateSocket(IoError::last_os_error()));
}
Expand Down
18 changes: 7 additions & 11 deletions net_util/src/open_tap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause

use std::net::Ipv4Addr;
use std::net::IpAddr;
use std::path::Path;
use std::{fs, io};

Expand All @@ -22,10 +22,8 @@ pub enum Error {
ReadSysfsTunFlags(io::Error),
#[error("Open tap device failed: {0}")]
TapOpen(TapError),
#[error("Setting tap IP failed: {0}")]
TapSetIp(TapError),
#[error("Setting tap netmask failed: {0}")]
TapSetNetmask(TapError),
#[error("Setting tap IP and/or netmask failed: {0}")]
TapSetIpNetmask(TapError),
#[error("Setting MAC address failed: {0}")]
TapSetMac(TapError),
#[error("Getting MAC address failed: {0}")]
Expand Down Expand Up @@ -64,8 +62,8 @@ fn check_mq_support(if_name: &Option<&str>, queue_pairs: usize) -> Result<()> {
/// netmask.
pub fn open_tap(
if_name: Option<&str>,
ip_addr: Option<Ipv4Addr>,
netmask: Option<Ipv4Addr>,
ip_addr: Option<IpAddr>,
netmask: Option<IpAddr>,
host_mac: &mut Option<MacAddr>,
mtu: Option<u16>,
num_rx_q: usize,
Expand Down Expand Up @@ -94,10 +92,8 @@ pub fn open_tap(
// Don't overwrite ip configuration of existing interfaces:
if !tap_existed {
if let Some(ip) = ip_addr {
tap.set_ip_addr(ip).map_err(Error::TapSetIp)?;
}
if let Some(mask) = netmask {
tap.set_netmask(mask).map_err(Error::TapSetNetmask)?;
tap.set_ip_addr(ip, netmask)
.map_err(Error::TapSetIpNetmask)?;
}
} else {
warn!(
Expand Down
151 changes: 123 additions & 28 deletions net_util/src/tap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

use std::fs::File;
use std::io::{Error as IoError, Read, Result as IoResult, Write};
use std::net;
use std::net::{IpAddr, Ipv6Addr};
use std::os::raw::*;
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};

Expand Down Expand Up @@ -38,6 +38,8 @@ pub enum Error {
InvalidIfname,
#[error("Error parsing MAC data: {0}")]
MacParsing(IoError),
#[error("Invalid netmask")]
InvalidNetmask,
}

pub type Result<T> = ::std::result::Result<T, Error>;
Expand Down Expand Up @@ -88,6 +90,37 @@ fn build_terminated_if_name(if_name: &str) -> Result<Vec<u8>> {
Ok(terminated_if_name)
}

fn ipv6_mask_to_prefix(mask: Ipv6Addr) -> Result<u8> {
let mask = mask.segments();
let mut iter = mask.iter();

let mut prefix = 0;
for &segment in &mut iter {
if segment == 0xffff {
prefix += 16;
} else if segment == 0 {
break;
} else {
let prefix_bits = segment.leading_ones() as u8;
if segment << prefix_bits != 0 {
return Err(Error::InvalidNetmask);
}

prefix += prefix_bits;
break;
}
}

// Check that remaining bits are all unset
for &segment in iter {
if segment != 0 {
return Err(Error::InvalidNetmask);
}
}

Ok(prefix)
}

impl Tap {
unsafe fn ioctl_with_mut_ref<F: AsRawFd, T>(fd: &F, req: c_ulong, arg: &mut T) -> Result<()> {
let ret = ioctl_with_mut_ref(fd, req, arg);
Expand Down Expand Up @@ -235,16 +268,78 @@ impl Tap {
}

/// Set the host-side IP address for the tap interface.
pub fn set_ip_addr(&self, ip_addr: net::Ipv4Addr) -> Result<()> {
let sock = create_inet_socket().map_err(Error::NetUtil)?;
let addr = create_sockaddr(ip_addr);
pub fn set_ip_addr(&self, ip_addr: IpAddr, netmask: Option<IpAddr>) -> Result<()> {
let sock = create_inet_socket(ip_addr).map_err(Error::NetUtil)?;

let mut ifreq = self.get_ifreq();

ifreq.ifr_ifru.ifru_addr = addr;
match ip_addr {
IpAddr::V4(addr) => {
let addr = create_sockaddr(addr);

// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
unsafe { Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCSIFADDR as c_ulong, &ifreq) }
ifreq.ifr_ifru.ifru_addr = addr;

// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
unsafe {
Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCSIFADDR as c_ulong, &ifreq)?;
}

if let Some(IpAddr::V4(mask)) = netmask {
ifreq.ifr_ifru.ifru_netmask = create_sockaddr(mask);

// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
unsafe {
Self::ioctl_with_ref(
&sock,
net_gen::sockios::SIOCSIFNETMASK as c_ulong,
&ifreq,
)?;
}
};

Ok(())
}
IpAddr::V6(addr) => {
let ifindex = {
// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
unsafe {
Self::ioctl_with_ref(
&sock,
net_gen::sockios::SIOCGIFINDEX as c_ulong,
&ifreq,
)?;
}

// SAFETY: ifru_ivalue contains the ifindex and is set by the previous ioctl
unsafe {
match ifreq.ifr_ifru.ifru_ivalue {
0 => return Err(Error::InvalidIfname),
i => i,
}
}
};

let prefixlen = match netmask {
Some(IpAddr::V6(netmask)) => ipv6_mask_to_prefix(netmask)?,
Some(IpAddr::V4(_)) => return Err(Error::InvalidNetmask),
None => 0,
};

let ifreq = net_gen::in6_ifreq {
// SAFETY: addr can be safely transmuted to in6_addr
ifr6_addr: unsafe {
std::mem::transmute::<[u8; 16], net_gen::ipv6::in6_addr>(addr.octets())
},
ifr6_prefixlen: prefixlen as u32,
ifr6_ifindex: ifindex,
};

// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
unsafe {
Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCSIFADDR as c_ulong, &ifreq)
}
}
}
}

/// Set mac addr for tap interface.
Expand Down Expand Up @@ -294,19 +389,6 @@ impl Tap {
Ok(addr)
}

/// Set the netmask for the subnet that the tap interface will exist on.
pub fn set_netmask(&self, netmask: net::Ipv4Addr) -> Result<()> {
let sock = create_inet_socket().map_err(Error::NetUtil)?;
let addr = create_sockaddr(netmask);

let mut ifreq = self.get_ifreq();

ifreq.ifr_ifru.ifru_addr = addr;

// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
unsafe { Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCSIFNETMASK as c_ulong, &ifreq) }
}

#[cfg(not(fuzzing))]
pub fn mtu(&self) -> Result<i32> {
let sock = create_unix_socket().map_err(Error::NetUtil)?;
Expand Down Expand Up @@ -602,11 +684,22 @@ mod tests {
let tap_ip_guard = TAP_IP_LOCK.lock().unwrap();

let tap = Tap::new(1).unwrap();
let ip_addr: net::Ipv4Addr = (*tap_ip_guard).parse().unwrap();
let netmask: net::Ipv4Addr = SUBNET_MASK.parse().unwrap();
let ip_addr = IpAddr::V4((*tap_ip_guard).parse().unwrap());
let netmask = IpAddr::V4(SUBNET_MASK.parse().unwrap());

tap.set_ip_addr(ip_addr, Some(netmask)).unwrap();
}

#[test]
fn test_tap_configure_ipv6() {
let tap_ip6_lock: Mutex<&'static str> = Mutex::new("2001:db8:85a3::8a2e:370:7334");
let tap_ip6_guard = tap_ip6_lock.lock().unwrap();

let tap = Tap::new(1).unwrap();
let ip_addr = IpAddr::V6((*tap_ip6_guard).parse().unwrap());
let netmask = IpAddr::V6("ffff:ffff::".parse().unwrap());

tap.set_ip_addr(ip_addr).unwrap();
tap.set_netmask(netmask).unwrap();
tap.set_ip_addr(ip_addr, Some(netmask)).unwrap();
}

#[test]
Expand Down Expand Up @@ -640,8 +733,9 @@ mod tests {
let tap_ip_guard = TAP_IP_LOCK.lock().unwrap();

let mut tap = Tap::new(1).unwrap();
tap.set_ip_addr((*tap_ip_guard).parse().unwrap()).unwrap();
tap.set_netmask(SUBNET_MASK.parse().unwrap()).unwrap();
let ip_addr = IpAddr::V4((*tap_ip_guard).parse().unwrap());
let netmask = IpAddr::V4(SUBNET_MASK.parse().unwrap());
tap.set_ip_addr(ip_addr, Some(netmask)).unwrap();
tap.enable().unwrap();

// Send a packet to the interface. We expect to be able to receive it on the associated fd.
Expand Down Expand Up @@ -698,8 +792,9 @@ mod tests {
let tap_ip_guard = TAP_IP_LOCK.lock().unwrap();

let mut tap = Tap::new(1).unwrap();
tap.set_ip_addr((*tap_ip_guard).parse().unwrap()).unwrap();
tap.set_netmask(SUBNET_MASK.parse().unwrap()).unwrap();
let ip_addr = IpAddr::V4((*tap_ip_guard).parse().unwrap());
let netmask = IpAddr::V4(SUBNET_MASK.parse().unwrap());
tap.set_ip_addr(ip_addr, Some(netmask)).unwrap();
tap.enable().unwrap();

let (mac, _, mut rx) = pnet_get_mac_tx_rx(tap_name_to_string(&tap));
Expand Down
12 changes: 9 additions & 3 deletions tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6330,7 +6330,9 @@ mod common_parallel {
use std::str::FromStr;
let taps = net_util::open_tap(
Some("chtap0"),
Some(std::net::Ipv4Addr::from_str(&guest.network.host_ip).unwrap()),
Some(std::net::IpAddr::V4(
std::net::Ipv4Addr::from_str(&guest.network.host_ip).unwrap(),
)),
None,
&mut None,
None,
Expand Down Expand Up @@ -7597,7 +7599,9 @@ mod common_sequential {
use std::str::FromStr;
let taps = net_util::open_tap(
Some(tap_name),
Some(std::net::Ipv4Addr::from_str(&guest.network.host_ip).unwrap()),
Some(std::net::IpAddr::V4(
std::net::Ipv4Addr::from_str(&guest.network.host_ip).unwrap(),
)),
None,
&mut None,
None,
Expand Down Expand Up @@ -7695,7 +7699,9 @@ mod common_sequential {

let taps = net_util::open_tap(
Some(tap_name),
Some(std::net::Ipv4Addr::from_str(&guest.network.host_ip).unwrap()),
Some(std::net::IpAddr::V4(
std::net::Ipv4Addr::from_str(&guest.network.host_ip).unwrap(),
)),
None,
&mut None,
None,
Expand Down
Loading
Loading