use crate::sys::ifreq::ifreq;
use crate::sys::{dummy_socket, ioctls, InterfaceHandle};
use crate::{Error, Interface};
use ipnet::IpNet;
use libc::ARPHRD_ETHER;
use netlink_packet_core::{
NetlinkHeader, NetlinkMessage, NetlinkPayload, NLM_F_DUMP, NLM_F_REQUEST,
};
use netlink_packet_route::address::{AddressAttribute, AddressMessage};
use netlink_packet_route::{AddressFamily, RouteNetlinkMessage};
use netlink_sys::constants::NETLINK_ROUTE;
use netlink_sys::{Socket, SocketAddr};
use nix::net::if_::InterfaceFlags;
use std::net::IpAddr;
use std::os::unix::io::AsRawFd;
pub trait InterfaceExt {
fn set_up(&self, v: bool) -> Result<(), Error>;
fn set_running(&self, v: bool) -> Result<(), Error>;
fn set_hwaddress(&self, hwaddress: [u8; 6]) -> Result<(), Error>;
fn set_packet_info(&self, v: bool) -> Result<(), Error>;
}
impl InterfaceHandle {
pub fn add_address(&self, network: IpNet) -> Result<(), Error> {
let mut socket = Socket::new(NETLINK_ROUTE)?;
socket.bind_auto()?;
socket.connect(&SocketAddr::new(0, 0))?;
let message = make_address_message(self.index, network);
let mut nl_hdr = NetlinkHeader::default();
nl_hdr.flags = NLM_F_DUMP | NLM_F_REQUEST;
let mut req = NetlinkMessage::new(
nl_hdr,
NetlinkPayload::from(RouteNetlinkMessage::NewAddress(message)),
);
req.finalize();
let mut buf = vec![0; req.header.length as _];
req.serialize(&mut buf);
socket.send(&buf, 0)?;
Ok(())
}
pub fn remove_address(&self, network: IpNet) -> Result<(), Error> {
let mut socket = Socket::new(NETLINK_ROUTE)?;
socket.bind_auto()?;
socket.connect(&SocketAddr::new(0, 0))?;
let message = make_address_message(self.index, network);
let mut nl_hdr = NetlinkHeader::default();
nl_hdr.flags = NLM_F_REQUEST;
let mut req = NetlinkMessage::new(
nl_hdr,
NetlinkPayload::from(RouteNetlinkMessage::DelAddress(message)),
);
req.finalize();
let mut buf = vec![0; req.header.length as _];
req.serialize(&mut buf);
socket.send(&buf, 0)?;
Ok(())
}
pub fn hwaddress(&self) -> Result<[u8; 6], Error> {
let mut req = ifreq::new(self.name()?)?;
let socket = dummy_socket()?;
unsafe { ioctls::siocgifhwaddr(socket.as_raw_fd(), &mut req) }?;
let mut rs = [0; 6];
unsafe {
for (i, b) in req.ifr_ifru.ifru_hwaddr.sa_data.iter().take(6).enumerate() {
rs[i] = *b as _;
}
}
Ok(rs)
}
}
impl InterfaceExt for Interface {
fn set_up(&self, v: bool) -> Result<(), Error> {
self.0.set_up(v)
}
fn set_running(&self, v: bool) -> Result<(), Error> {
self.0.set_running(v)
}
fn set_hwaddress(&self, hwaddress: [u8; 6]) -> Result<(), Error> {
let mut req = ifreq::new(self.name()?)?;
req.ifr_ifru.ifru_hwaddr = libc::sockaddr {
sa_family: ARPHRD_ETHER,
sa_data: unsafe { std::mem::zeroed() },
};
unsafe {
req.ifr_ifru.ifru_hwaddr.sa_data[0..6]
.copy_from_slice(hwaddress.map(|c| c as _).as_slice());
}
let socket = dummy_socket()?;
unsafe { ioctls::siocsifhwaddr(socket.as_raw_fd(), &req) }?;
Ok(())
}
fn set_packet_info(&self, v: bool) -> Result<(), Error> {
let mut flags = self.0.flags()?;
flags.set(InterfaceFlags::IFF_NO_PI, !v);
self.0.set_flags(flags)?;
Ok(())
}
}
fn make_address_message(index: u32, network: IpNet) -> AddressMessage {
let mut message = AddressMessage::default();
message.header.prefix_len = network.prefix_len();
message.header.index = index;
let addr = network.addr();
message.header.family = if addr.is_ipv4() {
AddressFamily::Inet
} else {
AddressFamily::Inet6
};
if let IpAddr::V6(addr) = addr {
if addr.is_multicast() {
message.attributes.push(AddressAttribute::Multicast(addr));
return message;
}
}
message
.attributes
.push(AddressAttribute::Address(network.addr()));
if let IpNet::V4(network_v4) = network {
message
.attributes
.push(AddressAttribute::Local(network.addr()));
message
.attributes
.push(AddressAttribute::Broadcast(network_v4.broadcast()));
}
message
}