mirror of
https://github.com/containers/netavark.git
synced 2026-02-05 15:45:47 +01:00
refactor(netlink): Generalize socket for multi-protocol support
The existing `netlink.rs` was specific to `NETLINK_ROUTE`. To prepare for adding `NETLINK_NETFILTER` support for conntrack, this commit refactors the netlink socket implementation to be generic. - A new generic `Socket<P>` is introduced in `src/network/netlink.rs` to handle common send/receive logic. - All routing-specific code, types, and functions are moved to a new `src/network/netlink_route.rs`, which now uses `Socket<NetlinkRoute>`. - All imports and type signatures across the codebase have been updated to use this new structure. This is a pure refactoring with no functional changes. Signed-off-by: Shivang K Raghuvanshi <shivangraghuvanshi2005@gmail.com>
This commit is contained in:
@@ -5,7 +5,7 @@ use std::{collections::HashMap, os::fd::AsFd};
|
||||
use netavark::{
|
||||
network::{
|
||||
core_utils::{open_netlink_sockets, CoreUtils},
|
||||
netlink, types,
|
||||
netlink_route, types,
|
||||
},
|
||||
new_error,
|
||||
plugin::{Info, Plugin, PluginExec, API_VERSION},
|
||||
@@ -41,7 +41,9 @@ impl Plugin for Exec {
|
||||
|
||||
let name = opts.network.network_interface.unwrap_or_default();
|
||||
|
||||
let link = host.netlink.get_link(netlink::LinkID::Name(name.clone()))?;
|
||||
let link = host
|
||||
.netlink
|
||||
.get_link(netlink_route::LinkID::Name(name.clone()))?;
|
||||
|
||||
let mut mac_address = String::from("");
|
||||
for nla in link.attributes {
|
||||
@@ -98,7 +100,7 @@ impl Plugin for Exec {
|
||||
|
||||
let name = opts.network.network_interface.unwrap_or_default();
|
||||
|
||||
let link = netns.netlink.get_link(netlink::LinkID::Name(name))?;
|
||||
let link = netns.netlink.get_link(netlink_route::LinkID::Name(name))?;
|
||||
|
||||
netns
|
||||
.netlink
|
||||
|
||||
@@ -4,7 +4,8 @@ use crate::dns::aardvark::Aardvark;
|
||||
use crate::error::{NetavarkError, NetavarkResult};
|
||||
use crate::firewall;
|
||||
use crate::network::driver::{get_network_driver, DriverInfo, NetworkDriver};
|
||||
use crate::network::netlink::{self, LinkID};
|
||||
use crate::network::netlink::Socket;
|
||||
use crate::network::netlink_route::{LinkID, NetlinkRoute};
|
||||
use crate::network::{self};
|
||||
use crate::network::{core_utils, types};
|
||||
|
||||
@@ -160,8 +161,11 @@ impl Setup {
|
||||
}
|
||||
}
|
||||
|
||||
fn teardown_drivers<'a, I>(drivers: I, host: &mut netlink::Socket, netns: &mut netlink::Socket)
|
||||
where
|
||||
fn teardown_drivers<'a, I>(
|
||||
drivers: I,
|
||||
host: &mut Socket<NetlinkRoute>,
|
||||
netns: &mut Socket<NetlinkRoute>,
|
||||
) where
|
||||
I: Iterator<Item = &'a Box<dyn NetworkDriver + 'a>>,
|
||||
{
|
||||
for driver in drivers {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::{net::Ipv4Addr, sync::Arc};
|
||||
|
||||
use crate::network::netlink_route::{LinkID, Route};
|
||||
use log::debug;
|
||||
use mozim::{DhcpV4Client, DhcpV4Config, DhcpV4Lease as MozimV4Lease, DhcpV4State};
|
||||
use tokio::sync::Mutex;
|
||||
@@ -11,7 +12,7 @@ use crate::{
|
||||
lib::g_rpc::{Lease as NetavarkLease, NetworkConfig},
|
||||
},
|
||||
error::{ErrorWrap, NetavarkError, NetavarkResult},
|
||||
network::{core_utils, netlink::Route},
|
||||
network::core_utils,
|
||||
wrap,
|
||||
};
|
||||
|
||||
@@ -257,7 +258,7 @@ fn update_lease_ip(
|
||||
|
||||
if new_net != old_net {
|
||||
let link = sock
|
||||
.get_link(crate::network::netlink::LinkID::Name(interface.to_string()))
|
||||
.get_link(LinkID::Name(interface.to_string()))
|
||||
.wrap("get interface in netns")?;
|
||||
sock.add_addr(link.header.index, &ipnet::IpNet::V4(new_net))
|
||||
.wrap("add new addr")?;
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
pub use crate::dhcp_proxy::lib::g_rpc::{Lease as NetavarkLease, Lease};
|
||||
pub use crate::dhcp_proxy::types::{CustomErr, ProxyError};
|
||||
use crate::network::core_utils;
|
||||
use crate::network::netlink;
|
||||
use crate::network::netlink::Socket;
|
||||
use crate::network::netlink_route::{LinkID, NetlinkRoute};
|
||||
use ipnet::IpNet;
|
||||
use log::debug;
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
@@ -34,8 +34,8 @@ trait Address<T> {
|
||||
fn new(l: &Lease, interface: &str) -> Result<Self, ProxyError>
|
||||
where
|
||||
Self: Sized;
|
||||
fn add_ip(&self, nls: &mut Socket) -> Result<(), ProxyError>;
|
||||
fn add_gws(&self, nls: &mut Socket) -> Result<(), ProxyError>;
|
||||
fn add_ip(&self, nls: &mut Socket<NetlinkRoute>) -> Result<(), ProxyError>;
|
||||
fn add_gws(&self, nls: &mut Socket<NetlinkRoute>) -> Result<(), ProxyError>;
|
||||
}
|
||||
|
||||
fn handle_gws(g: Vec<String>, netmask: &str) -> Result<Vec<IpNet>, ProxyError> {
|
||||
@@ -112,10 +112,10 @@ impl Address<Ipv4Addr> for MacVLAN {
|
||||
}
|
||||
|
||||
// add the ip address to the container namespace
|
||||
fn add_ip(&self, nls: &mut Socket) -> Result<(), ProxyError> {
|
||||
fn add_ip(&self, nls: &mut Socket<NetlinkRoute>) -> Result<(), ProxyError> {
|
||||
debug!("adding network information for {}", self.interface);
|
||||
let ip = IpNet::new(self.address, self.prefix_length)?;
|
||||
let dev = nls.get_link(netlink::LinkID::Name(self.interface.clone()))?;
|
||||
let dev = nls.get_link(LinkID::Name(self.interface.clone()))?;
|
||||
match nls.add_addr(dev.header.index, &ip) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => Err(ProxyError::new(e.to_string())),
|
||||
@@ -123,7 +123,7 @@ impl Address<Ipv4Addr> for MacVLAN {
|
||||
}
|
||||
|
||||
// add one or more routes to the container namespace
|
||||
fn add_gws(&self, nls: &mut Socket) -> Result<(), ProxyError> {
|
||||
fn add_gws(&self, nls: &mut Socket<NetlinkRoute>) -> Result<(), ProxyError> {
|
||||
debug!("adding gateways to {}", self.interface);
|
||||
match core_utils::add_default_routes(nls, &self.gateways, None) {
|
||||
Ok(_) => Ok(()),
|
||||
|
||||
@@ -3,6 +3,10 @@ use std::{collections::HashMap, fs, net::IpAddr, os::fd::BorrowedFd};
|
||||
use crate::dns::aardvark::SafeString;
|
||||
use crate::network::core_utils::get_default_route_interface;
|
||||
use crate::network::dhcp::{dhcp_teardown, get_dhcp_lease};
|
||||
use crate::network::netlink::Socket;
|
||||
use crate::network::netlink_route::{
|
||||
parse_create_link_options, CreateLinkOptions, LinkID, NetlinkRoute,
|
||||
};
|
||||
use crate::{
|
||||
dns::aardvark::AardvarkEntry,
|
||||
error::{ErrorWrap, NetavarkError, NetavarkErrorList, NetavarkResult},
|
||||
@@ -34,7 +38,7 @@ use super::{
|
||||
IPAMAddresses, IsolateOption, PortForwardConfig, SetupNetwork, TearDownNetwork,
|
||||
TeardownPortForward,
|
||||
},
|
||||
netlink, sysctl,
|
||||
sysctl,
|
||||
types::StatusBlock,
|
||||
};
|
||||
|
||||
@@ -158,7 +162,7 @@ impl driver::NetworkDriver for Bridge<'_> {
|
||||
|
||||
fn setup(
|
||||
&self,
|
||||
netlink_sockets: (&mut netlink::Socket, &mut netlink::Socket),
|
||||
netlink_sockets: (&mut Socket<NetlinkRoute>, &mut Socket<NetlinkRoute>),
|
||||
) -> NetavarkResult<(StatusBlock, Option<AardvarkEntry<'_>>)> {
|
||||
let data = match &self.data {
|
||||
Some(d) => d,
|
||||
@@ -348,7 +352,7 @@ impl driver::NetworkDriver for Bridge<'_> {
|
||||
|
||||
fn teardown(
|
||||
&self,
|
||||
netlink_sockets: (&mut netlink::Socket, &mut netlink::Socket),
|
||||
netlink_sockets: (&mut Socket<NetlinkRoute>, &mut Socket<NetlinkRoute>),
|
||||
) -> NetavarkResult<()> {
|
||||
let mode: Option<String> = parse_option(&self.info.network.options, OPTION_MODE)?;
|
||||
let mode = get_bridge_mode_from_string(mode.as_deref())?;
|
||||
@@ -592,8 +596,8 @@ const IPV6_FORWARD: &str = "net/ipv6/conf/all/forwarding";
|
||||
|
||||
/// returns the container veth mac address
|
||||
fn create_interfaces(
|
||||
host: &mut netlink::Socket,
|
||||
netns: &mut netlink::Socket,
|
||||
host: &mut Socket<NetlinkRoute>,
|
||||
netns: &mut Socket<NetlinkRoute>,
|
||||
data: &InternalData,
|
||||
internal: bool,
|
||||
rootless: bool,
|
||||
@@ -601,9 +605,9 @@ fn create_interfaces(
|
||||
netns_fd: BorrowedFd<'_>,
|
||||
) -> NetavarkResult<CreateInterfacesResult> {
|
||||
let mut sysctl_writer = None;
|
||||
let (bridge_index, mtu, mac) = match host.get_link(netlink::LinkID::Name(
|
||||
data.bridge_interface_name.to_string(),
|
||||
)) {
|
||||
let (bridge_index, mtu, mac) = match host
|
||||
.get_link(LinkID::Name(data.bridge_interface_name.to_string()))
|
||||
{
|
||||
Ok(bridge) => {
|
||||
let (bridge_index, mtu) = validate_bridge_link(
|
||||
bridge,
|
||||
@@ -696,7 +700,7 @@ fn create_interfaces(
|
||||
// writer must be create before the bridge is created
|
||||
let sw = sysctl::SysctlDWriter::new(path, sysctls);
|
||||
|
||||
let mut create_link_opts = netlink::CreateLinkOptions::new(
|
||||
let mut create_link_opts = CreateLinkOptions::new(
|
||||
data.bridge_interface_name.to_string(),
|
||||
InfoKind::Bridge,
|
||||
);
|
||||
@@ -724,7 +728,7 @@ fn create_interfaces(
|
||||
}
|
||||
|
||||
if let Some(vrf_name) = &data.vrf {
|
||||
let vrf = match host.get_link(netlink::LinkID::Name(vrf_name.to_string())) {
|
||||
let vrf = match host.get_link(LinkID::Name(vrf_name.to_string())) {
|
||||
Ok(vrf) => check_link_is_vrf(vrf, vrf_name)?,
|
||||
Err(err) => return Err(err).wrap("get vrf to set up bridge interface"),
|
||||
};
|
||||
@@ -738,9 +742,7 @@ fn create_interfaces(
|
||||
sysctl_writer = Some(sw);
|
||||
|
||||
let link = host
|
||||
.get_link(netlink::LinkID::Name(
|
||||
data.bridge_interface_name.to_string(),
|
||||
))
|
||||
.get_link(LinkID::Name(data.bridge_interface_name.to_string()))
|
||||
.wrap("get bridge interface")?;
|
||||
|
||||
let mut mac = None;
|
||||
@@ -761,7 +763,7 @@ fn create_interfaces(
|
||||
.wrap("add ip addr to bridge")?;
|
||||
}
|
||||
|
||||
host.set_up(netlink::LinkID::ID(link.header.index))
|
||||
host.set_up(LinkID::ID(link.header.index))
|
||||
.wrap("set bridge up")?;
|
||||
|
||||
(link.header.index, mtu, mac)
|
||||
@@ -791,8 +793,8 @@ fn create_interfaces(
|
||||
/// return the container veth mac address
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn create_veth_pair<'fd>(
|
||||
host: &mut netlink::Socket,
|
||||
netns: &mut netlink::Socket,
|
||||
host: &mut Socket<NetlinkRoute>,
|
||||
netns: &mut Socket<NetlinkRoute>,
|
||||
data: &InternalData,
|
||||
primary_index: u32,
|
||||
bridge_mac: Option<Vec<u8>>,
|
||||
@@ -802,16 +804,15 @@ fn create_veth_pair<'fd>(
|
||||
mtu: u32,
|
||||
) -> NetavarkResult<String> {
|
||||
let mut peer_opts =
|
||||
netlink::CreateLinkOptions::new(data.container_interface_name.to_string(), InfoKind::Veth);
|
||||
CreateLinkOptions::new(data.container_interface_name.to_string(), InfoKind::Veth);
|
||||
peer_opts.mac = data.mac_address.clone().unwrap_or_default();
|
||||
peer_opts.mtu = mtu;
|
||||
peer_opts.netns = Some(netns_fd);
|
||||
|
||||
let mut peer = LinkMessage::default();
|
||||
netlink::parse_create_link_options(&mut peer, peer_opts);
|
||||
parse_create_link_options(&mut peer, peer_opts);
|
||||
|
||||
let mut host_veth =
|
||||
netlink::CreateLinkOptions::new(data.host_interface_name.clone(), InfoKind::Veth);
|
||||
let mut host_veth = CreateLinkOptions::new(data.host_interface_name.clone(), InfoKind::Veth);
|
||||
host_veth.mtu = mtu;
|
||||
host_veth.primary_index = primary_index;
|
||||
host_veth.info_data = Some(InfoData::Veth(InfoVeth::Peer(peer)));
|
||||
@@ -835,9 +836,7 @@ fn create_veth_pair<'fd>(
|
||||
})?;
|
||||
|
||||
let veth = netns
|
||||
.get_link(netlink::LinkID::Name(
|
||||
data.container_interface_name.to_string(),
|
||||
))
|
||||
.get_link(LinkID::Name(data.container_interface_name.to_string()))
|
||||
.wrap("get container veth")?;
|
||||
|
||||
let mut mac = String::from("");
|
||||
@@ -890,7 +889,7 @@ fn create_veth_pair<'fd>(
|
||||
})?;
|
||||
|
||||
if data.ipam.ipv6_enabled {
|
||||
let host_veth = host.get_link(netlink::LinkID::ID(host_link))?;
|
||||
let host_veth = host.get_link(LinkID::ID(host_link))?;
|
||||
|
||||
for nla in host_veth.attributes.into_iter() {
|
||||
if let LinkAttribute::IfName(name) = nla {
|
||||
@@ -903,7 +902,7 @@ fn create_veth_pair<'fd>(
|
||||
}
|
||||
}
|
||||
|
||||
host.set_up(netlink::LinkID::ID(host_link))
|
||||
host.set_up(LinkID::ID(host_link))
|
||||
.wrap("failed to set host veth up")?;
|
||||
|
||||
// Ok this is extremely strange, by default the kernel will always choose the mac address with the
|
||||
@@ -916,7 +915,7 @@ fn create_veth_pair<'fd>(
|
||||
// connected otherwise no connectivity is possible at all and I have no idea why but CNI does it
|
||||
// also in the same way.
|
||||
if let Some(m) = bridge_mac {
|
||||
host.set_mac_address(netlink::LinkID::ID(primary_index), m)
|
||||
host.set_mac_address(LinkID::ID(primary_index), m)
|
||||
.wrap("set static mac on bridge")?;
|
||||
}
|
||||
|
||||
@@ -927,7 +926,7 @@ fn create_veth_pair<'fd>(
|
||||
}
|
||||
|
||||
netns
|
||||
.set_up(netlink::LinkID::ID(veth.header.index))
|
||||
.set_up(LinkID::ID(veth.header.index))
|
||||
.wrap("set container veth up")?;
|
||||
|
||||
if !internal && !data.no_default_route {
|
||||
@@ -948,7 +947,7 @@ fn create_veth_pair<'fd>(
|
||||
fn validate_bridge_link(
|
||||
msg: LinkMessage,
|
||||
vlan: bool,
|
||||
netlink: &mut netlink::Socket,
|
||||
netlink: &mut Socket<NetlinkRoute>,
|
||||
br_name: &str,
|
||||
) -> NetavarkResult<(u32, u32)> {
|
||||
let mut mtu: u32 = 0;
|
||||
@@ -1038,20 +1037,20 @@ fn check_link_is_vrf(msg: LinkMessage, vrf_name: &str) -> NetavarkResult<LinkMes
|
||||
}
|
||||
|
||||
fn remove_link(
|
||||
host: &mut netlink::Socket,
|
||||
netns: &mut netlink::Socket,
|
||||
host: &mut Socket<NetlinkRoute>,
|
||||
netns: &mut Socket<NetlinkRoute>,
|
||||
mode: BridgeMode,
|
||||
br_name: &str,
|
||||
container_veth_name: &str,
|
||||
) -> NetavarkResult<bool> {
|
||||
netns
|
||||
.del_link(netlink::LinkID::Name(container_veth_name.to_string()))
|
||||
.del_link(LinkID::Name(container_veth_name.to_string()))
|
||||
.wrap(format!(
|
||||
"failed to delete container veth {container_veth_name}"
|
||||
))?;
|
||||
|
||||
let br = host
|
||||
.get_link(netlink::LinkID::Name(br_name.to_string()))
|
||||
.get_link(LinkID::Name(br_name.to_string()))
|
||||
.wrap("failed to get bridge interface")?;
|
||||
|
||||
let links = host
|
||||
@@ -1061,7 +1060,7 @@ fn remove_link(
|
||||
if links.is_empty() {
|
||||
if let BridgeMode::Managed = mode {
|
||||
log::info!("removing bridge {br_name}");
|
||||
host.del_link(netlink::LinkID::ID(br.header.index))
|
||||
host.del_link(LinkID::ID(br.header.index))
|
||||
.wrap(format!("failed to delete bridge {container_veth_name}"))?;
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::netlink;
|
||||
use crate::network::netlink::Socket;
|
||||
use crate::network::netlink_route::{LinkID, NetlinkRoute, Route};
|
||||
|
||||
use netlink_packet_route::link::LinkAttribute;
|
||||
|
||||
@@ -126,7 +128,7 @@ pub fn get_ipam_addresses<'a>(
|
||||
});
|
||||
}
|
||||
|
||||
let routes: Vec<netlink::Route> = match create_route_list(&network.routes) {
|
||||
let routes: Vec<Route> = match create_route_list(&network.routes) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
@@ -274,7 +276,7 @@ pub struct NamespaceOptions {
|
||||
/// Note we have to return the File object since the fd is only valid
|
||||
/// as long as the File object is valid
|
||||
pub file: File,
|
||||
pub netlink: netlink::Socket,
|
||||
pub netlink: Socket<NetlinkRoute>,
|
||||
}
|
||||
|
||||
pub fn open_netlink_sockets(
|
||||
@@ -283,11 +285,11 @@ pub fn open_netlink_sockets(
|
||||
let netns = open_netlink_socket(netns_path).wrap("open container netns")?;
|
||||
let hostns = open_netlink_socket("/proc/self/ns/net").wrap("open host netns")?;
|
||||
|
||||
let host_socket = netlink::Socket::new().wrap("host netlink socket")?;
|
||||
let host_socket = netlink::Socket::<NetlinkRoute>::new().wrap("host netlink socket")?;
|
||||
let netns_sock = exec_netns!(
|
||||
hostns.as_fd(),
|
||||
netns.as_fd(),
|
||||
netlink::Socket::new().wrap("netns netlink socket")
|
||||
netlink::Socket::<NetlinkRoute>::new().wrap("netns netlink socket")
|
||||
)?;
|
||||
|
||||
Ok((
|
||||
@@ -307,7 +309,7 @@ fn open_netlink_socket(netns_path: &str) -> NetavarkResult<File> {
|
||||
}
|
||||
|
||||
pub fn add_default_routes(
|
||||
sock: &mut netlink::Socket,
|
||||
sock: &mut Socket<NetlinkRoute>,
|
||||
gws: &[ipnet::IpNet],
|
||||
metric: Option<u32>,
|
||||
) -> NetavarkResult<()> {
|
||||
@@ -321,7 +323,7 @@ pub fn add_default_routes(
|
||||
}
|
||||
ipv4 = true;
|
||||
|
||||
netlink::Route::Ipv4 {
|
||||
Route::Ipv4 {
|
||||
dest: ipnet::Ipv4Net::new(Ipv4Addr::new(0, 0, 0, 0), 0)?,
|
||||
gw: v4.addr(),
|
||||
metric,
|
||||
@@ -333,7 +335,7 @@ pub fn add_default_routes(
|
||||
}
|
||||
ipv6 = true;
|
||||
|
||||
netlink::Route::Ipv6 {
|
||||
Route::Ipv6 {
|
||||
dest: ipnet::Ipv6Net::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0), 0)?,
|
||||
gw: v6.addr(),
|
||||
metric,
|
||||
@@ -346,9 +348,7 @@ pub fn add_default_routes(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_route_list(
|
||||
routes: &Option<Vec<types::Route>>,
|
||||
) -> NetavarkResult<Vec<netlink::Route>> {
|
||||
pub fn create_route_list(routes: &Option<Vec<types::Route>>) -> NetavarkResult<Vec<Route>> {
|
||||
match routes {
|
||||
Some(rs) => rs
|
||||
.iter()
|
||||
@@ -357,12 +357,12 @@ pub fn create_route_list(
|
||||
let dst = r.destination;
|
||||
let mtr = r.metric;
|
||||
match (gw, dst) {
|
||||
(IpAddr::V4(gw4), IpNet::V4(dst4)) => Ok(netlink::Route::Ipv4 {
|
||||
(IpAddr::V4(gw4), IpNet::V4(dst4)) => Ok(Route::Ipv4 {
|
||||
dest: dst4,
|
||||
gw: gw4,
|
||||
metric: mtr,
|
||||
}),
|
||||
(IpAddr::V6(gw6), IpNet::V6(dst6)) => Ok(netlink::Route::Ipv6 {
|
||||
(IpAddr::V6(gw6), IpNet::V6(dst6)) => Ok(Route::Ipv6 {
|
||||
dest: dst6,
|
||||
gw: gw6,
|
||||
metric: mtr,
|
||||
@@ -398,7 +398,7 @@ pub fn is_using_systemd() -> bool {
|
||||
}
|
||||
|
||||
/// Returns the *first* interface with a default route or an error if no default route interface exists.
|
||||
pub fn get_default_route_interface(host: &mut netlink::Socket) -> NetavarkResult<LinkMessage> {
|
||||
pub fn get_default_route_interface(host: &mut Socket<NetlinkRoute>) -> NetavarkResult<LinkMessage> {
|
||||
let routes = host.dump_routes().wrap("dump routes")?;
|
||||
|
||||
for route in routes {
|
||||
@@ -416,7 +416,7 @@ pub fn get_default_route_interface(host: &mut netlink::Socket) -> NetavarkResult
|
||||
// if there is no dest we have a default route
|
||||
// return the output interface for this route
|
||||
if !dest && out_if > 0 {
|
||||
return host.get_link(netlink::LinkID::ID(out_if));
|
||||
return host.get_link(LinkID::ID(out_if));
|
||||
}
|
||||
}
|
||||
Err(NetavarkError::msg("failed to get default route interface"))
|
||||
|
||||
@@ -7,8 +7,10 @@ use std::str::FromStr;
|
||||
use crate::dhcp_proxy::lib::g_rpc::NetworkConfig;
|
||||
use crate::dhcp_proxy::proxy_conf::DEFAULT_UDS_PATH;
|
||||
|
||||
use super::core_utils;
|
||||
use super::driver::DriverInfo;
|
||||
use super::{core_utils, netlink};
|
||||
use crate::network::netlink::Socket;
|
||||
use crate::network::netlink_route::{LinkID, NetlinkRoute};
|
||||
|
||||
pub type DhcpLeaseInfo = (Vec<NetAddress>, Option<Vec<IpAddr>>, Option<Vec<String>>);
|
||||
|
||||
@@ -160,14 +162,14 @@ pub fn release_dhcp_lease(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn dhcp_teardown(info: &DriverInfo, sock: &mut netlink::Socket) -> NetavarkResult<()> {
|
||||
pub fn dhcp_teardown(info: &DriverInfo, sock: &mut Socket<NetlinkRoute>) -> NetavarkResult<()> {
|
||||
let ipam = core_utils::get_ipam_addresses(info.per_network_opts, info.network)?;
|
||||
let if_name = info.per_network_opts.interface_name.clone();
|
||||
|
||||
// If we are using DHCP, we need to at least call to the proxy so that
|
||||
// the proxy's cache can get updated and the current lease can be released.
|
||||
if ipam.dhcp_enabled {
|
||||
let dev = sock.get_link(netlink::LinkID::Name(if_name)).wrap(format!(
|
||||
let dev = sock.get_link(LinkID::Name(if_name)).wrap(format!(
|
||||
"get container interface {}",
|
||||
&info.per_network_opts.interface_name
|
||||
))?;
|
||||
|
||||
@@ -8,11 +8,13 @@ use std::{ffi::OsString, net::IpAddr, os::fd::BorrowedFd, path::Path};
|
||||
|
||||
use super::{
|
||||
bridge::Bridge,
|
||||
constants, netlink,
|
||||
constants,
|
||||
plugin::PluginDriver,
|
||||
types::{Network, PerNetworkOptions, PortMapping, StatusBlock},
|
||||
vlan::Vlan,
|
||||
};
|
||||
use crate::network::netlink::Socket;
|
||||
use crate::network::netlink_route::NetlinkRoute;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
pub struct DriverInfo<'a> {
|
||||
@@ -38,12 +40,12 @@ pub trait NetworkDriver {
|
||||
/// setup the network interfaces/firewall rules for this driver
|
||||
fn setup(
|
||||
&self,
|
||||
netlink_sockets: (&mut netlink::Socket, &mut netlink::Socket),
|
||||
netlink_sockets: (&mut Socket<NetlinkRoute>, &mut Socket<NetlinkRoute>),
|
||||
) -> NetavarkResult<(StatusBlock, Option<AardvarkEntry<'_>>)>;
|
||||
/// teardown the network interfaces/firewall rules for this driver
|
||||
fn teardown(
|
||||
&self,
|
||||
netlink_sockets: (&mut netlink::Socket, &mut netlink::Socket),
|
||||
netlink_sockets: (&mut Socket<NetlinkRoute>, &mut Socket<NetlinkRoute>),
|
||||
) -> NetavarkResult<()>;
|
||||
|
||||
/// return the network name
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::netlink;
|
||||
use crate::network::netlink_route::Route;
|
||||
use crate::network::types;
|
||||
use std::net::IpAddr;
|
||||
|
||||
@@ -103,7 +103,7 @@ pub struct IPAMAddresses {
|
||||
// if using macvlan and dhcp, then true
|
||||
pub dhcp_enabled: bool,
|
||||
pub gateway_addresses: Vec<ipnet::IpNet>,
|
||||
pub routes: Vec<netlink::Route>,
|
||||
pub routes: Vec<Route>,
|
||||
pub ipv6_enabled: bool,
|
||||
// result for podman
|
||||
pub net_addresses: Vec<types::NetAddress>,
|
||||
|
||||
@@ -16,7 +16,10 @@ pub mod core_utils;
|
||||
mod dhcp;
|
||||
pub mod driver;
|
||||
pub mod internal_types;
|
||||
|
||||
pub mod netlink;
|
||||
pub mod netlink_route;
|
||||
|
||||
pub mod plugin;
|
||||
pub mod sysctl;
|
||||
pub mod vlan;
|
||||
|
||||
@@ -1,95 +1,27 @@
|
||||
use std::{
|
||||
net::{Ipv4Addr, Ipv6Addr},
|
||||
os::fd::{AsFd, AsRawFd, BorrowedFd},
|
||||
};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::{
|
||||
error::{ErrorWrap, NetavarkError, NetavarkResult},
|
||||
network::constants,
|
||||
wrap,
|
||||
};
|
||||
use log::{info, trace};
|
||||
use log::trace;
|
||||
use netlink_packet_core::{
|
||||
NetlinkHeader, NetlinkMessage, NetlinkPayload, NLM_F_ACK, NLM_F_CREATE, NLM_F_DUMP, NLM_F_EXCL,
|
||||
NLM_F_REQUEST,
|
||||
NetlinkDeserializable, NetlinkHeader, NetlinkMessage, NetlinkPayload, NetlinkSerializable,
|
||||
NLM_F_DUMP, NLM_F_REQUEST,
|
||||
};
|
||||
use netlink_packet_route::{
|
||||
address::AddressMessage,
|
||||
link::{
|
||||
AfSpecBridge, BridgeVlanInfo, BridgeVlanInfoFlags, InfoBridge, InfoData, InfoKind,
|
||||
LinkAttribute, LinkFlags, LinkInfo, LinkMessage,
|
||||
},
|
||||
route::{RouteAddress, RouteMessage, RouteProtocol, RouteScope, RouteType},
|
||||
AddressFamily, RouteNetlinkMessage,
|
||||
};
|
||||
use netlink_sys::{protocols::NETLINK_ROUTE, SocketAddr};
|
||||
use netlink_sys::SocketAddr;
|
||||
|
||||
pub struct Socket {
|
||||
pub trait NetlinkFamily {
|
||||
const PROTOCOL: isize;
|
||||
type Message;
|
||||
}
|
||||
|
||||
pub struct Socket<P: NetlinkFamily> {
|
||||
socket: netlink_sys::Socket,
|
||||
sequence_number: u32,
|
||||
/// buffer size for reading netlink messages, see NLMSG_GOODSIZE in the kernel
|
||||
buffer: [u8; 8192],
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CreateLinkOptions<'fd> {
|
||||
pub name: String,
|
||||
kind: InfoKind,
|
||||
pub info_data: Option<InfoData>,
|
||||
pub mtu: u32,
|
||||
pub primary_index: u32,
|
||||
pub link: u32,
|
||||
pub mac: Vec<u8>,
|
||||
pub netns: Option<BorrowedFd<'fd>>,
|
||||
}
|
||||
|
||||
pub enum LinkID {
|
||||
ID(u32),
|
||||
Name(String),
|
||||
}
|
||||
|
||||
pub enum Route {
|
||||
Ipv4 {
|
||||
dest: ipnet::Ipv4Net,
|
||||
gw: Ipv4Addr,
|
||||
metric: Option<u32>,
|
||||
},
|
||||
Ipv6 {
|
||||
dest: ipnet::Ipv6Net,
|
||||
gw: Ipv6Addr,
|
||||
metric: Option<u32>,
|
||||
},
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Route {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let (dest, gw, metric) = match self {
|
||||
Route::Ipv4 { dest, gw, metric } => (
|
||||
dest.to_string(),
|
||||
gw.to_string(),
|
||||
metric.unwrap_or(constants::DEFAULT_METRIC),
|
||||
),
|
||||
Route::Ipv6 { dest, gw, metric } => (
|
||||
dest.to_string(),
|
||||
gw.to_string(),
|
||||
metric.unwrap_or(constants::DEFAULT_METRIC),
|
||||
),
|
||||
};
|
||||
write!(f, "(dest: {dest} ,gw: {gw}, metric {metric})")
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! expect_netlink_result {
|
||||
($result:expr, $count:expr) => {
|
||||
if $result.len() != $count {
|
||||
return Err(NetavarkError::msg(format!(
|
||||
"{}: unexpected netlink result (got {} result(s), want {})",
|
||||
function!(),
|
||||
$result.len(),
|
||||
$count
|
||||
)));
|
||||
}
|
||||
};
|
||||
_protocol: PhantomData<P>,
|
||||
}
|
||||
|
||||
/// get the function name of the currently executed function
|
||||
@@ -109,10 +41,28 @@ macro_rules! function {
|
||||
}
|
||||
}};
|
||||
}
|
||||
pub(crate) use function;
|
||||
|
||||
impl Socket {
|
||||
pub fn new() -> NetavarkResult<Socket> {
|
||||
let mut socket = wrap!(netlink_sys::Socket::new(NETLINK_ROUTE), "open")?;
|
||||
macro_rules! expect_netlink_result {
|
||||
($result:expr, $count:expr) => {
|
||||
if $result.len() != $count {
|
||||
return Err(NetavarkError::msg(format!(
|
||||
"{}: unexpected netlink result (got {} result(s), want {})",
|
||||
function!(),
|
||||
$result.len(),
|
||||
$count
|
||||
)));
|
||||
}
|
||||
};
|
||||
}
|
||||
pub(crate) use expect_netlink_result;
|
||||
|
||||
impl<P> Socket<P>
|
||||
where
|
||||
P: NetlinkFamily,
|
||||
{
|
||||
pub fn new() -> NetavarkResult<Socket<P>> {
|
||||
let mut socket = wrap!(netlink_sys::Socket::new(P::PROTOCOL), "open")?;
|
||||
let addr = &SocketAddr::new(0, 0);
|
||||
// Needs to be enabled for dump filtering to work
|
||||
socket.set_netlink_get_strict_chk(true)?;
|
||||
@@ -123,370 +73,15 @@ impl Socket {
|
||||
socket,
|
||||
sequence_number: 0,
|
||||
buffer: [0; 8192],
|
||||
_protocol: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_link(&mut self, id: LinkID) -> NetavarkResult<LinkMessage> {
|
||||
let mut msg = LinkMessage::default();
|
||||
|
||||
match id {
|
||||
LinkID::ID(id) => msg.header.index = id,
|
||||
LinkID::Name(name) => msg.attributes.push(LinkAttribute::IfName(name)),
|
||||
}
|
||||
|
||||
let mut result = self.make_netlink_request(RouteNetlinkMessage::GetLink(msg), 0)?;
|
||||
expect_netlink_result!(result, 1);
|
||||
match result.remove(0) {
|
||||
RouteNetlinkMessage::NewLink(m) => Ok(m),
|
||||
m => Err(NetavarkError::Message(format!(
|
||||
"unexpected netlink message type: {}",
|
||||
m.message_type()
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_link(&mut self, options: CreateLinkOptions) -> NetavarkResult<()> {
|
||||
let mut msg = LinkMessage::default();
|
||||
parse_create_link_options(&mut msg, options);
|
||||
let result = self.make_netlink_request(
|
||||
RouteNetlinkMessage::NewLink(msg),
|
||||
NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE,
|
||||
)?;
|
||||
expect_netlink_result!(result, 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_link_name(&mut self, id: u32, name: String) -> NetavarkResult<()> {
|
||||
let mut msg = LinkMessage::default();
|
||||
msg.header.index = id;
|
||||
msg.attributes.push(LinkAttribute::IfName(name));
|
||||
let result = self.make_netlink_request(RouteNetlinkMessage::SetLink(msg), NLM_F_ACK)?;
|
||||
expect_netlink_result!(result, 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn del_link(&mut self, id: LinkID) -> NetavarkResult<()> {
|
||||
let mut msg = LinkMessage::default();
|
||||
|
||||
match id {
|
||||
LinkID::ID(id) => msg.header.index = id,
|
||||
LinkID::Name(name) => msg.attributes.push(LinkAttribute::IfName(name)),
|
||||
}
|
||||
|
||||
let result = self.make_netlink_request(RouteNetlinkMessage::DelLink(msg), NLM_F_ACK)?;
|
||||
expect_netlink_result!(result, 0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_link_ns<Fd: AsFd>(&mut self, link_id: u32, netns: Fd) -> NetavarkResult<()> {
|
||||
let mut msg = LinkMessage::default();
|
||||
msg.header.index = link_id;
|
||||
msg.attributes
|
||||
.push(LinkAttribute::NetNsFd(netns.as_fd().as_raw_fd()));
|
||||
|
||||
let result = self.make_netlink_request(RouteNetlinkMessage::SetLink(msg), NLM_F_ACK)?;
|
||||
expect_netlink_result!(result, 0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// set the vlan_filtering attribute on a bridge
|
||||
pub fn set_vlan_filtering(&mut self, link_id: u32, vlan_filtering: bool) -> NetavarkResult<()> {
|
||||
let mut msg = LinkMessage::default();
|
||||
msg.header.index = link_id;
|
||||
msg.attributes.push(LinkAttribute::LinkInfo(vec![
|
||||
LinkInfo::Kind(InfoKind::Bridge),
|
||||
LinkInfo::Data(InfoData::Bridge(vec![InfoBridge::VlanFiltering(
|
||||
vlan_filtering,
|
||||
)])),
|
||||
]));
|
||||
|
||||
// Now idea why this must use NewLink not SetLink, I strace'd ip route
|
||||
// and they use newlink and which setlink here it does not error but also does not set the setting.
|
||||
let result = self.make_netlink_request(RouteNetlinkMessage::NewLink(msg), NLM_F_ACK)?;
|
||||
expect_netlink_result!(result, 0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// set the vlan id for an interface which is attached to the bridge with vlan_filtering
|
||||
/// Performs the equivalent of "bridge vlan add dev test vid <num> [flags]"
|
||||
pub fn set_vlan_id(
|
||||
&mut self,
|
||||
link_id: u32,
|
||||
// vlan id
|
||||
vid: u16,
|
||||
// flags for the vlan config
|
||||
flags: BridgeVlanInfoFlags,
|
||||
) -> NetavarkResult<()> {
|
||||
let mut msg = LinkMessage::default();
|
||||
msg.header.interface_family = AddressFamily::Bridge;
|
||||
// msg.header.link_layer_type = LinkLayerType::Netrom;
|
||||
msg.header.index = link_id;
|
||||
msg.attributes
|
||||
.push(LinkAttribute::AfSpecBridge(vec![AfSpecBridge::VlanInfo(
|
||||
BridgeVlanInfo { flags, vid },
|
||||
)]));
|
||||
|
||||
let result = self.make_netlink_request(RouteNetlinkMessage::SetLink(msg), NLM_F_ACK)?;
|
||||
expect_netlink_result!(result, 0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_addr_msg(link_id: u32, addr: &ipnet::IpNet) -> AddressMessage {
|
||||
let mut msg = AddressMessage::default();
|
||||
msg.header.index = link_id;
|
||||
|
||||
match addr {
|
||||
ipnet::IpNet::V4(v4) => {
|
||||
msg.header.family = AddressFamily::Inet;
|
||||
msg.attributes
|
||||
.push(netlink_packet_route::address::AddressAttribute::Broadcast(
|
||||
v4.broadcast(),
|
||||
));
|
||||
}
|
||||
ipnet::IpNet::V6(_) => {
|
||||
msg.header.family = AddressFamily::Inet6;
|
||||
}
|
||||
};
|
||||
|
||||
msg.header.prefix_len = addr.prefix_len();
|
||||
msg.attributes
|
||||
.push(netlink_packet_route::address::AddressAttribute::Local(
|
||||
addr.addr(),
|
||||
));
|
||||
msg
|
||||
}
|
||||
|
||||
pub fn add_addr(&mut self, link_id: u32, addr: &ipnet::IpNet) -> NetavarkResult<()> {
|
||||
let msg = Self::create_addr_msg(link_id, addr);
|
||||
let result = match self.make_netlink_request(
|
||||
RouteNetlinkMessage::NewAddress(msg),
|
||||
NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE,
|
||||
) {
|
||||
Ok(result) => result,
|
||||
Err(err) => match err {
|
||||
// kernel returns EACCES when we try to add an ipv6 but ipv6 is disabled in the kernel
|
||||
NetavarkError::Netlink(ref e) if -e.raw_code() == libc::EACCES => match addr {
|
||||
ipnet::IpNet::V6(_) => {
|
||||
return Err(NetavarkError::wrap(
|
||||
"failed to add ipv6 address, is ipv6 enabled in the kernel?",
|
||||
err,
|
||||
));
|
||||
}
|
||||
_ => return Err(err),
|
||||
},
|
||||
err => return Err(err),
|
||||
},
|
||||
};
|
||||
expect_netlink_result!(result, 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn del_addr(&mut self, link_id: u32, addr: &ipnet::IpNet) -> NetavarkResult<()> {
|
||||
let msg = Self::create_addr_msg(link_id, addr);
|
||||
let result = self.make_netlink_request(RouteNetlinkMessage::DelAddress(msg), NLM_F_ACK)?;
|
||||
expect_netlink_result!(result, 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_route_msg(route: &Route) -> RouteMessage {
|
||||
let mut msg = RouteMessage::default();
|
||||
|
||||
msg.header.table = libc::RT_TABLE_MAIN;
|
||||
msg.header.protocol = RouteProtocol::Static;
|
||||
msg.header.scope = RouteScope::Universe;
|
||||
msg.header.kind = RouteType::Unicast;
|
||||
|
||||
let (dest, dest_prefix, gateway, final_metric) = match route {
|
||||
Route::Ipv4 { dest, gw, metric } => {
|
||||
msg.header.address_family = AddressFamily::Inet;
|
||||
(
|
||||
RouteAddress::Inet(dest.addr()),
|
||||
dest.prefix_len(),
|
||||
RouteAddress::Inet(*gw),
|
||||
metric.unwrap_or(constants::DEFAULT_METRIC),
|
||||
)
|
||||
}
|
||||
Route::Ipv6 { dest, gw, metric } => {
|
||||
msg.header.address_family = AddressFamily::Inet6;
|
||||
(
|
||||
RouteAddress::Inet6(dest.addr()),
|
||||
dest.prefix_len(),
|
||||
RouteAddress::Inet6(*gw),
|
||||
metric.unwrap_or(constants::DEFAULT_METRIC),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
msg.header.destination_prefix_length = dest_prefix;
|
||||
msg.attributes
|
||||
.push(netlink_packet_route::route::RouteAttribute::Destination(
|
||||
dest,
|
||||
));
|
||||
msg.attributes
|
||||
.push(netlink_packet_route::route::RouteAttribute::Gateway(
|
||||
gateway,
|
||||
));
|
||||
msg.attributes
|
||||
.push(netlink_packet_route::route::RouteAttribute::Priority(
|
||||
final_metric,
|
||||
));
|
||||
msg
|
||||
}
|
||||
|
||||
pub fn add_route(&mut self, route: &Route) -> NetavarkResult<()> {
|
||||
let msg = Self::create_route_msg(route);
|
||||
info!("Adding route {route}");
|
||||
|
||||
let result = self
|
||||
.make_netlink_request(RouteNetlinkMessage::NewRoute(msg), NLM_F_ACK | NLM_F_CREATE)?;
|
||||
expect_netlink_result!(result, 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn del_route(&mut self, route: &Route) -> NetavarkResult<()> {
|
||||
let msg = Self::create_route_msg(route);
|
||||
info!("Deleting route {route}");
|
||||
|
||||
let result = self.make_netlink_request(RouteNetlinkMessage::DelRoute(msg), NLM_F_ACK)?;
|
||||
expect_netlink_result!(result, 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn dump_routes(&mut self) -> NetavarkResult<Vec<RouteMessage>> {
|
||||
let mut msg = RouteMessage::default();
|
||||
|
||||
msg.header.table = libc::RT_TABLE_MAIN;
|
||||
msg.header.protocol = RouteProtocol::Unspec;
|
||||
msg.header.scope = RouteScope::Universe;
|
||||
msg.header.kind = RouteType::Unicast;
|
||||
|
||||
let results =
|
||||
self.make_netlink_request(RouteNetlinkMessage::GetRoute(msg), NLM_F_DUMP | NLM_F_ACK)?;
|
||||
|
||||
let mut routes = Vec::with_capacity(results.len());
|
||||
|
||||
for res in results {
|
||||
match res {
|
||||
RouteNetlinkMessage::NewRoute(m) => routes.push(m),
|
||||
m => {
|
||||
return Err(NetavarkError::Message(format!(
|
||||
"unexpected netlink message type: {}",
|
||||
m.message_type()
|
||||
)))
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok(routes)
|
||||
}
|
||||
|
||||
pub fn dump_links(
|
||||
&mut self,
|
||||
nlas: &mut Vec<LinkAttribute>,
|
||||
) -> NetavarkResult<Vec<LinkMessage>> {
|
||||
let mut msg = LinkMessage::default();
|
||||
msg.attributes.append(nlas);
|
||||
|
||||
let results =
|
||||
self.make_netlink_request(RouteNetlinkMessage::GetLink(msg), NLM_F_DUMP | NLM_F_ACK)?;
|
||||
|
||||
let mut links = Vec::with_capacity(results.len());
|
||||
|
||||
for res in results {
|
||||
match res {
|
||||
RouteNetlinkMessage::NewLink(m) => links.push(m),
|
||||
m => {
|
||||
return Err(NetavarkError::Message(format!(
|
||||
"unexpected netlink message type: {}",
|
||||
m.message_type()
|
||||
)))
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok(links)
|
||||
}
|
||||
|
||||
// If filtering options are supplied, then only the ip addresses satisfying the filter are returned. Otherwise all ip addresses of all interfaces are returned
|
||||
pub fn dump_addresses(
|
||||
&mut self,
|
||||
interface_id_filter: Option<u32>,
|
||||
) -> NetavarkResult<Vec<AddressMessage>> {
|
||||
let mut msg = AddressMessage::default();
|
||||
|
||||
if let Some(id) = interface_id_filter {
|
||||
msg.header.index = id;
|
||||
}
|
||||
|
||||
let results =
|
||||
self.make_netlink_request(RouteNetlinkMessage::GetAddress(msg), NLM_F_DUMP)?;
|
||||
|
||||
let mut addresses = Vec::with_capacity(results.len());
|
||||
|
||||
for res in results {
|
||||
match res {
|
||||
RouteNetlinkMessage::NewAddress(m) => addresses.push(m),
|
||||
m => {
|
||||
return Err(NetavarkError::Message(format!(
|
||||
"unexpected netlink message type: {}",
|
||||
m.message_type()
|
||||
)))
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok(addresses)
|
||||
}
|
||||
|
||||
pub fn set_up(&mut self, id: LinkID) -> NetavarkResult<()> {
|
||||
let mut msg = LinkMessage::default();
|
||||
|
||||
match id {
|
||||
LinkID::ID(id) => msg.header.index = id,
|
||||
LinkID::Name(name) => msg.attributes.push(LinkAttribute::IfName(name)),
|
||||
}
|
||||
|
||||
msg.header.flags = LinkFlags::Up;
|
||||
msg.header.change_mask = LinkFlags::Up;
|
||||
|
||||
let result = self.make_netlink_request(
|
||||
RouteNetlinkMessage::SetLink(msg),
|
||||
NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE,
|
||||
)?;
|
||||
expect_netlink_result!(result, 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_mac_address(&mut self, id: LinkID, mac: Vec<u8>) -> NetavarkResult<()> {
|
||||
let mut msg = LinkMessage::default();
|
||||
|
||||
match id {
|
||||
LinkID::ID(id) => msg.header.index = id,
|
||||
LinkID::Name(name) => msg.attributes.push(LinkAttribute::IfName(name)),
|
||||
}
|
||||
|
||||
msg.attributes.push(LinkAttribute::Address(mac));
|
||||
|
||||
let result = self.make_netlink_request(RouteNetlinkMessage::SetLink(msg), NLM_F_ACK)?;
|
||||
expect_netlink_result!(result, 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn make_netlink_request(
|
||||
&mut self,
|
||||
msg: RouteNetlinkMessage,
|
||||
flags: u16,
|
||||
) -> NetavarkResult<Vec<RouteNetlinkMessage>> {
|
||||
self.send(msg, flags).wrap("send to netlink")?;
|
||||
self.recv(flags & NLM_F_DUMP == NLM_F_DUMP)
|
||||
}
|
||||
|
||||
fn send(&mut self, msg: RouteNetlinkMessage, flags: u16) -> NetavarkResult<()> {
|
||||
fn send(&mut self, msg: P::Message, flags: u16) -> NetavarkResult<()>
|
||||
where
|
||||
P::Message: NetlinkSerializable + std::fmt::Debug,
|
||||
NetlinkPayload<P::Message>: From<P::Message>,
|
||||
{
|
||||
let mut packet = NetlinkMessage::new(NetlinkHeader::default(), NetlinkPayload::from(msg));
|
||||
packet.header.flags = NLM_F_REQUEST | flags;
|
||||
packet.header.sequence_number = {
|
||||
@@ -502,7 +97,10 @@ impl Socket {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn recv(&mut self, multi: bool) -> NetavarkResult<Vec<RouteNetlinkMessage>> {
|
||||
fn recv(&mut self, multi: bool) -> NetavarkResult<Vec<P::Message>>
|
||||
where
|
||||
P::Message: NetlinkDeserializable + std::fmt::Debug,
|
||||
{
|
||||
let mut offset = 0;
|
||||
let mut result = Vec::new();
|
||||
|
||||
@@ -515,8 +113,8 @@ impl Socket {
|
||||
|
||||
loop {
|
||||
let bytes = &self.buffer[offset..];
|
||||
let rx_packet: NetlinkMessage<RouteNetlinkMessage> =
|
||||
NetlinkMessage::deserialize(bytes).map_err(|e| {
|
||||
let rx_packet: NetlinkMessage<P::Message> = NetlinkMessage::deserialize(bytes)
|
||||
.map_err(|e| {
|
||||
NetavarkError::Message(format!(
|
||||
"failed to deserialize netlink message: {e}",
|
||||
))
|
||||
@@ -565,60 +163,16 @@ impl Socket {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CreateLinkOptions<'_> {
|
||||
pub fn new(name: String, kind: InfoKind) -> Self {
|
||||
CreateLinkOptions {
|
||||
name,
|
||||
kind,
|
||||
info_data: None,
|
||||
mtu: 0,
|
||||
primary_index: 0,
|
||||
link: 0,
|
||||
mac: vec![],
|
||||
netns: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_create_link_options(msg: &mut LinkMessage, options: CreateLinkOptions) {
|
||||
// add link specific data
|
||||
let mut link_info_nlas = vec![LinkInfo::Kind(options.kind)];
|
||||
if let Some(data) = options.info_data {
|
||||
link_info_nlas.push(LinkInfo::Data(data));
|
||||
}
|
||||
msg.attributes.push(LinkAttribute::LinkInfo(link_info_nlas));
|
||||
|
||||
// add name
|
||||
if !options.name.is_empty() {
|
||||
msg.attributes.push(LinkAttribute::IfName(options.name));
|
||||
}
|
||||
|
||||
// add mtu
|
||||
if options.mtu != 0 {
|
||||
msg.attributes.push(LinkAttribute::Mtu(options.mtu));
|
||||
}
|
||||
|
||||
// add mac address
|
||||
if !options.mac.is_empty() {
|
||||
msg.attributes.push(LinkAttribute::Address(options.mac));
|
||||
}
|
||||
|
||||
// add primary device
|
||||
if options.primary_index != 0 {
|
||||
msg.attributes
|
||||
.push(LinkAttribute::Controller(options.primary_index));
|
||||
}
|
||||
|
||||
// add link device
|
||||
if options.link != 0 {
|
||||
msg.attributes.push(LinkAttribute::Link(options.link));
|
||||
}
|
||||
|
||||
// add netnsfd
|
||||
if let Some(netns) = options.netns {
|
||||
msg.attributes
|
||||
.push(LinkAttribute::NetNsFd(netns.as_raw_fd()));
|
||||
pub fn make_netlink_request(
|
||||
&mut self,
|
||||
msg: P::Message,
|
||||
flags: u16,
|
||||
) -> NetavarkResult<Vec<P::Message>>
|
||||
where
|
||||
P::Message: NetlinkSerializable + NetlinkDeserializable + std::fmt::Debug,
|
||||
NetlinkPayload<P::Message>: From<P::Message>,
|
||||
{
|
||||
self.send(msg, flags).wrap("send to netlink")?;
|
||||
self.recv(flags & NLM_F_DUMP == NLM_F_DUMP)
|
||||
}
|
||||
}
|
||||
|
||||
488
src/network/netlink_route.rs
Normal file
488
src/network/netlink_route.rs
Normal file
@@ -0,0 +1,488 @@
|
||||
use std::{
|
||||
net::{Ipv4Addr, Ipv6Addr},
|
||||
os::fd::{AsFd, AsRawFd, BorrowedFd},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
error::{NetavarkError, NetavarkResult},
|
||||
network::{
|
||||
constants,
|
||||
netlink::{expect_netlink_result, function, NetlinkFamily, Socket},
|
||||
},
|
||||
};
|
||||
use log::info;
|
||||
use netlink_packet_core::{NLM_F_ACK, NLM_F_CREATE, NLM_F_DUMP, NLM_F_EXCL};
|
||||
use netlink_packet_route::{
|
||||
address::AddressMessage,
|
||||
link::{
|
||||
AfSpecBridge, BridgeVlanInfo, BridgeVlanInfoFlags, InfoBridge, InfoData, InfoKind,
|
||||
LinkAttribute, LinkFlags, LinkInfo, LinkMessage,
|
||||
},
|
||||
route::{RouteAddress, RouteMessage, RouteProtocol, RouteScope, RouteType},
|
||||
AddressFamily, RouteNetlinkMessage,
|
||||
};
|
||||
use netlink_sys::protocols::NETLINK_ROUTE;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CreateLinkOptions<'fd> {
|
||||
pub name: String,
|
||||
kind: InfoKind,
|
||||
pub info_data: Option<InfoData>,
|
||||
pub mtu: u32,
|
||||
pub primary_index: u32,
|
||||
pub link: u32,
|
||||
pub mac: Vec<u8>,
|
||||
pub netns: Option<BorrowedFd<'fd>>,
|
||||
}
|
||||
|
||||
pub enum LinkID {
|
||||
ID(u32),
|
||||
Name(String),
|
||||
}
|
||||
|
||||
pub enum Route {
|
||||
Ipv4 {
|
||||
dest: ipnet::Ipv4Net,
|
||||
gw: Ipv4Addr,
|
||||
metric: Option<u32>,
|
||||
},
|
||||
Ipv6 {
|
||||
dest: ipnet::Ipv6Net,
|
||||
gw: Ipv6Addr,
|
||||
metric: Option<u32>,
|
||||
},
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Route {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let (dest, gw, metric) = match self {
|
||||
Route::Ipv4 { dest, gw, metric } => (
|
||||
dest.to_string(),
|
||||
gw.to_string(),
|
||||
metric.unwrap_or(constants::DEFAULT_METRIC),
|
||||
),
|
||||
Route::Ipv6 { dest, gw, metric } => (
|
||||
dest.to_string(),
|
||||
gw.to_string(),
|
||||
metric.unwrap_or(constants::DEFAULT_METRIC),
|
||||
),
|
||||
};
|
||||
write!(f, "(dest: {dest} ,gw: {gw}, metric {metric})")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NetlinkRoute;
|
||||
|
||||
impl NetlinkFamily for NetlinkRoute {
|
||||
const PROTOCOL: isize = NETLINK_ROUTE;
|
||||
type Message = RouteNetlinkMessage;
|
||||
}
|
||||
|
||||
impl Socket<NetlinkRoute> {
|
||||
pub fn get_link(&mut self, id: LinkID) -> NetavarkResult<LinkMessage> {
|
||||
let mut msg = LinkMessage::default();
|
||||
|
||||
match id {
|
||||
LinkID::ID(id) => msg.header.index = id,
|
||||
LinkID::Name(name) => msg.attributes.push(LinkAttribute::IfName(name)),
|
||||
}
|
||||
|
||||
let mut result = self.make_netlink_request(RouteNetlinkMessage::GetLink(msg), 0)?;
|
||||
expect_netlink_result!(result, 1);
|
||||
match result.remove(0) {
|
||||
RouteNetlinkMessage::NewLink(m) => Ok(m),
|
||||
m => Err(NetavarkError::Message(format!(
|
||||
"unexpected netlink message type: {}",
|
||||
m.message_type()
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_link(&mut self, options: CreateLinkOptions) -> NetavarkResult<()> {
|
||||
let mut msg = LinkMessage::default();
|
||||
parse_create_link_options(&mut msg, options);
|
||||
let result = self.make_netlink_request(
|
||||
RouteNetlinkMessage::NewLink(msg),
|
||||
NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE,
|
||||
)?;
|
||||
expect_netlink_result!(result, 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_link_name(&mut self, id: u32, name: String) -> NetavarkResult<()> {
|
||||
let mut msg = LinkMessage::default();
|
||||
msg.header.index = id;
|
||||
msg.attributes.push(LinkAttribute::IfName(name));
|
||||
let result = self.make_netlink_request(RouteNetlinkMessage::SetLink(msg), NLM_F_ACK)?;
|
||||
expect_netlink_result!(result, 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn del_link(&mut self, id: LinkID) -> NetavarkResult<()> {
|
||||
let mut msg = LinkMessage::default();
|
||||
|
||||
match id {
|
||||
LinkID::ID(id) => msg.header.index = id,
|
||||
LinkID::Name(name) => msg.attributes.push(LinkAttribute::IfName(name)),
|
||||
}
|
||||
|
||||
let result = self.make_netlink_request(RouteNetlinkMessage::DelLink(msg), NLM_F_ACK)?;
|
||||
expect_netlink_result!(result, 0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_link_ns<Fd: AsFd>(&mut self, link_id: u32, netns: Fd) -> NetavarkResult<()> {
|
||||
let mut msg = LinkMessage::default();
|
||||
msg.header.index = link_id;
|
||||
msg.attributes
|
||||
.push(LinkAttribute::NetNsFd(netns.as_fd().as_raw_fd()));
|
||||
|
||||
let result = self.make_netlink_request(RouteNetlinkMessage::SetLink(msg), NLM_F_ACK)?;
|
||||
expect_netlink_result!(result, 0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// set the vlan_filtering attribute on a bridge
|
||||
pub fn set_vlan_filtering(&mut self, link_id: u32, vlan_filtering: bool) -> NetavarkResult<()> {
|
||||
let mut msg = LinkMessage::default();
|
||||
msg.header.index = link_id;
|
||||
msg.attributes.push(LinkAttribute::LinkInfo(vec![
|
||||
LinkInfo::Kind(InfoKind::Bridge),
|
||||
LinkInfo::Data(InfoData::Bridge(vec![InfoBridge::VlanFiltering(
|
||||
vlan_filtering,
|
||||
)])),
|
||||
]));
|
||||
|
||||
// Now idea why this must use NewLink not SetLink, I strace'd ip route
|
||||
// and they use newlink and which setlink here it does not error but also does not set the setting.
|
||||
let result = self.make_netlink_request(RouteNetlinkMessage::NewLink(msg), NLM_F_ACK)?;
|
||||
expect_netlink_result!(result, 0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// set the vlan id for an interface which is attached to the bridge with vlan_filtering
|
||||
/// Performs the equivalent of "bridge vlan add dev test vid <num> [flags]"
|
||||
pub fn set_vlan_id(
|
||||
&mut self,
|
||||
link_id: u32,
|
||||
// vlan id
|
||||
vid: u16,
|
||||
// flags for the vlan config
|
||||
flags: BridgeVlanInfoFlags,
|
||||
) -> NetavarkResult<()> {
|
||||
let mut msg = LinkMessage::default();
|
||||
msg.header.interface_family = AddressFamily::Bridge;
|
||||
// msg.header.link_layer_type = LinkLayerType::Netrom;
|
||||
msg.header.index = link_id;
|
||||
msg.attributes
|
||||
.push(LinkAttribute::AfSpecBridge(vec![AfSpecBridge::VlanInfo(
|
||||
BridgeVlanInfo { flags, vid },
|
||||
)]));
|
||||
|
||||
let result = self.make_netlink_request(RouteNetlinkMessage::SetLink(msg), NLM_F_ACK)?;
|
||||
expect_netlink_result!(result, 0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_addr_msg(link_id: u32, addr: &ipnet::IpNet) -> AddressMessage {
|
||||
let mut msg = AddressMessage::default();
|
||||
msg.header.index = link_id;
|
||||
|
||||
match addr {
|
||||
ipnet::IpNet::V4(v4) => {
|
||||
msg.header.family = AddressFamily::Inet;
|
||||
msg.attributes
|
||||
.push(netlink_packet_route::address::AddressAttribute::Broadcast(
|
||||
v4.broadcast(),
|
||||
));
|
||||
}
|
||||
ipnet::IpNet::V6(_) => {
|
||||
msg.header.family = AddressFamily::Inet6;
|
||||
}
|
||||
};
|
||||
|
||||
msg.header.prefix_len = addr.prefix_len();
|
||||
msg.attributes
|
||||
.push(netlink_packet_route::address::AddressAttribute::Local(
|
||||
addr.addr(),
|
||||
));
|
||||
msg
|
||||
}
|
||||
|
||||
pub fn add_addr(&mut self, link_id: u32, addr: &ipnet::IpNet) -> NetavarkResult<()> {
|
||||
let msg = Self::create_addr_msg(link_id, addr);
|
||||
let result = match self.make_netlink_request(
|
||||
RouteNetlinkMessage::NewAddress(msg),
|
||||
NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE,
|
||||
) {
|
||||
Ok(result) => result,
|
||||
Err(err) => match err {
|
||||
// kernel returns EACCES when we try to add an ipv6 but ipv6 is disabled in the kernel
|
||||
NetavarkError::Netlink(ref e) if -e.raw_code() == libc::EACCES => match addr {
|
||||
ipnet::IpNet::V6(_) => {
|
||||
return Err(NetavarkError::wrap(
|
||||
"failed to add ipv6 address, is ipv6 enabled in the kernel?",
|
||||
err,
|
||||
));
|
||||
}
|
||||
_ => return Err(err),
|
||||
},
|
||||
err => return Err(err),
|
||||
},
|
||||
};
|
||||
expect_netlink_result!(result, 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn del_addr(&mut self, link_id: u32, addr: &ipnet::IpNet) -> NetavarkResult<()> {
|
||||
let msg = Self::create_addr_msg(link_id, addr);
|
||||
let result = self.make_netlink_request(RouteNetlinkMessage::DelAddress(msg), NLM_F_ACK)?;
|
||||
expect_netlink_result!(result, 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_route_msg(route: &Route) -> RouteMessage {
|
||||
let mut msg = RouteMessage::default();
|
||||
|
||||
msg.header.table = libc::RT_TABLE_MAIN;
|
||||
msg.header.protocol = RouteProtocol::Static;
|
||||
msg.header.scope = RouteScope::Universe;
|
||||
msg.header.kind = RouteType::Unicast;
|
||||
|
||||
let (dest, dest_prefix, gateway, final_metric) = match route {
|
||||
Route::Ipv4 { dest, gw, metric } => {
|
||||
msg.header.address_family = AddressFamily::Inet;
|
||||
(
|
||||
RouteAddress::Inet(dest.addr()),
|
||||
dest.prefix_len(),
|
||||
RouteAddress::Inet(*gw),
|
||||
metric.unwrap_or(constants::DEFAULT_METRIC),
|
||||
)
|
||||
}
|
||||
Route::Ipv6 { dest, gw, metric } => {
|
||||
msg.header.address_family = AddressFamily::Inet6;
|
||||
(
|
||||
RouteAddress::Inet6(dest.addr()),
|
||||
dest.prefix_len(),
|
||||
RouteAddress::Inet6(*gw),
|
||||
metric.unwrap_or(constants::DEFAULT_METRIC),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
msg.header.destination_prefix_length = dest_prefix;
|
||||
msg.attributes
|
||||
.push(netlink_packet_route::route::RouteAttribute::Destination(
|
||||
dest,
|
||||
));
|
||||
msg.attributes
|
||||
.push(netlink_packet_route::route::RouteAttribute::Gateway(
|
||||
gateway,
|
||||
));
|
||||
msg.attributes
|
||||
.push(netlink_packet_route::route::RouteAttribute::Priority(
|
||||
final_metric,
|
||||
));
|
||||
msg
|
||||
}
|
||||
|
||||
pub fn add_route(&mut self, route: &Route) -> NetavarkResult<()> {
|
||||
let msg = Self::create_route_msg(route);
|
||||
info!("Adding route {route}");
|
||||
|
||||
let result = self
|
||||
.make_netlink_request(RouteNetlinkMessage::NewRoute(msg), NLM_F_ACK | NLM_F_CREATE)?;
|
||||
expect_netlink_result!(result, 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn del_route(&mut self, route: &Route) -> NetavarkResult<()> {
|
||||
let msg = Self::create_route_msg(route);
|
||||
info!("Deleting route {route}");
|
||||
|
||||
let result = self.make_netlink_request(RouteNetlinkMessage::DelRoute(msg), NLM_F_ACK)?;
|
||||
expect_netlink_result!(result, 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn dump_routes(&mut self) -> NetavarkResult<Vec<RouteMessage>> {
|
||||
let mut msg = RouteMessage::default();
|
||||
|
||||
msg.header.table = libc::RT_TABLE_MAIN;
|
||||
msg.header.protocol = RouteProtocol::Unspec;
|
||||
msg.header.scope = RouteScope::Universe;
|
||||
msg.header.kind = RouteType::Unicast;
|
||||
|
||||
let results =
|
||||
self.make_netlink_request(RouteNetlinkMessage::GetRoute(msg), NLM_F_DUMP | NLM_F_ACK)?;
|
||||
|
||||
let mut routes = Vec::with_capacity(results.len());
|
||||
|
||||
for res in results {
|
||||
match res {
|
||||
RouteNetlinkMessage::NewRoute(m) => routes.push(m),
|
||||
m => {
|
||||
return Err(NetavarkError::Message(format!(
|
||||
"unexpected netlink message type: {}",
|
||||
m.message_type()
|
||||
)))
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok(routes)
|
||||
}
|
||||
|
||||
pub fn dump_links(
|
||||
&mut self,
|
||||
nlas: &mut Vec<LinkAttribute>,
|
||||
) -> NetavarkResult<Vec<LinkMessage>> {
|
||||
let mut msg = LinkMessage::default();
|
||||
msg.attributes.append(nlas);
|
||||
|
||||
let results =
|
||||
self.make_netlink_request(RouteNetlinkMessage::GetLink(msg), NLM_F_DUMP | NLM_F_ACK)?;
|
||||
|
||||
let mut links = Vec::with_capacity(results.len());
|
||||
|
||||
for res in results {
|
||||
match res {
|
||||
RouteNetlinkMessage::NewLink(m) => links.push(m),
|
||||
m => {
|
||||
return Err(NetavarkError::Message(format!(
|
||||
"unexpected netlink message type: {}",
|
||||
m.message_type()
|
||||
)))
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok(links)
|
||||
}
|
||||
|
||||
// If filtering options are supplied, then only the ip addresses satisfying the filter are returned. Otherwise all ip addresses of all interfaces are returned
|
||||
pub fn dump_addresses(
|
||||
&mut self,
|
||||
interface_id_filter: Option<u32>,
|
||||
) -> NetavarkResult<Vec<AddressMessage>> {
|
||||
let mut msg = AddressMessage::default();
|
||||
|
||||
if let Some(id) = interface_id_filter {
|
||||
msg.header.index = id;
|
||||
}
|
||||
|
||||
let results =
|
||||
self.make_netlink_request(RouteNetlinkMessage::GetAddress(msg), NLM_F_DUMP)?;
|
||||
|
||||
let mut addresses = Vec::with_capacity(results.len());
|
||||
|
||||
for res in results {
|
||||
match res {
|
||||
RouteNetlinkMessage::NewAddress(m) => addresses.push(m),
|
||||
m => {
|
||||
return Err(NetavarkError::Message(format!(
|
||||
"unexpected netlink message type: {}",
|
||||
m.message_type()
|
||||
)))
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok(addresses)
|
||||
}
|
||||
|
||||
pub fn set_up(&mut self, id: LinkID) -> NetavarkResult<()> {
|
||||
let mut msg = LinkMessage::default();
|
||||
|
||||
match id {
|
||||
LinkID::ID(id) => msg.header.index = id,
|
||||
LinkID::Name(name) => msg.attributes.push(LinkAttribute::IfName(name)),
|
||||
}
|
||||
|
||||
msg.header.flags = LinkFlags::Up;
|
||||
msg.header.change_mask = LinkFlags::Up;
|
||||
|
||||
let result = self.make_netlink_request(
|
||||
RouteNetlinkMessage::SetLink(msg),
|
||||
NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE,
|
||||
)?;
|
||||
expect_netlink_result!(result, 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_mac_address(&mut self, id: LinkID, mac: Vec<u8>) -> NetavarkResult<()> {
|
||||
let mut msg = LinkMessage::default();
|
||||
|
||||
match id {
|
||||
LinkID::ID(id) => msg.header.index = id,
|
||||
LinkID::Name(name) => msg.attributes.push(LinkAttribute::IfName(name)),
|
||||
}
|
||||
|
||||
msg.attributes.push(LinkAttribute::Address(mac));
|
||||
|
||||
let result = self.make_netlink_request(RouteNetlinkMessage::SetLink(msg), NLM_F_ACK)?;
|
||||
expect_netlink_result!(result, 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl CreateLinkOptions<'_> {
|
||||
pub fn new(name: String, kind: InfoKind) -> Self {
|
||||
CreateLinkOptions {
|
||||
name,
|
||||
kind,
|
||||
info_data: None,
|
||||
mtu: 0,
|
||||
primary_index: 0,
|
||||
link: 0,
|
||||
mac: vec![],
|
||||
netns: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_create_link_options(msg: &mut LinkMessage, options: CreateLinkOptions) {
|
||||
// add link specific data
|
||||
let mut link_info_nlas = vec![LinkInfo::Kind(options.kind)];
|
||||
if let Some(data) = options.info_data {
|
||||
link_info_nlas.push(LinkInfo::Data(data));
|
||||
}
|
||||
msg.attributes.push(LinkAttribute::LinkInfo(link_info_nlas));
|
||||
|
||||
// add name
|
||||
if !options.name.is_empty() {
|
||||
msg.attributes.push(LinkAttribute::IfName(options.name));
|
||||
}
|
||||
|
||||
// add mtu
|
||||
if options.mtu != 0 {
|
||||
msg.attributes.push(LinkAttribute::Mtu(options.mtu));
|
||||
}
|
||||
|
||||
// add mac address
|
||||
if !options.mac.is_empty() {
|
||||
msg.attributes.push(LinkAttribute::Address(options.mac));
|
||||
}
|
||||
|
||||
// add primary device
|
||||
if options.primary_index != 0 {
|
||||
msg.attributes
|
||||
.push(LinkAttribute::Controller(options.primary_index));
|
||||
}
|
||||
|
||||
// add link device
|
||||
if options.link != 0 {
|
||||
msg.attributes.push(LinkAttribute::Link(options.link));
|
||||
}
|
||||
|
||||
// add netnsfd
|
||||
if let Some(netns) = options.netns {
|
||||
msg.attributes
|
||||
.push(LinkAttribute::NetNsFd(netns.as_raw_fd()));
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,8 @@ use super::{
|
||||
driver::{DriverInfo, NetworkDriver},
|
||||
types,
|
||||
};
|
||||
use crate::network::netlink::Socket;
|
||||
use crate::network::netlink_route::NetlinkRoute;
|
||||
|
||||
pub struct PluginDriver<'a> {
|
||||
path: PathBuf,
|
||||
@@ -36,7 +38,7 @@ impl NetworkDriver for PluginDriver<'_> {
|
||||
|
||||
fn setup(
|
||||
&self,
|
||||
_netlink_sockets: (&mut super::netlink::Socket, &mut super::netlink::Socket),
|
||||
_netlink_sockets: (&mut Socket<NetlinkRoute>, &mut Socket<NetlinkRoute>),
|
||||
) -> NetavarkResult<(types::StatusBlock, Option<AardvarkEntry<'_>>)> {
|
||||
let result = self.exec_plugin(true, self.info.netns_path).wrap(format!(
|
||||
"plugin {:?} failed",
|
||||
@@ -49,7 +51,7 @@ impl NetworkDriver for PluginDriver<'_> {
|
||||
|
||||
fn teardown(
|
||||
&self,
|
||||
_netlink_sockets: (&mut super::netlink::Socket, &mut super::netlink::Socket),
|
||||
_netlink_sockets: (&mut Socket<NetlinkRoute>, &mut Socket<NetlinkRoute>),
|
||||
) -> NetavarkResult<()> {
|
||||
self.exec_plugin(false, self.info.netns_path).wrap(format!(
|
||||
"plugin {:?} failed",
|
||||
|
||||
@@ -2,6 +2,8 @@ use log::{debug, error};
|
||||
use std::os::fd::BorrowedFd;
|
||||
use std::{collections::HashMap, net::IpAddr};
|
||||
|
||||
use crate::network::netlink::Socket;
|
||||
use crate::network::netlink_route::{CreateLinkOptions, LinkID, NetlinkRoute};
|
||||
use netlink_packet_route::link::{
|
||||
InfoData, InfoIpVlan, InfoKind, InfoMacVlan, IpVlanMode, MacVlanMode,
|
||||
};
|
||||
@@ -25,7 +27,6 @@ use super::{
|
||||
core_utils::{self, get_ipam_addresses, get_mac_address, parse_option, CoreUtils},
|
||||
driver::{self, DriverInfo},
|
||||
internal_types::IPAMAddresses,
|
||||
netlink::{self, CreateLinkOptions},
|
||||
types::{NetInterface, StatusBlock},
|
||||
};
|
||||
|
||||
@@ -145,7 +146,7 @@ impl driver::NetworkDriver for Vlan<'_> {
|
||||
|
||||
fn setup(
|
||||
&self,
|
||||
netlink_sockets: (&mut netlink::Socket, &mut netlink::Socket),
|
||||
netlink_sockets: (&mut Socket<NetlinkRoute>, &mut Socket<NetlinkRoute>),
|
||||
) -> Result<(StatusBlock, Option<AardvarkEntry<'_>>), NetavarkError> {
|
||||
let data = match &self.data {
|
||||
Some(d) => d,
|
||||
@@ -218,7 +219,7 @@ impl driver::NetworkDriver for Vlan<'_> {
|
||||
|
||||
fn teardown(
|
||||
&self,
|
||||
netlink_sockets: (&mut netlink::Socket, &mut netlink::Socket),
|
||||
netlink_sockets: (&mut Socket<NetlinkRoute>, &mut Socket<NetlinkRoute>),
|
||||
) -> NetavarkResult<()> {
|
||||
dhcp_teardown(&self.info, netlink_sockets.1)?;
|
||||
|
||||
@@ -227,7 +228,7 @@ impl driver::NetworkDriver for Vlan<'_> {
|
||||
netlink_sockets.1.del_route(route)?;
|
||||
}
|
||||
|
||||
netlink_sockets.1.del_link(netlink::LinkID::Name(
|
||||
netlink_sockets.1.del_link(LinkID::Name(
|
||||
self.info.per_network_opts.interface_name.to_string(),
|
||||
))?;
|
||||
Ok(())
|
||||
@@ -235,8 +236,8 @@ impl driver::NetworkDriver for Vlan<'_> {
|
||||
}
|
||||
|
||||
fn setup(
|
||||
host: &mut netlink::Socket,
|
||||
netns: &mut netlink::Socket,
|
||||
host: &mut Socket<NetlinkRoute>,
|
||||
netns: &mut Socket<NetlinkRoute>,
|
||||
if_name: &str,
|
||||
data: &InternalData,
|
||||
hostns_fd: BorrowedFd<'_>,
|
||||
@@ -245,7 +246,7 @@ fn setup(
|
||||
) -> NetavarkResult<String> {
|
||||
let link = match data.host_interface_name.as_ref() {
|
||||
"" => get_default_route_interface(host)?,
|
||||
host_name => host.get_link(netlink::LinkID::Name(host_name.to_string()))?,
|
||||
host_name => host.get_link(LinkID::Name(host_name.to_string()))?,
|
||||
};
|
||||
|
||||
let opts = match kind_data {
|
||||
@@ -307,7 +308,7 @@ fn setup(
|
||||
}
|
||||
|
||||
let link = netns
|
||||
.get_link(netlink::LinkID::Name(tmp_name.clone()))
|
||||
.get_link(LinkID::Name(tmp_name.clone()))
|
||||
.wrap(format!("get tmp {kind_data} interface"))?;
|
||||
netns
|
||||
.set_link_name(link.header.index, if_name.to_string())
|
||||
@@ -315,8 +316,7 @@ fn setup(
|
||||
.inspect_err(|_| {
|
||||
// If there is an error here most likely the name in the netns is already used,
|
||||
// make sure to delete the tmp interface.
|
||||
if let Err(err) = netns.del_link(netlink::LinkID::ID(link.header.index))
|
||||
{
|
||||
if let Err(err) = netns.del_link(LinkID::ID(link.header.index)) {
|
||||
error!("failed to delete tmp {kind_data} link {tmp_name}: {err}");
|
||||
};
|
||||
})?;
|
||||
@@ -332,7 +332,7 @@ fn setup(
|
||||
exec_netns!(hostns_fd, netns_fd, { disable_ipv6_autoconf(if_name) })?;
|
||||
|
||||
let dev = netns
|
||||
.get_link(netlink::LinkID::Name(if_name.to_string()))
|
||||
.get_link(LinkID::Name(if_name.to_string()))
|
||||
.wrap(format!("get {kind_data} interface"))?;
|
||||
|
||||
for addr in &data.ipam.container_addresses {
|
||||
@@ -342,7 +342,7 @@ fn setup(
|
||||
}
|
||||
|
||||
netns
|
||||
.set_up(netlink::LinkID::ID(dev.header.index))
|
||||
.set_up(LinkID::ID(dev.header.index))
|
||||
.wrap(format!("set {kind_data} up"))?;
|
||||
|
||||
if !data.no_default_route {
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
mod tests {
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
|
||||
use netavark::network::netlink::*;
|
||||
use netavark::network::netlink::Socket;
|
||||
use netavark::network::netlink_route::{CreateLinkOptions, LinkID, NetlinkRoute, Route};
|
||||
use netlink_packet_route::{address, link::InfoKind};
|
||||
|
||||
macro_rules! test_setup {
|
||||
@@ -28,13 +29,16 @@ mod tests {
|
||||
#[test]
|
||||
fn test_socket_new() {
|
||||
test_setup!();
|
||||
assert!(Socket::new().is_ok(), "Netlink Socket::new() should work");
|
||||
assert!(
|
||||
Socket::<NetlinkRoute>::new().is_ok(),
|
||||
"Netlink Socket::new() should work"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_link() {
|
||||
test_setup!();
|
||||
let mut sock = Socket::new().expect("Socket::new()");
|
||||
let mut sock = Socket::<NetlinkRoute>::new().expect("Socket::new()");
|
||||
|
||||
let name = String::from("test1");
|
||||
sock.create_link(CreateLinkOptions::new(name.clone(), InfoKind::Dummy))
|
||||
@@ -49,7 +53,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_add_addr() {
|
||||
test_setup!();
|
||||
let mut sock = Socket::new().expect("Socket::new()");
|
||||
let mut sock = Socket::<NetlinkRoute>::new().expect("Socket::new()");
|
||||
|
||||
let out = run_command!("ip", "link", "add", "test1", "type", "dummy");
|
||||
eprintln!("{}", String::from_utf8(out.stderr).unwrap());
|
||||
@@ -72,7 +76,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_del_addr() {
|
||||
test_setup!();
|
||||
let mut sock = Socket::new().expect("Socket::new()");
|
||||
let mut sock = Socket::<NetlinkRoute>::new().expect("Socket::new()");
|
||||
|
||||
let out = run_command!("ip", "link", "add", "test1", "type", "dummy");
|
||||
eprintln!("{}", String::from_utf8(out.stderr).unwrap());
|
||||
@@ -110,7 +114,7 @@ mod tests {
|
||||
#[ignore]
|
||||
fn test_del_route() {
|
||||
test_setup!();
|
||||
let mut sock = Socket::new().expect("Socket::new()");
|
||||
let mut sock = Socket::<NetlinkRoute>::new().expect("Socket::new()");
|
||||
|
||||
let out = run_command!("ip", "link", "add", "test1", "type", "dummy");
|
||||
eprintln!("{}", String::from_utf8(out.stderr).unwrap());
|
||||
@@ -159,7 +163,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_dump_addr() {
|
||||
test_setup!();
|
||||
let mut sock = Socket::new().expect("Socket::new()");
|
||||
let mut sock = Socket::<NetlinkRoute>::new().expect("Socket::new()");
|
||||
|
||||
let out = run_command!("ip", "link", "add", "test1", "type", "dummy");
|
||||
eprintln!("{}", String::from_utf8(out.stderr).unwrap());
|
||||
@@ -190,7 +194,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_dump_addr_filter() {
|
||||
test_setup!();
|
||||
let mut sock = Socket::new().expect("Socket::new()");
|
||||
let mut sock = Socket::<NetlinkRoute>::new().expect("Socket::new()");
|
||||
|
||||
let out = run_command!("ip", "link", "add", "test1", "type", "dummy");
|
||||
eprintln!("{}", String::from_utf8(out.stderr).unwrap());
|
||||
|
||||
Reference in New Issue
Block a user