mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
net_util: add support for IPv6 addresses on tap interfaces
Allow tap interfaces to be configured with an IPv6 address. The change is fairly straightforward: we need to update the API types and CLI parsing to accept either an IPv6 or IPv4 and then match on the IP address type when the tap device is configured. For IPv6 addresses, the netmask (prefix) must be provided at the same time as the address itself (in the SIOCSIFADDR ioctl). They cannot be configured separately. So we remove the separate "set_netmask" function and convert "set_ip_addr" to also accept a netmask. For IPv4 addresses, the IP address and netmask were already always set together, so this should have no functional impact for users of IPv4 addresses. Signed-off-by: Gregory Anders <ganders@cloudflare.com>
This commit is contained in:
committed by
Rob Bradford
parent
1454d39b28
commit
dce82a34d0
@@ -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};
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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}")]
|
||||
@@ -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,
|
||||
@@ -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!(
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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>;
|
||||
@@ -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);
|
||||
@@ -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.
|
||||
@@ -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)?;
|
||||
@@ -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).unwrap();
|
||||
tap.set_netmask(netmask).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, Some(netmask)).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -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.
|
||||
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user