diff --git a/config/src/errors.rs b/config/src/errors.rs index a417f7e8c..067cb4509 100644 --- a/config/src/errors.rs +++ b/config/src/errors.rs @@ -77,6 +77,8 @@ pub enum ConfigError { InvalidIpAddress(String), #[error("Invalid mask length in interface address: {0}")] InvalidMaskLength(String), + #[error("Invalid configuration: {0}")] + Invalid(String), } /// Result-like type for configurations diff --git a/dataplane/src/drivers/kernel.rs b/dataplane/src/drivers/kernel.rs index a0d1ab180..ba41a5434 100644 --- a/dataplane/src/drivers/kernel.rs +++ b/dataplane/src/drivers/kernel.rs @@ -31,7 +31,8 @@ use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; use net::buffer::test_buffer::TestBuffer; -use net::packet::{InterfaceId, Packet}; +use net::interface::InterfaceIndex; +use net::packet::{DoneReason, Packet}; use netdev::Interface; use pipeline::{DynPipeline, NetworkFunction}; use tracing::{debug, error, info, trace, warn}; @@ -50,18 +51,18 @@ type WorkerChans = (Vec, WorkerRx); /// Simple representation of a kernel interface. pub struct Kif { - ifindex: u32, /* ifindex of interface */ - token: Token, /* token for polling */ - name: String, /* name of interface */ - sock: RawPacketStream, /* packet socket */ - raw_fd: RawFd, /* raw desc of packet socket */ + ifindex: InterfaceIndex, /* ifindex of interface */ + token: Token, /* token for polling */ + name: String, /* name of interface */ + sock: RawPacketStream, /* packet socket */ + raw_fd: RawFd, /* raw desc of packet socket */ } impl Kif { /// Create a kernel interface entry. Each interface gets a [`Token`] assigned /// and a packet socket opened, which gets registered in a poller to detect /// activity. - fn new(ifindex: u32, name: &str, token: Token) -> io::Result { + fn new(ifindex: InterfaceIndex, name: &str, token: Token) -> io::Result { let mut sock = RawPacketStream::new().map_err(|e| { error!("Failed to open raw sock for interface {name}: {e}"); e @@ -101,7 +102,7 @@ impl KifTable { } /// Add a kernel interface 'representor' to this table. For each interface, a packet socket /// is created and a poller [`Token`] assigned. - pub fn add(&mut self, ifindex: u32, name: &str) -> io::Result<()> { + pub fn add(&mut self, ifindex: InterfaceIndex, name: &str) -> io::Result<()> { debug!("Adding interface '{name}'..."); let token = Token(self.next_token); let interface = Kif::new(ifindex, name, token)?; @@ -122,9 +123,9 @@ impl KifTable { self.by_token.get_mut(&token) } - /// Get a mutable reference to the [`Kif`] with the indicated ifindex. - /// TODO: replace this linear search with a hash lookup if needed. - pub fn get_mut_by_index(&mut self, ifindex: u32) -> Option<&mut Kif> { + /// Get a mutable reference to the [`Kif`] with the indicated ifindex + /// Todo: replace this linear search with a hash lookup + pub fn get_mut_by_index(&mut self, ifindex: InterfaceIndex) -> Option<&mut Kif> { self.by_token .values_mut() .find(|kif| kif.ifindex == ifindex) @@ -132,19 +133,17 @@ impl KifTable { } /// Get the ifindex of the interface with the given name. -fn get_interface_ifindex(interfaces: &[Interface], name: &str) -> Option { +fn get_interface_ifindex(interfaces: &[Interface], name: &str) -> Option { interfaces .iter() .position(|interface| interface.name == name) - .map(|pos| interfaces[pos].index) + .and_then(|pos| InterfaceIndex::try_new(interfaces[pos].index).ok()) } /// Build a table of kernel interfaces to receive packets from (or send to). /// Interfaces of interest are indicated by --interface INTERFACE in the command line. /// Argument --interface ANY|any instructs the driver to capture on all interfaces. -fn build_kif_table( - args: impl IntoIterator + Clone>, -) -> io::Result { +fn build_kif_table(args: impl IntoIterator>) -> io::Result { /* learn about existing kernel network interfaces. We need these to know their ifindex */ let interfaces = netdev::get_interfaces(); @@ -162,7 +161,15 @@ fn build_kif_table( if ifnames.len() == 1 && ifnames[0].eq_ignore_ascii_case("ANY") { /* use all interfaces */ for interface in &interfaces { - if let Err(e) = kiftable.add(interface.index, &interface.name) { + let if_index = match InterfaceIndex::try_new(interface.index) { + Ok(if_index) => if_index, + Err(e) => match e { + net::interface::InterfaceIndexError::Zero => { + return Err(io::Error::new(io::ErrorKind::InvalidData, e)); + } + }, + }; + if let Err(e) = kiftable.add(if_index, &interface.name) { error!("Skipping interface '{}': {e}", interface.name); } } @@ -331,7 +338,7 @@ impl DriverKernel { // 1) Drain processed packets coming back from workers, serialize + TX while let Ok(mut pkt) = from_workers.try_recv() { // choose outgoing interface from meta - let oif_id_opt = pkt.get_meta().oif.as_ref().map(InterfaceId::get_id); + let oif_id_opt = pkt.get_meta().oif; if let Some(oif_id) = oif_id_opt { if let Some(outgoing) = kiftable.get_mut_by_index(oif_id) { match pkt.serialize() { @@ -423,7 +430,7 @@ impl DriverKernel { let buf = TestBuffer::from_raw_data(&raw[..bytes]); match Packet::new(buf) { Ok(mut incoming) => { - incoming.get_meta_mut().iif = InterfaceId::new(interface.ifindex); + incoming.get_meta_mut().iif = Some(interface.ifindex); pkts.push(Box::new(incoming)); } Err(e) => { diff --git a/dataplane/src/packet_processor/egress.rs b/dataplane/src/packet_processor/egress.rs index 0c4568884..39fa762b3 100644 --- a/dataplane/src/packet_processor/egress.rs +++ b/dataplane/src/packet_processor/egress.rs @@ -15,11 +15,12 @@ use net::{ }; use net::headers::TryEthMut; +use net::interface::InterfaceIndex; use net::packet::{DoneReason, Packet}; use pipeline::NetworkFunction; use routing::interfaces::iftablerw::IfTableReader; -use routing::interfaces::interface::{IfIndex, IfState, IfType, Interface}; +use routing::interfaces::interface::{IfState, IfType, Interface}; use routing::{atable::atablerw::AtableReader, interfaces::iftable::IfTable}; use tracectl::trace_target; @@ -122,7 +123,7 @@ impl Egress { &self, packet: &mut Packet, addr: IpAddr, - ifindex: IfIndex, + ifindex: InterfaceIndex, ) -> Option { let nfi = &self.name; @@ -153,7 +154,7 @@ impl Egress { fn resolve_next_mac( &self, - ifindex: IfIndex, + ifindex: InterfaceIndex, packet: &mut Packet, ) -> Option { let nfi = &self.name; @@ -181,7 +182,6 @@ impl Egress { }; /* resolve destination mac */ - let oif = oif.get_id(); let Some(dst_mac) = self.resolve_next_mac(oif, packet) else { // we could not figure out the destination MAC. // resolve_next_mac() already calls packet.done() diff --git a/dataplane/src/packet_processor/ingress.rs b/dataplane/src/packet_processor/ingress.rs index 66e2ec752..4924434ee 100644 --- a/dataplane/src/packet_processor/ingress.rs +++ b/dataplane/src/packet_processor/ingress.rs @@ -49,7 +49,7 @@ impl Ingress { match &interface.attachment { Some(Attachment::VRF(fibr)) => { let Some(vrfid) = fibr.get_id().map(|x| x.as_u32()) else { - /* we may ocassionaly not be able to enter a fib on reconfigs */ + /* we may occasionally not be able to enter a fib on reconfigs */ warn!("Failed to access fib on ingress!"); packet.done(DoneReason::Unroutable); return; @@ -145,6 +145,7 @@ impl Ingress { } impl NetworkFunction for Ingress { + #[tracing::instrument(level = "trace", skip(self, input))] fn process<'a, Input: Iterator> + 'a>( &'a mut self, input: Input, @@ -154,12 +155,19 @@ impl NetworkFunction for Ingress { let nfi = self.name(); if !packet.is_done() { if let Some(iftable) = self.iftr.enter() { - let iif = packet.get_meta().iif.get_id(); - if let Some(interface) = iftable.get_interface(iif) { - self.interface_ingress(interface, &mut packet); - } else { - warn!("{nfi}: unknown incoming interface {iif}"); - packet.done(DoneReason::InterfaceUnknown); + match packet.get_meta().iif { + None => { + warn!("no incoming interface for packet"); + } + Some(iif) => match iftable.get_interface(iif) { + None => { + warn!("{nfi}: unknown incoming interface {iif}"); + packet.done(DoneReason::InterfaceUnknown); + } + Some(interface) => { + self.interface_ingress(interface, &mut packet); + } + }, } } } diff --git a/dataplane/src/packet_processor/ipforward.rs b/dataplane/src/packet_processor/ipforward.rs index 2c7282496..0badc35ac 100644 --- a/dataplane/src/packet_processor/ipforward.rs +++ b/dataplane/src/packet_processor/ipforward.rs @@ -10,7 +10,7 @@ use std::net::IpAddr; use tracing::{debug, error, trace, warn}; use net::headers::{TryHeadersMut, TryIpv4Mut, TryIpv6Mut}; -use net::packet::{DoneReason, InterfaceId, Packet}; +use net::packet::{DoneReason, Packet}; use net::{buffer::PacketBufferMut, checksum::Checksum}; use pipeline::NetworkFunction; @@ -20,12 +20,12 @@ use routing::fib::fibtable::FibTableReader; use routing::fib::fibtype::FibId; use routing::evpn::Vtep; -use routing::interfaces::interface::IfIndex; use routing::rib::encapsulation::{Encapsulation, VxlanEncapsulation}; use routing::rib::vrf::VrfId; use net::headers::Headers; use net::headers::Net; +use net::interface::InterfaceIndex; use net::ip::NextHeader; use net::ipv4::Ipv4; use net::ipv4::UnicastIpv4Addr; @@ -123,7 +123,7 @@ impl IpForwarder { &self, packet: &mut Packet, fibtable: &FibTable, - _ifindex: IfIndex, /* we get it from metadata */ + _ifindex: InterfaceIndex, /* we get it from metadata */ ) { let nfi = &self.name; @@ -313,7 +313,7 @@ impl IpForwarder { ) { let meta = packet.get_meta_mut(); if let Some(ifindex) = egress.ifindex() { - meta.oif = Some(InterfaceId::new(*ifindex)); + meta.oif = Some(*ifindex); } if let Some(addr) = egress.address() { meta.nh_addr = Some(*addr); diff --git a/mgmt/src/processor/confbuild/router.rs b/mgmt/src/processor/confbuild/router.rs index 9bac3141d..4fcd01e38 100644 --- a/mgmt/src/processor/confbuild/router.rs +++ b/mgmt/src/processor/confbuild/router.rs @@ -12,7 +12,7 @@ use crate::processor::confbuild::namegen::VpcInterfacesNames; use std::collections::HashMap; use tracing::{debug, error}; -use net::interface::{Interface, InterfaceName, Mtu}; +use net::interface::{Interface, InterfaceIndex, InterfaceName, Mtu}; use routing::interfaces::interface::{AttachConfig, IfDataEthernet, IfState, IfType}; use config::internal::interfaces::interface::InterfaceConfig; @@ -61,7 +61,15 @@ fn build_router_interface_config( vrfid: VrfId, ) -> Result { let name = kiface.name.as_str(); - let mut new = RouterInterfaceConfig::new(name, kiface.index); + let ifindex = match InterfaceIndex::try_new(kiface.index) { + Ok(ifindex) => ifindex, + Err(err) => { + let msg = format!("{err}"); + error!("failed to build router interface: {err}"); + return Err(ConfigError::Invalid(msg)); + } + }; + let mut new = RouterInterfaceConfig::new(name, ifindex); // set admin status -- currently from oper let status = if kiface.is_up() { diff --git a/net/src/packet/display.rs b/net/src/packet/display.rs index 492541da7..5605127ea 100644 --- a/net/src/packet/display.rs +++ b/net/src/packet/display.rs @@ -14,7 +14,9 @@ use crate::udp::{Udp, UdpEncap}; use crate::buffer::PacketBufferMut; use crate::checksum::Checksum; -use crate::packet::{BridgeDomain, DoneReason, InterfaceId, InvalidPacket, Packet, PacketMeta}; +#[allow(deprecated)] +use crate::packet::InterfaceId; +use crate::packet::{BridgeDomain, DoneReason, InvalidPacket, Packet, PacketMeta}; use std::fmt::{Display, Formatter}; impl Display for Eth { @@ -217,6 +219,7 @@ fn fmt_opt( } } } +#[allow(deprecated)] impl Display for InterfaceId { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.get_id()) @@ -230,7 +233,14 @@ impl Display for BridgeDomain { impl Display for PacketMeta { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { writeln!(f, " metadata:")?; - write!(f, " iif: {}", self.iif.get_id())?; + match self.iif { + None => { + write!(f, " iif: None")?; + } + Some(iif) => { + write!(f, " iif: {iif}")?; + } + } fmt_opt(f, " oif", self.oif, true)?; writeln!(f, " bcast: {}", self.is_l2bcast())?; diff --git a/net/src/packet/meta.rs b/net/src/packet/meta.rs index 37f39b586..3a5802a3f 100644 --- a/net/src/packet/meta.rs +++ b/net/src/packet/meta.rs @@ -3,6 +3,7 @@ #![allow(missing_docs)] // TODO +use crate::interface::InterfaceIndex; use crate::vxlan::Vni; use bitflags::bitflags; use concurrency::sync::Arc; @@ -16,9 +17,10 @@ use tracing::error; /// Every VRF is univocally identified with a numerical VRF id pub type VrfId = u32; +#[deprecated = "use InterfaceIndex from net"] #[derive(Debug, Default, Copy, Clone)] pub struct InterfaceId(u32); -#[allow(unused)] +#[allow(deprecated)] impl InterfaceId { #[must_use] pub fn new(val: u32) -> Self { @@ -130,11 +132,11 @@ bitflags! { #[derive(Debug, Default, Clone)] pub struct PacketMeta { flags: MetaFlags, - pub iif: InterfaceId, /* incoming interface - set early */ - pub oif: Option, /* outgoing interface - set late */ - pub nh_addr: Option, /* IP address of next-hop */ - pub vrf: Option, /* for IP packet, the VRF to use to route it */ - pub bridge: Option, /* the bridge domain to forward the packet to */ + pub iif: Option, /* incoming interface - set early */ + pub oif: Option, /* outgoing interface - set late */ + pub nh_addr: Option, /* IP address of next-hop */ + pub vrf: Option, /* for IP packet, the VRF to use to route it */ + pub bridge: Option, /* the bridge domain to forward the packet to */ pub done: Option, /* if Some, the reason why a packet was marked as done, including delivery to NF */ pub src_vpcd: Option, /* the vpc discriminant of a received encapsulated packet */ pub dst_vpcd: Option, /* the vpc discriminant of a packet to be (or already) re-encapsulated by the gateway */ diff --git a/routing/src/atable/adjacency.rs b/routing/src/atable/adjacency.rs index 48b8cfccc..b637da4b1 100644 --- a/routing/src/atable/adjacency.rs +++ b/routing/src/atable/adjacency.rs @@ -3,10 +3,9 @@ //! State objects to keep adjacency information -use crate::interfaces::interface::IfIndex; use ahash::RandomState; -use dplane_rpc::msg::Ifindex; use net::eth::mac::Mac; +use net::interface::InterfaceIndex; use std::collections::HashMap; use std::net::IpAddr; @@ -14,14 +13,14 @@ use std::net::IpAddr; /// Object that represents an adjacency or ARP/ND entry pub struct Adjacency { address: IpAddr, - ifindex: IfIndex, + ifindex: InterfaceIndex, mac: Mac, } impl Adjacency { /// Create an [`Adjacency`] object #[must_use] - pub fn new(address: IpAddr, ifindex: IfIndex, mac: Mac) -> Self { + pub fn new(address: IpAddr, ifindex: InterfaceIndex, mac: Mac) -> Self { Self { address, ifindex, @@ -30,7 +29,7 @@ impl Adjacency { } /// Get the Ifindex of an [`Adjacency`] object #[must_use] - pub fn get_ifindex(&self) -> Ifindex { + pub fn get_ifindex(&self) -> InterfaceIndex { self.ifindex } @@ -49,7 +48,7 @@ impl Adjacency { /// A table of [`Adjacency`]ies #[derive(Default, Clone)] -pub struct AdjacencyTable(HashMap<(IfIndex, IpAddr), Adjacency, RandomState>); +pub struct AdjacencyTable(HashMap<(InterfaceIndex, IpAddr), Adjacency, RandomState>); impl AdjacencyTable { #[must_use] @@ -64,7 +63,7 @@ impl AdjacencyTable { pub fn is_empty(&self) -> bool { self.0.is_empty() } - pub fn iter(&self) -> impl Iterator { + pub fn iter(&self) -> impl Iterator { self.0.iter() } pub fn values(&self) -> impl Iterator { @@ -74,11 +73,11 @@ impl AdjacencyTable { self.0 .insert((adjacency.ifindex, adjacency.address), adjacency); } - pub fn del_adjacency(&mut self, address: IpAddr, ifindex: IfIndex) { + pub fn del_adjacency(&mut self, address: IpAddr, ifindex: InterfaceIndex) { self.0.remove(&(ifindex, address)); } #[must_use] - pub fn get_adjacency(&self, address: IpAddr, ifindex: IfIndex) -> Option<&Adjacency> { + pub fn get_adjacency(&self, address: IpAddr, ifindex: InterfaceIndex) -> Option<&Adjacency> { self.0.get(&(ifindex, address)) } pub fn clear(&mut self) { @@ -89,25 +88,26 @@ impl AdjacencyTable { #[cfg(test)] #[rustfmt::skip] pub mod tests { - use super::*; - use crate::rib::vrf::tests::mk_addr; +use super::*; +use crate::rib::vrf::tests::mk_addr; +use net::interface::InterfaceIndex; pub fn build_test_atable() -> AdjacencyTable { let mut atable = AdjacencyTable::new(); { let ip = mk_addr("10.0.0.1"); let mac = Mac::from([0x0, 0x0, 0x0, 0x0 ,0xaa, 0x1]); - atable.add_adjacency(Adjacency::new(ip, 2, mac)); + atable.add_adjacency(Adjacency::new(ip, InterfaceIndex::try_new(2).unwrap(), mac)); } { let ip = mk_addr("10.0.0.5"); let mac = Mac::from([0x0, 0x0, 0x0, 0x0 ,0xaa, 0x5]); - atable.add_adjacency(Adjacency::new(ip, 3, mac)); + atable.add_adjacency(Adjacency::new(ip, InterfaceIndex::try_new(3).unwrap(), mac)); } { let ip = mk_addr("10.0.0.9"); let mac = Mac::from([0x0, 0x0, 0x0, 0x0 ,0xaa, 0x9]); - atable.add_adjacency(Adjacency::new(ip, 4, mac )); + atable.add_adjacency(Adjacency::new(ip, InterfaceIndex::try_new(4).unwrap(), mac )); } atable } @@ -118,11 +118,11 @@ pub mod tests { let ip = mk_addr("10.0.0.1"); let mac = Mac::from([0x0, 0x0, 0x0, 0x0 ,0x0, 0x1]); - let a1 = Adjacency::new(ip, 10, mac); + let a1 = Adjacency::new(ip, InterfaceIndex::try_new(10).unwrap(), mac); atable.add_adjacency(a1); - assert_eq!(atable.get_adjacency(ip, 10).unwrap().mac, mac); + assert_eq!(atable.get_adjacency(ip, InterfaceIndex::try_new(10).unwrap()).unwrap().mac, mac); - atable.del_adjacency(ip, 10); - assert!(atable.get_adjacency(ip, 10).is_none()); + atable.del_adjacency(ip, InterfaceIndex::try_new(10).unwrap()); + assert!(atable.get_adjacency(ip, InterfaceIndex::try_new(10).unwrap()).is_none()); } } diff --git a/routing/src/atable/atablerw.rs b/routing/src/atable/atablerw.rs index 45443c228..f6d7e45a1 100644 --- a/routing/src/atable/atablerw.rs +++ b/routing/src/atable/atablerw.rs @@ -3,15 +3,14 @@ //! Adjacency table left-right +use crate::atable::adjacency::{Adjacency, AdjacencyTable}; use left_right::{Absorb, ReadGuard, ReadHandle, ReadHandleFactory, WriteHandle}; +use net::interface::InterfaceIndex; use std::net::IpAddr; -use crate::atable::adjacency::{Adjacency, AdjacencyTable}; -use crate::interfaces::interface::IfIndex; - enum AtableChange { Add(Adjacency), - Del((IpAddr, IfIndex)), + Del((IpAddr, InterfaceIndex)), Clear, } @@ -50,7 +49,7 @@ impl AtableWriter { self.0.publish(); } } - pub fn del_adjacency(&mut self, address: IpAddr, ifindex: IfIndex, publish: bool) { + pub fn del_adjacency(&mut self, address: IpAddr, ifindex: InterfaceIndex, publish: bool) { self.0.append(AtableChange::Del((address, ifindex))); if publish { self.0.publish(); diff --git a/routing/src/atable/resolver.rs b/routing/src/atable/resolver.rs index 260dca4b0..05befeef3 100644 --- a/routing/src/atable/resolver.rs +++ b/routing/src/atable/resolver.rs @@ -14,20 +14,29 @@ use netdev::Interface; use netdev::get_interfaces; use procfs::net::arp; +use super::adjacency::Adjacency; +use super::atablerw::AtableReader; use crate::atable::atablerw::AtableWriter; use net::eth::mac::Mac; +use net::interface::{InterfaceIndex, InterfaceIndexError}; use tracing::{debug, error, warn}; -use super::adjacency::Adjacency; -use super::atablerw::AtableReader; - /// Util that returns the ifindex of the interface with the given name out of the slice of /// interfaces provided as argument. -fn get_interface_ifindex(interfaces: &[Interface], name: &str) -> Option { +fn get_interface_ifindex( + interfaces: &[Interface], + name: &str, +) -> Result, InterfaceIndexError> { interfaces .iter() - .position(|interface| interface.name == name) - .map(|pos| interfaces[pos].index) + .find_map(|interface| { + if interface.name == name { + Some(InterfaceIndex::try_new(interface.index)) + } else { + None + } + }) + .transpose() } /// An object able to resolve ARP entries and update the adjacency table. The [`AtResolver`] @@ -106,13 +115,23 @@ impl AtResolver { if let Ok(arptable) = arp() { let adjs = arptable.iter().filter_map(|entry| { if let Some(mac) = entry.hw_address { - if let Some(ifindex) = get_interface_ifindex(&interfaces, &entry.device) { - let adj = - Adjacency::new(IpAddr::V4(entry.ip_address), ifindex, Mac::from(mac)); - Some(adj) - } else { - warn!("Unable to find Ifindex of {0}", entry.device); - None + match get_interface_ifindex(&interfaces, &entry.device) { + Ok(Some(ifindex)) => { + let adj = Adjacency::new( + IpAddr::V4(entry.ip_address), + ifindex, + Mac::from(mac), + ); + Some(adj) + } + Ok(None) => { + warn!("Unable to find Ifindex of {0}", entry.device); + None + } + Err(e) => { + error!("error refreshing ARP/ND table: {e}"); + None + } } } else { None diff --git a/routing/src/config/interface.rs b/routing/src/config/interface.rs index d95a3a6b6..49072e139 100644 --- a/routing/src/config/interface.rs +++ b/routing/src/config/interface.rs @@ -3,13 +3,12 @@ //! Router interface configuration -#[allow(unused)] -use tracing::debug; - use crate::RouterError; use crate::config::RouterConfig; use crate::interfaces::iftable::IfTable; -use crate::interfaces::interface::IfIndex; +use net::interface::InterfaceIndex; +#[allow(unused)] +use tracing::debug; use crate::interfaces::iftablerw::IfTableWriter; use crate::interfaces::interface::{AttachConfig, Attachment}; @@ -20,8 +19,8 @@ use crate::rib::VrfTable; /////////////////////////////////////////////////////////////////////////////////////// pub(crate) struct ReconfigInterfacePlan { #[allow(unused)] - to_keep: Vec, /* interfaces to keep as is */ - to_delete: Vec, /* interfaces to delete */ + to_keep: Vec, /* interfaces to keep as is */ + to_delete: Vec, /* interfaces to delete */ to_modify: Vec, /* interfaces to change */ to_add: Vec, /* interfaces to add */ } @@ -32,8 +31,8 @@ impl ReconfigInterfacePlan { /////////////////////////////////////////////////////////////////////////////////// #[must_use] pub(crate) fn generate(config: &RouterConfig, iftable: &IfTable) -> Self { - let mut to_delete: Vec = vec![]; - let mut to_keep: Vec = vec![]; + let mut to_delete: Vec = vec![]; + let mut to_keep: Vec = vec![]; let mut to_modify: Vec = vec![]; let mut to_add: Vec = vec![]; diff --git a/routing/src/config/mod.rs b/routing/src/config/mod.rs index 6c1996b89..dc7444a4c 100644 --- a/routing/src/config/mod.rs +++ b/routing/src/config/mod.rs @@ -9,20 +9,19 @@ mod interface; mod vrf; mod vtep; -use config::GenId; -use net::vxlan::Vni; -use std::collections::{BTreeMap, BTreeSet}; -use std::fmt::format; -use tracing::{debug, error}; - use crate::RouterError; use crate::evpn::Vtep; use crate::interfaces::iftable::IfTable; -use crate::interfaces::interface::IfIndex; use crate::interfaces::interface::RouterInterfaceConfig; use crate::rib::VrfTable; use crate::rib::vrf::{RouterVrfConfig, VrfId}; use crate::routingdb::RoutingDb; +use config::GenId; +use net::interface::InterfaceIndex; +use net::vxlan::Vni; +use std::collections::{BTreeMap, BTreeSet}; +use std::fmt::format; +use tracing::{debug, error}; use crate::config::interface::ReconfigInterfacePlan; use crate::config::vrf::ReconfigVrfPlan; @@ -37,7 +36,7 @@ pub type FrrConfig = String; pub struct RouterConfig { genid: GenId, vrfs: BTreeMap, - interfaces: BTreeMap, + interfaces: BTreeMap, vtep: Option, frr_cfg: Option, } @@ -126,7 +125,7 @@ impl RouterConfig { /// Get the config for an interface with a given [`IfIndex`] ////////////////////////////////////////////////////////////////////////////////// #[must_use] - fn get_interface(&self, ifindex: IfIndex) -> Option<&RouterInterfaceConfig> { + fn get_interface(&self, ifindex: InterfaceIndex) -> Option<&RouterInterfaceConfig> { self.interfaces.get(&ifindex) } @@ -135,7 +134,7 @@ impl RouterConfig { ////////////////////////////////////////////////////////////////////////////////// #[cfg(test)] #[must_use] - fn get_interface_mut(&mut self, ifindex: IfIndex) -> Option<&mut RouterInterfaceConfig> { + fn get_interface_mut(&mut self, ifindex: InterfaceIndex) -> Option<&mut RouterInterfaceConfig> { self.interfaces.get_mut(&ifindex) } @@ -237,7 +236,8 @@ mod tests { use tracing::debug; use net::{route::RouteTableId, vxlan::Vni}; use net::eth::mac::Mac; - use crate::{config::RouterConfig, evpn::Vtep, interfaces::interface::{AttachConfig, RouterInterfaceConfig}, rib::vrf::RouterVrfConfig}; +use net::interface::InterfaceIndex; +use crate::{config::RouterConfig, evpn::Vtep, interfaces::interface::{AttachConfig, RouterInterfaceConfig}, rib::vrf::RouterVrfConfig}; use crate::interfaces::interface::IfState; use crate::interfaces::interface::IfType; use crate::interfaces::interface::IfDataEthernet; @@ -282,13 +282,15 @@ mod tests { } fn add_router_interface_configs(config: &mut RouterConfig) { - let mut ifconfig = RouterInterfaceConfig::new("Loopback", 1); + let lo_idx = InterfaceIndex::try_new(1).unwrap(); + let mut ifconfig = RouterInterfaceConfig::new("Loopback", lo_idx); ifconfig.set_description("main loopback interface"); ifconfig.set_iftype(IfType::Loopback); ifconfig.set_admin_state(IfState::Up); config.add_interface(ifconfig); - let mut ifconfig = RouterInterfaceConfig::new("Eth0", 10); + let eth0_idx = InterfaceIndex::try_new(10).unwrap(); + let mut ifconfig = RouterInterfaceConfig::new("Eth0", eth0_idx); ifconfig.set_description("Interface to Spine-1"); ifconfig.set_admin_state(IfState::Up); ifconfig.set_iftype(IfType::Ethernet(IfDataEthernet { @@ -297,7 +299,8 @@ mod tests { ifconfig.set_attach_cfg(Some(AttachConfig::VRF(100))); config.add_interface(ifconfig); - let mut ifconfig = RouterInterfaceConfig::new("Eth1", 11); + let eth1_idx = InterfaceIndex::try_new(11).unwrap(); + let mut ifconfig = RouterInterfaceConfig::new("Eth1", eth1_idx); ifconfig.set_description("Interface to Spine-2"); ifconfig.set_admin_state(IfState::Up); ifconfig.set_iftype(IfType::Ethernet(IfDataEthernet { @@ -436,7 +439,8 @@ mod tests { debug!("━━━━━━━━ Test: Change interface name, mac, admin state and attach it to another vrf"); config.genid = 6; - let ifconfig = config.get_interface_mut(10).expect("Should find config"); + let idx = InterfaceIndex::try_new(10).unwrap(); + let ifconfig = config.get_interface_mut(idx).expect("Should find config"); ifconfig.set_name("CHANGED-NAME"); ifconfig.set_description("Interface with changed config"); ifconfig.set_admin_state(IfState::Down); @@ -448,7 +452,7 @@ mod tests { debug!("━━━━━━━━ Test: Detach interface"); config.genid = 7; - let ifconfig = config.get_interface_mut(10).expect("Should find config"); + let ifconfig = config.get_interface_mut(idx).expect("Should find config"); ifconfig.set_attach_cfg(None); test_apply_config(&config, &mut db).expect("Should succeed"); } diff --git a/routing/src/cpi.rs b/routing/src/cpi.rs index f6306bd70..46a6d0bad 100644 --- a/routing/src/cpi.rs +++ b/routing/src/cpi.rs @@ -18,6 +18,7 @@ use std::os::unix::net::SocketAddr; use std::process; use std::time::{SystemTime, UNIX_EPOCH}; +use net::interface::InterfaceIndex; #[allow(unused)] use tracing::{debug, error, info, trace, warn}; @@ -264,13 +265,27 @@ impl RpcOperation for Rmac { impl RpcOperation for IfAddress { type ObjectStore = RoutingDb; fn add(&self, db: &mut Self::ObjectStore) -> RpcResultCode { + let ifindex = match InterfaceIndex::try_new(self.ifindex) { + Ok(idx) => idx, + Err(e) => { + error!("unable to add interface address: {e}"); + return RpcResultCode::InvalidRequest; + } + }; db.iftw - .add_ip_address(self.ifindex, (self.address, self.mask_len)); + .add_ip_address(ifindex, (self.address, self.mask_len)); RpcResultCode::Ok } fn del(&self, db: &mut Self::ObjectStore) -> RpcResultCode { + let ifindex = match InterfaceIndex::try_new(self.ifindex) { + Ok(idx) => idx, + Err(e) => { + error!("unable to remove interface address: {e}"); + return RpcResultCode::InvalidRequest; + } + }; db.iftw - .del_ip_address(self.ifindex, (self.address, self.mask_len)); + .del_ip_address(ifindex, (self.address, self.mask_len)); RpcResultCode::Ok } } diff --git a/routing/src/errors.rs b/routing/src/errors.rs index 0e555974a..34e3759ba 100644 --- a/routing/src/errors.rs +++ b/routing/src/errors.rs @@ -3,12 +3,13 @@ //! The error results used by this library. +use net::interface::InterfaceIndex; use thiserror::Error; #[derive(Error, Debug, PartialEq)] pub enum RouterError { #[error("No interface with ifindex {0}")] - NoSuchInterface(u32), + NoSuchInterface(InterfaceIndex), #[error("No such VRF")] NoSuchVrf, @@ -23,7 +24,7 @@ pub enum RouterError { VniInvalid(u32), #[error("An interface with ifindex {0} already exists")] - InterfaceExists(u32), + InterfaceExists(InterfaceIndex), #[error("Invalid socket path '{0}'")] InvalidPath(String), diff --git a/routing/src/fib/fibgroupstore.rs b/routing/src/fib/fibgroupstore.rs index b2eb39621..fbf742e68 100644 --- a/routing/src/fib/fibgroupstore.rs +++ b/routing/src/fib/fibgroupstore.rs @@ -249,6 +249,8 @@ impl FibRoute { #[cfg(test)] pub mod tests { + use net::interface::InterfaceIndex; + use crate::fib::fibgroupstore::{FibError, FibGroupStore, FibRoute}; use crate::fib::fibobjects::EgressObject; use crate::fib::fibobjects::FibEntry; @@ -263,8 +265,11 @@ pub mod tests { fn build_fib_entry_egress(ifindex: u32, address: &str, ifname: &str) -> FibEntry { let addr = Some(IpAddr::from_str(address).unwrap()); let ifname = Some(ifname.to_string()); - let ifindex = Some(ifindex); - let inst = PktInstruction::Egress(EgressObject::new(ifindex, addr, ifname)); + let inst = PktInstruction::Egress(EgressObject::new( + InterfaceIndex::try_new(ifindex).ok(), + addr, + ifname, + )); FibEntry::with_inst(inst) } // builds fibgroup with a single entry diff --git a/routing/src/fib/fibobjects.rs b/routing/src/fib/fibobjects.rs index 11869a717..8234836d0 100644 --- a/routing/src/fib/fibobjects.rs +++ b/routing/src/fib/fibobjects.rs @@ -5,8 +5,8 @@ use net::vxlan::Vni; -use crate::interfaces::interface::IfIndex; use crate::rib::encapsulation::Encapsulation; +use net::interface::InterfaceIndex; use std::net::IpAddr; #[derive(Debug, Default, Clone, Ord, PartialOrd, Eq, PartialEq)] @@ -14,14 +14,18 @@ use std::net::IpAddr; /// has to be sent and, optionally, a next-hop ip address. If /// no address is provided, ND/ARP is required. pub struct EgressObject { - pub(crate) ifindex: Option, + pub(crate) ifindex: Option, pub(crate) address: Option, pub(crate) ifname: Option, } impl EgressObject { #[must_use] - pub fn new(ifindex: Option, address: Option, ifname: Option) -> Self { + pub fn new( + ifindex: Option, + address: Option, + ifname: Option, + ) -> Self { Self { ifindex, address, @@ -29,7 +33,7 @@ impl EgressObject { } } #[must_use] - pub fn ifindex(&self) -> &Option { + pub fn ifindex(&self) -> &Option { &self.ifindex } #[must_use] @@ -214,7 +218,7 @@ impl FibEntry { pub enum PktInstruction { #[default] Drop, /* drop the packet */ - Local(IfIndex), /* packet is destined to gw */ - Encap(Encapsulation), /* encapsulate the packet */ - Egress(EgressObject), /* send the packet over interface to some ip */ + Local(InterfaceIndex), /* packet is destined to gw */ + Encap(Encapsulation), /* encapsulate the packet */ + Egress(EgressObject), /* send the packet over interface to some ip */ } diff --git a/routing/src/interfaces/iftable.rs b/routing/src/interfaces/iftable.rs index 1132ab260..5d4ca7245 100644 --- a/routing/src/interfaces/iftable.rs +++ b/routing/src/interfaces/iftable.rs @@ -5,17 +5,18 @@ use crate::errors::RouterError; use crate::fib::fibtype::{FibId, FibReader}; -use crate::interfaces::interface::{IfAddress, IfIndex, IfState, Interface, RouterInterfaceConfig}; +use crate::interfaces::interface::{IfAddress, IfState, Interface, RouterInterfaceConfig}; use ahash::RandomState; use std::collections::HashMap; +use net::interface::InterfaceIndex; #[allow(unused)] use tracing::{debug, error}; #[derive(Clone)] /// A table of network interface objects, keyed by some ifindex (u32) pub struct IfTable { - by_index: HashMap, + by_index: HashMap, } #[allow(clippy::new_without_default)] @@ -38,7 +39,7 @@ impl IfTable { self.by_index.is_empty() } #[must_use] - pub fn contains(&self, ifindex: IfIndex) -> bool { + pub fn contains(&self, ifindex: InterfaceIndex) -> bool { self.by_index.contains_key(&ifindex) } pub fn values(&self) -> impl Iterator { @@ -96,7 +97,7 @@ impl IfTable { ////////////////////////////////////////////////////////////////// /// Remove an interface from the table ////////////////////////////////////////////////////////////////// - pub fn del_interface(&mut self, ifindex: IfIndex) { + pub fn del_interface(&mut self, ifindex: InterfaceIndex) { if let Some(iface) = self.by_index.remove(&ifindex) { debug!("Deleted interface '{}'", iface.name); } @@ -106,7 +107,7 @@ impl IfTable { /// Get an immutable reference to an [`Interface`] ////////////////////////////////////////////////////////////////// #[must_use] - pub fn get_interface(&self, ifindex: IfIndex) -> Option<&Interface> { + pub fn get_interface(&self, ifindex: InterfaceIndex) -> Option<&Interface> { self.by_index.get(&ifindex) } @@ -114,7 +115,7 @@ impl IfTable { /// Get a mutable reference to an [`Interface`] ////////////////////////////////////////////////////////////////// #[must_use] - pub fn get_interface_mut(&mut self, ifindex: IfIndex) -> Option<&mut Interface> { + pub fn get_interface_mut(&mut self, ifindex: InterfaceIndex) -> Option<&mut Interface> { self.by_index.get_mut(&ifindex) } @@ -125,7 +126,11 @@ impl IfTable { /// /// Fails if the interface is not found ////////////////////////////////////////////////////////////////// - pub fn add_ifaddr(&mut self, ifindex: IfIndex, ifaddr: &IfAddress) -> Result<(), RouterError> { + pub fn add_ifaddr( + &mut self, + ifindex: InterfaceIndex, + ifaddr: &IfAddress, + ) -> Result<(), RouterError> { if let Some(iface) = self.by_index.get_mut(&ifindex) { iface.add_ifaddr(ifaddr); Ok(()) @@ -137,7 +142,7 @@ impl IfTable { ////////////////////////////////////////////////////////////////// /// Un-assign an Ip address from an interface. ////////////////////////////////////////////////////////////////// - pub fn del_ifaddr(&mut self, ifindex: IfIndex, ifaddr: &IfAddress) { + pub fn del_ifaddr(&mut self, ifindex: InterfaceIndex, ifaddr: &IfAddress) { if let Some(iface) = self.by_index.get_mut(&ifindex) { iface.del_ifaddr(&(ifaddr.0, ifaddr.1)); } @@ -157,7 +162,7 @@ impl IfTable { ////////////////////////////////////////////////////////////////////// /// Attach [`Interface`] to the provided [`FibReader`] ////////////////////////////////////////////////////////////////////// - pub fn attach_interface_to_vrf(&mut self, ifindex: IfIndex, fibr: FibReader) { + pub fn attach_interface_to_vrf(&mut self, ifindex: InterfaceIndex, fibr: FibReader) { if let Some(iface) = self.get_interface_mut(ifindex) { iface.attach_vrf(fibr); } else { @@ -168,7 +173,7 @@ impl IfTable { ////////////////////////////////////////////////////////////////////// /// Detach [`Interface`] from wherever it is attached ////////////////////////////////////////////////////////////////////// - pub fn detach_interface_from_vrf(&mut self, ifindex: IfIndex) { + pub fn detach_interface_from_vrf(&mut self, ifindex: InterfaceIndex) { if let Some(iface) = self.get_interface_mut(ifindex) { iface.detach(); } else { @@ -179,7 +184,7 @@ impl IfTable { ////////////////////////////////////////////////////////////////////// /// Set the operational state of an [`Interface`] ////////////////////////////////////////////////////////////////////// - pub fn set_iface_oper_state(&mut self, ifindex: IfIndex, state: IfState) { + pub fn set_iface_oper_state(&mut self, ifindex: InterfaceIndex, state: IfState) { if let Some(ifr) = self.get_interface_mut(ifindex) { ifr.set_oper_state(state); } @@ -188,7 +193,7 @@ impl IfTable { ////////////////////////////////////////////////////////////////////// /// Set the admin state of an [`Interface`] ////////////////////////////////////////////////////////////////////// - pub fn set_iface_admin_state(&mut self, ifindex: IfIndex, state: IfState) { + pub fn set_iface_admin_state(&mut self, ifindex: InterfaceIndex, state: IfState) { if let Some(ifr) = self.get_interface_mut(ifindex) { ifr.set_admin_state(state); } diff --git a/routing/src/interfaces/iftablerw.rs b/routing/src/interfaces/iftablerw.rs index eded88429..cb22287c0 100644 --- a/routing/src/interfaces/iftablerw.rs +++ b/routing/src/interfaces/iftablerw.rs @@ -6,24 +6,25 @@ use crate::errors::RouterError; use crate::fib::fibtype::FibId; use crate::fib::fibtype::FibReader; +use crate::interfaces::iftable::IfTable; +use crate::interfaces::interface::{IfAddress, IfState, RouterInterfaceConfig}; use crate::rib::vrf::VrfId; use crate::rib::vrftable::VrfTable; -use left_right::{Absorb, ReadGuard, ReadHandle, ReadHandleFactory, WriteHandle}; - -use crate::interfaces::iftable::IfTable; -use crate::interfaces::interface::{IfAddress, IfIndex, IfState, RouterInterfaceConfig}; +use left_right::ReadHandleFactory; +use left_right::{Absorb, ReadGuard, ReadHandle, WriteHandle}; +use net::interface::InterfaceIndex; enum IfTableChange { Add(RouterInterfaceConfig), Mod(RouterInterfaceConfig), - Del(IfIndex), - Attach((IfIndex, FibReader)), - Detach(IfIndex), + Del(InterfaceIndex), + Attach((InterfaceIndex, FibReader)), + Detach(InterfaceIndex), DetachFromVrf(FibId), - AddIpAddress((IfIndex, IfAddress)), - DelIpAddress((IfIndex, IfAddress)), - UpdateOpState((IfIndex, IfState)), - UpdateAdmState((IfIndex, IfState)), + AddIpAddress((InterfaceIndex, IfAddress)), + DelIpAddress((InterfaceIndex, IfAddress)), + UpdateOpState((InterfaceIndex, IfState)), + UpdateAdmState((InterfaceIndex, IfState)), } impl Absorb for IfTable { fn absorb_first(&mut self, change: &mut IfTableChange, _: &Self) { @@ -99,26 +100,26 @@ impl IfTableWriter { self.0.publish(); Ok(()) } - pub fn del_interface(&mut self, ifindex: IfIndex) { + pub fn del_interface(&mut self, ifindex: InterfaceIndex) { self.0.append(IfTableChange::Del(ifindex)); self.0.publish(); } - pub fn add_ip_address(&mut self, ifindex: IfIndex, ifaddr: IfAddress) { + pub fn add_ip_address(&mut self, ifindex: InterfaceIndex, ifaddr: IfAddress) { self.0 .append(IfTableChange::AddIpAddress((ifindex, ifaddr))); self.0.publish(); } - pub fn del_ip_address(&mut self, ifindex: IfIndex, ifaddr: IfAddress) { + pub fn del_ip_address(&mut self, ifindex: InterfaceIndex, ifaddr: IfAddress) { self.0 .append(IfTableChange::DelIpAddress((ifindex, ifaddr))); self.0.publish(); } - pub fn set_iface_oper_state(&mut self, ifindex: IfIndex, state: IfState) { + pub fn set_iface_oper_state(&mut self, ifindex: InterfaceIndex, state: IfState) { self.0 .append(IfTableChange::UpdateOpState((ifindex, state))); self.0.publish(); } - pub fn set_iface_admin_state(&mut self, ifindex: IfIndex, state: IfState) { + pub fn set_iface_admin_state(&mut self, ifindex: InterfaceIndex, state: IfState) { self.0 .append(IfTableChange::UpdateAdmState((ifindex, state))); self.0.publish(); @@ -134,7 +135,7 @@ impl IfTableWriter { fn interface_attach_check( &mut self, - ifindex: IfIndex, + ifindex: InterfaceIndex, vrfid: VrfId, vrftable: &VrfTable, ) -> Result { @@ -154,7 +155,7 @@ impl IfTableWriter { /// Fails if the interface is not found pub fn attach_interface_to_vrf( &mut self, - ifindex: IfIndex, + ifindex: InterfaceIndex, vrfid: VrfId, vrftable: &VrfTable, ) -> Result<(), RouterError> { @@ -163,7 +164,7 @@ impl IfTableWriter { self.0.publish(); Ok(()) } - pub fn detach_interface(&mut self, ifindex: IfIndex) { + pub fn detach_interface(&mut self, ifindex: InterfaceIndex) { self.0.append(IfTableChange::Detach(ifindex)); self.0.publish(); } diff --git a/routing/src/interfaces/interface.rs b/routing/src/interfaces/interface.rs index 71c962489..e1b59cdd7 100644 --- a/routing/src/interfaces/interface.rs +++ b/routing/src/interfaces/interface.rs @@ -8,7 +8,7 @@ use crate::fib::fibtype::{FibId, FibReader}; use crate::rib::vrf::VrfId; use net::eth::mac::Mac; -use net::interface::Mtu; +use net::interface::{InterfaceIndex, Mtu}; use net::vlan::Vid; use std::net::IpAddr; @@ -17,9 +17,6 @@ use std::collections::HashSet; #[allow(unused)] use tracing::{debug, error, info}; -/// A type to uniquely identify a network interface -pub type IfIndex = u32; - /// An Ipv4 or Ipv6 address and mask configured on an interface pub type IfAddress = (IpAddr, u8); @@ -85,16 +82,16 @@ pub enum AttachConfig { /// An object representing the configuration for an [`Interface`] #[derive(Clone, Debug, PartialEq)] pub struct RouterInterfaceConfig { - pub ifindex: IfIndex, /* ifindex of kernel interface (key) */ - pub name: String, /* name of interface */ - pub description: Option, /* description - informational */ - pub iftype: IfType, /* type of interface */ - pub admin_state: IfState, /* admin state */ + pub ifindex: InterfaceIndex, /* ifindex of kernel interface (key) */ + pub name: String, /* name of interface */ + pub description: Option, /* description - informational */ + pub iftype: IfType, /* type of interface */ + pub admin_state: IfState, /* admin state */ pub attach_cfg: Option, /* attach config */ pub mtu: Option, } impl RouterInterfaceConfig { - pub fn new(name: &str, ifindex: IfIndex) -> Self { + pub fn new(name: &str, ifindex: InterfaceIndex) -> Self { Self { ifindex, name: name.to_owned(), @@ -130,7 +127,7 @@ impl RouterInterfaceConfig { pub struct Interface { pub name: String, pub description: Option, - pub ifindex: IfIndex, + pub ifindex: InterfaceIndex, pub iftype: IfType, pub admin_state: IfState, pub mtu: Option, diff --git a/routing/src/interfaces/mod.rs b/routing/src/interfaces/mod.rs index c3a725bbe..70208789d 100644 --- a/routing/src/interfaces/mod.rs +++ b/routing/src/interfaces/mod.rs @@ -18,6 +18,7 @@ pub mod tests { }; use crate::rib::vrf::{RouterVrfConfig, Vrf}; use net::eth::mac::Mac; + use net::interface::InterfaceIndex; use net::vlan::Vid; use std::net::IpAddr; use std::str::FromStr; @@ -27,13 +28,15 @@ pub mod tests { let mut iftable = IfTable::new(); /* create loopback */ - let mut lo = RouterInterfaceConfig::new("Loopback", 1); + let lo_idx = InterfaceIndex::try_new(1).unwrap(); + let mut lo = RouterInterfaceConfig::new("Loopback", lo_idx); lo.set_admin_state(IfState::Up); lo.set_description("Main loopback interface"); lo.set_iftype(IfType::Loopback); /* create Eth0 */ - let mut eth0 = RouterInterfaceConfig::new("eth0", 2); + let eth0_idx = InterfaceIndex::try_new(2).unwrap(); + let mut eth0 = RouterInterfaceConfig::new("eth0", eth0_idx); eth0.set_admin_state(IfState::Up); eth0.set_description("Uplink to the Moon"); eth0.set_iftype(IfType::Ethernet(IfDataEthernet { @@ -41,7 +44,8 @@ pub mod tests { })); /* create Eth1 */ - let mut eth1 = RouterInterfaceConfig::new("eth1", 3); + let eth1_idx = InterfaceIndex::try_new(3).unwrap(); + let mut eth1 = RouterInterfaceConfig::new("eth1", eth1_idx); eth1.set_admin_state(IfState::Up); eth1.set_description("Downlink from Mars"); eth1.set_iftype(IfType::Ethernet(IfDataEthernet { @@ -49,7 +53,8 @@ pub mod tests { })); /* create Eth2 */ - let mut eth2 = RouterInterfaceConfig::new("eth2", 4); + let eth2_idx = InterfaceIndex::try_new(4).unwrap(); + let mut eth2 = RouterInterfaceConfig::new("eth2", eth2_idx); eth2.set_admin_state(IfState::Up); eth2.set_description("Downlink from Sun"); eth2.set_iftype(IfType::Ethernet(IfDataEthernet { @@ -57,7 +62,8 @@ pub mod tests { })); /* create vlan.100 */ - let mut vlan100 = RouterInterfaceConfig::new("eth1.100", 5); + let vlan100_idx = InterfaceIndex::try_new(5).unwrap(); + let mut vlan100 = RouterInterfaceConfig::new("eth1.100", vlan100_idx); vlan100.set_admin_state(IfState::Up); vlan100.set_description("External customer 1"); vlan100.set_iftype(IfType::Dot1q(IfDataDot1q { @@ -66,7 +72,8 @@ pub mod tests { })); /* create vlan.200 */ - let mut vlan200 = RouterInterfaceConfig::new("eth1.200", 6); + let vlan200_idx = InterfaceIndex::try_new(6).unwrap(); + let mut vlan200 = RouterInterfaceConfig::new("eth1.200", vlan200_idx); vlan200.set_admin_state(IfState::Up); vlan200.set_description("External customer 2"); vlan200.set_iftype(IfType::Dot1q(IfDataDot1q { @@ -114,15 +121,17 @@ pub mod tests { vrf.set_fibw(fibw); /* lookup interface with non-existent index */ - let iface = iftable.get_interface(100); + let idx100 = InterfaceIndex::try_new(100).unwrap(); + let iface = iftable.get_interface(idx100); assert!(iface.is_none()); /* Lookup interface by ifindex 2 */ - let iface = iftable.get_interface_mut(2); + let idx2 = InterfaceIndex::try_new(2).unwrap(); + let iface = iftable.get_interface_mut(idx2); assert!(iface.is_some()); let eth0 = iface.unwrap(); assert_eq!(eth0.name, "eth0", "We should get eth0"); - assert_eq!(eth0.ifindex, 2, "eth0 has ifindex 2"); + assert_eq!(eth0.ifindex, idx2, "eth0 has ifindex 2"); /* Add an ip address (the interface is in the iftable) */ let address = IpAddr::from_str("10.0.0.1").expect("Bad address"); @@ -136,7 +145,8 @@ pub mod tests { let mut iftable = IfTable::new(); /* create Eth0 */ - let mut eth0 = RouterInterfaceConfig::new("eth0", 2); + let eth0_idx = InterfaceIndex::try_new(2).unwrap(); + let mut eth0 = RouterInterfaceConfig::new("eth0", eth0_idx); eth0.set_iftype(IfType::Ethernet(IfDataEthernet { mac: Mac::from([0x0, 0xaa, 0x0, 0x0, 0x0, 0x1]), })); @@ -146,14 +156,14 @@ pub mod tests { assert_eq!(iftable.len(), 1, "Eth0 should be there"); /* test get_mac */ - let iface = iftable.get_interface(2).expect("Should be there"); + let iface = iftable.get_interface(eth0_idx).expect("Should be there"); assert_eq!( Mac::from([0x0, 0xaa, 0x0, 0x0, 0x0, 0x1]), iface.get_mac().unwrap() ); /* Add interface again -- idempotence */ - let mut eth0 = RouterInterfaceConfig::new("eth0", 2); + let mut eth0 = RouterInterfaceConfig::new("eth0", eth0_idx); eth0.set_iftype(IfType::Ethernet(IfDataEthernet { mac: Mac::from([0x0, 0xaa, 0x0, 0x0, 0x0, 0x1]), })); @@ -162,7 +172,7 @@ pub mod tests { assert_eq!(iftable.len(), 1, "Only eth0 should be there"); /* Delete eth0 by index */ - iftable.del_interface(2); + iftable.del_interface(eth0_idx); assert_eq!(iftable.len(), 0, "No interface should be there"); } } diff --git a/routing/src/rib/nexthop.rs b/routing/src/rib/nexthop.rs index 03bbb5779..9678c538d 100644 --- a/routing/src/rib/nexthop.rs +++ b/routing/src/rib/nexthop.rs @@ -18,6 +18,7 @@ use std::hash::Hash; use std::net::IpAddr; use std::option::Option; +use net::interface::InterfaceIndex; use std::cell::RefCell; use std::rc::Rc; #[cfg(test)] @@ -56,7 +57,7 @@ pub enum FwAction { pub struct NhopKey { pub origin: RouteOrigin, pub address: Option, - pub ifindex: Option, + pub ifindex: Option, pub encap: Option, pub fwaction: FwAction, pub ifname: Option, @@ -70,7 +71,7 @@ impl NhopKey { pub fn new( origin: RouteOrigin, address: Option, - ifindex: Option, + ifindex: Option, encap: Option, fwaction: FwAction, ifname: Option, @@ -104,7 +105,7 @@ impl NhopKey { } #[cfg(test)] #[must_use] - pub fn with_addr_ifindex(address: &IpAddr, ifindex: u32) -> Self { + pub fn with_addr_ifindex(address: &IpAddr, ifindex: InterfaceIndex) -> Self { Self { address: Some(*address), ifindex: Some(ifindex), @@ -121,7 +122,7 @@ impl NhopKey { } #[cfg(test)] #[must_use] - pub fn with_ifindex(ifindex: u32) -> Self { + pub fn with_ifindex(ifindex: InterfaceIndex) -> Self { Self { ifindex: Some(ifindex), ..Default::default() @@ -486,9 +487,9 @@ mod tests { let n2_k = NhopKey::expect_from("10.0.2.1"); let n3_k = NhopKey::expect_from("10.0.3.1"); - let i1_k = NhopKey::with_ifindex(1); - let i2_k = NhopKey::with_ifindex(2); - let i3_k = NhopKey::with_ifindex(3); + let i1_k = NhopKey::with_ifindex(InterfaceIndex::try_new(1).unwrap()); + let i2_k = NhopKey::with_ifindex(InterfaceIndex::try_new(2).unwrap()); + let i3_k = NhopKey::with_ifindex(InterfaceIndex::try_new(3).unwrap()); /* Add some next-hops and references */ { @@ -531,7 +532,7 @@ mod tests { fn test_nhop_store_shared_resolvers() { let mut store = NhopStore::new(); - let i1_k = NhopKey::with_ifindex(1); + let i1_k = NhopKey::with_ifindex(InterfaceIndex::try_new(1).unwrap()); let n1_k = NhopKey::expect_from("11.0.0.1"); let n2_k = NhopKey::expect_from("11.0.0.2"); @@ -574,7 +575,7 @@ mod tests { fn test_nhop_store_flush_resolvers() { let mut store = NhopStore::new(); - let i1_k = NhopKey::with_ifindex(1); + let i1_k = NhopKey::with_ifindex(InterfaceIndex::try_new(1).unwrap()); let n1_k = NhopKey::expect_from("11.0.0.1"); let n2_k = NhopKey::expect_from("11.0.0.2"); @@ -601,9 +602,9 @@ mod tests { let mut store = NhopStore::new(); /* add "interface" next-hops */ - let i1 = store.add_nhop(&NhopKey::with_ifindex(1)); - let i2 = store.add_nhop(&NhopKey::with_ifindex(2)); - let i3 = store.add_nhop(&NhopKey::with_ifindex(3)); + let i1 = store.add_nhop(&NhopKey::with_ifindex(InterfaceIndex::try_new(1).unwrap())); + let i2 = store.add_nhop(&NhopKey::with_ifindex(InterfaceIndex::try_new(2).unwrap())); + let i3 = store.add_nhop(&NhopKey::with_ifindex(InterfaceIndex::try_new(3).unwrap())); /* add "adjacent" nexthops */ let a1 = store.add_nhop(&NhopKey::expect_from("10.0.0.1")); @@ -642,15 +643,15 @@ mod tests { /* add "adjacent" nexthops with interface resolved */ let a1 = store.add_nhop(&NhopKey::with_addr_ifindex( &("10.0.0.1".parse().unwrap()), - 1, + InterfaceIndex::try_new(1).unwrap(), )); let a2 = store.add_nhop(&NhopKey::with_addr_ifindex( &("10.0.0.5".parse().unwrap()), - 2, + InterfaceIndex::try_new(2).unwrap(), )); let a3 = store.add_nhop(&NhopKey::with_addr_ifindex( &("10.0.0.9".parse().unwrap()), - 3, + InterfaceIndex::try_new(3).unwrap(), )); // add "non-adjacent" nexthops diff --git a/routing/src/rib/rib2fib.rs b/routing/src/rib/rib2fib.rs index 47e4d6795..e04982932 100644 --- a/routing/src/rib/rib2fib.rs +++ b/routing/src/rib/rib2fib.rs @@ -23,7 +23,13 @@ impl Nhop { fn build_pkt_instructions(&self, rstore: &RmacStore) -> Vec { let mut instructions = Vec::with_capacity(2); if self.key.origin == RouteOrigin::Local { - instructions.push(PktInstruction::Local(self.key.ifindex.unwrap_or(0))); + match self.key.ifindex { + Some(if_index) => instructions.push(PktInstruction::Local(if_index)), + None => { + warn!("packet is locally destined but has no target interface index: dropping"); + instructions.push(PktInstruction::Drop); + } + }; return instructions; } if self.key.fwaction == FwAction::Drop { diff --git a/routing/src/rib/vrf.rs b/routing/src/rib/vrf.rs index 28cc79b36..da6905f17 100644 --- a/routing/src/rib/vrf.rs +++ b/routing/src/rib/vrf.rs @@ -724,9 +724,10 @@ impl Vrf { #[cfg(test)] #[rustfmt::skip] pub mod tests { + use net::interface::InterfaceIndex; + use super::*; use std::str::FromStr; - use crate::interfaces::interface::IfIndex; use crate::rib::vrf::VrfId; use crate::rib::nexthop::{FwAction, NhopKey}; use crate::rib::encapsulation::{Encapsulation, VxlanEncapsulation}; @@ -794,14 +795,14 @@ pub mod tests { pub fn build_test_nhop( address: Option<&str>, - ifindex: Option, + ifindex: Option, vrfid: VrfId, encap: Option, ) -> RouteNhop { let key = NhopKey::new( RouteOrigin::default(), address.map(mk_addr), - ifindex, encap,FwAction::Forward, None); + ifindex.map(|i| InterfaceIndex::try_new(i).unwrap()), encap,FwAction::Forward, None); RouteNhop { vrfid, @@ -922,8 +923,8 @@ pub mod tests { assert_eq!(best.metric, route.metric); assert_eq!(best.origin, route.origin); assert_eq!(best.s_nhops.len(), 2); - assert!(best.s_nhops.iter().any(|s| s.rc.key.address == Some(mk_addr("10.0.0.1")) && s.rc.key.ifindex == Some(1))); - assert!(best.s_nhops.iter().any(|s| s.rc.key.address == Some(mk_addr("10.0.0.2")) && s.rc.key.ifindex == Some(2))); + assert!(best.s_nhops.iter().any(|s| s.rc.key.address == Some(mk_addr("10.0.0.1")) && s.rc.key.ifindex == Some(InterfaceIndex::try_new(1).unwrap()))); + assert!(best.s_nhops.iter().any(|s| s.rc.key.address == Some(mk_addr("10.0.0.2")) && s.rc.key.ifindex == Some(InterfaceIndex::try_new(2).unwrap()))); } assert_eq!(vrf.len_v4(), (1 + num_routes) as usize, "There must be default + the ones added"); assert_eq!(vrf.nhstore.len(), 3usize,"There is drop + 2 nexthops shared by all routes"); diff --git a/routing/src/rib/vrftable.rs b/routing/src/rib/vrftable.rs index d655ad0ed..c992e8106 100644 --- a/routing/src/rib/vrftable.rs +++ b/routing/src/rib/vrftable.rs @@ -18,7 +18,6 @@ use ahash::RandomState; use net::vxlan::Vni; use std::collections::HashMap; -#[allow(unused)] use tracing::{debug, error}; pub struct VrfTable { @@ -425,13 +424,14 @@ mod tests { use crate::interfaces::tests::build_test_iftable_left_right; use crate::pretty_utils::Frame; use crate::rib::encapsulation::Encapsulation; - use crate::rib::vrf::tests::build_test_vrf_nhops_partially_resolved; + use crate::rib::vrf::tests::{build_test_vrf, mk_addr}; use crate::rib::vrf::tests::{ - build_test_vrf, init_test_vrf, mk_addr, mod_test_vrf_1, mod_test_vrf_2, + build_test_vrf_nhops_partially_resolved, init_test_vrf, mod_test_vrf_1, mod_test_vrf_2, }; use crate::{ evpn::rmac::tests::build_sample_rmac_store, rib::encapsulation::VxlanEncapsulation, }; + use net::interface::InterfaceIndex; use tracing_test::traced_test; fn mk_vni(vni: u32) -> Vni { @@ -516,44 +516,48 @@ mod tests { /* Attach eth0 */ let vrfid = 2; + let idx2 = InterfaceIndex::try_new(2).unwrap(); debug!("━━━━━━━━ Test: Attach eth0 to vrf {vrfid}"); - iftw.attach_interface_to_vrf(2, vrfid, &vrftable) + iftw.attach_interface_to_vrf(idx2, vrfid, &vrftable) .expect("Should succeed"); let ift = iftr.enter().unwrap(); - let eth0 = ift.get_interface(2).expect("Should find interface"); + let eth0 = ift.get_interface(idx2).expect("Should find interface"); assert!(eth0.is_attached_to_fib(FibId::Id(vrfid))); println!("{}", *ift); drop(ift); /* Attach eth1 */ let vrfid = 2; + let idx3 = InterfaceIndex::try_new(3).unwrap(); debug!("━━━━━━━━ Test: Attach eth1 to vrf {vrfid}"); - iftw.attach_interface_to_vrf(3, vrfid, &vrftable) + iftw.attach_interface_to_vrf(idx3, vrfid, &vrftable) .expect("Should succeed"); let ift = iftr.enter().unwrap(); - let eth1 = ift.get_interface(3).expect("Should find interface"); + let eth1 = ift.get_interface(idx3).expect("Should find interface"); assert!(eth1.is_attached_to_fib(FibId::Id(vrfid))); println!("{}", *ift); drop(ift); /* Attach vlan100 */ let vrfid = 1; + let idx4 = InterfaceIndex::try_new(4).unwrap(); debug!("━━━━━━━━ Test: Attach eth2 to vrf {vrfid}"); - iftw.attach_interface_to_vrf(4, vrfid, &vrftable) + iftw.attach_interface_to_vrf(idx4, vrfid, &vrftable) .expect("Should succeed"); let ift = iftr.enter().unwrap(); - let eth2 = ift.get_interface(4).expect("Should find interface"); + let eth2 = ift.get_interface(idx4).expect("Should find interface"); assert!(eth2.is_attached_to_fib(FibId::Id(vrfid))); println!("{}", *ift); drop(ift); /* Attach vlan200 */ let vrfid = 1; + let idx5 = InterfaceIndex::try_new(5).unwrap(); debug!("━━━━━━━━ Test: Attach eth1.100 to vrf {vrfid}"); - iftw.attach_interface_to_vrf(5, vrfid, &vrftable) + iftw.attach_interface_to_vrf(idx5, vrfid, &vrftable) .expect("Should succeed"); let ift = iftr.enter().unwrap(); - let iface = ift.get_interface(5).expect("Should find interface"); + let iface = ift.get_interface(idx5).expect("Should find interface"); assert!(iface.is_attached_to_fib(FibId::Id(vrfid))); println!("{}", *ift); drop(ift); @@ -571,10 +575,10 @@ mod tests { ); println!("{vrftable}"); let ift = iftr.enter().unwrap(); - let iface = ift.get_interface(4).expect("Should be there"); + let iface = ift.get_interface(idx4).expect("Should be there"); assert!(!iface.is_attached_to_fib(FibId::Id(vrfid))); assert!(iface.attachment.is_none()); - let iface = ift.get_interface(5).expect("Should be there"); + let iface = ift.get_interface(idx5).expect("Should be there"); assert!(!iface.is_attached_to_fib(FibId::Id(vrfid))); assert!(iface.attachment.is_none()); println!("{}", *ift); @@ -603,10 +607,10 @@ mod tests { .is_err_and(|e| e == RouterError::NoSuchVrf) ); let ift = iftr.enter().unwrap(); - let eth0 = ift.get_interface(2).expect("Should be there"); + let eth0 = ift.get_interface(idx2).expect("Should be there"); assert!(!eth0.is_attached_to_fib(FibId::Id(vrfid))); assert!(eth0.attachment.is_none()); - let eth1 = ift.get_interface(3).expect("Should be there"); + let eth1 = ift.get_interface(idx3).expect("Should be there"); assert!(!eth1.is_attached_to_fib(FibId::Id(vrfid))); assert!(eth1.attachment.is_none()); println!("{}", *ift); @@ -709,17 +713,18 @@ mod tests { assert_eq!(vrftable.len(), 2); // default is always there debug!("━━━━Test: Get interface from iftable"); + let idx = InterfaceIndex::try_new(2).unwrap(); if let Some(iftable) = iftr.enter() { - let iface = iftable.get_interface(2).expect("Should be there"); + let iface = iftable.get_interface(idx).expect("Should be there"); assert_eq!(iface.name, "eth0"); debug!("\n{}", *iftable); } debug!("━━━━Test: Attach interface to vrf"); - iftw.attach_interface_to_vrf(2, vrfid, &vrftable) + iftw.attach_interface_to_vrf(idx, vrfid, &vrftable) .expect("Should succeed"); if let Some(iftable) = iftr.enter() { - let iface = iftable.get_interface(2).expect("Should be there"); + let iface = iftable.get_interface(idx).expect("Should be there"); assert!(iface.attachment.is_some()); debug!("\n{}", *iftable); } @@ -742,7 +747,7 @@ mod tests { assert_eq!(fibtable.len(), 1); } if let Some(iftable) = iftr.enter() { - let iface = iftable.get_interface(2).expect("Should be there"); + let iface = iftable.get_interface(idx).expect("Should be there"); assert!(iface.attachment.is_none(), "Should have been detached"); } @@ -789,10 +794,10 @@ mod tests { assert_eq!(entry.instructions[0], PktInstruction::Encap(Encapsulation::Vxlan(vxlan))); assert_eq!(entry.instructions[1], PktInstruction::Encap(Encapsulation::Mpls(7000))); match num { - 0 => assert_eq!(entry.instructions[3], PktInstruction::Egress(EgressObject::new(Some(1), Some(mk_addr("10.0.0.1")), None))), - 1 => assert_eq!(entry.instructions[3], PktInstruction::Egress(EgressObject::new(Some(2), Some(mk_addr("10.0.0.5")), None))), - 2 => assert_eq!(entry.instructions[3], PktInstruction::Egress(EgressObject::new(Some(2), Some(mk_addr("10.0.0.5")), None))), - 3 => assert_eq!(entry.instructions[3], PktInstruction::Egress(EgressObject::new(Some(3), Some(mk_addr("10.0.0.9")), None))), + 0 => assert_eq!(entry.instructions[3], PktInstruction::Egress(EgressObject::new(InterfaceIndex::try_new(1).ok(), Some(mk_addr("10.0.0.1")), None))), + 1 => assert_eq!(entry.instructions[3], PktInstruction::Egress(EgressObject::new(InterfaceIndex::try_new(2).ok(), Some(mk_addr("10.0.0.5")), None))), + 2 => assert_eq!(entry.instructions[3], PktInstruction::Egress(EgressObject::new(InterfaceIndex::try_new(2).ok(), Some(mk_addr("10.0.0.5")), None))), + 3 => assert_eq!(entry.instructions[3], PktInstruction::Egress(EgressObject::new(InterfaceIndex::try_new(3).ok(), Some(mk_addr("10.0.0.9")), None))), _ => unreachable!(), } } @@ -816,8 +821,8 @@ mod tests { assert_eq!(entry.instructions[0], PktInstruction::Encap(Encapsulation::Vxlan(vxlan))); assert_eq!(entry.instructions[1], PktInstruction::Encap(Encapsulation::Mpls(7000))); match num { - 0 => assert_eq!(entry.instructions[3], PktInstruction::Egress(EgressObject::new(Some(1), Some(mk_addr("10.0.0.1")), None))), - 1 => assert_eq!(entry.instructions[3], PktInstruction::Egress(EgressObject::new(Some(3), Some(mk_addr("10.0.0.9")), None))), + 0 => assert_eq!(entry.instructions[3], PktInstruction::Egress(EgressObject::new(InterfaceIndex::try_new(1).ok(), Some(mk_addr("10.0.0.1")), None))), + 1 => assert_eq!(entry.instructions[3], PktInstruction::Egress(EgressObject::new(InterfaceIndex::try_new(3).ok(), Some(mk_addr("10.0.0.9")), None))), _ => unreachable!(), } } @@ -840,7 +845,7 @@ mod tests { assert_eq!(entry.instructions[0], PktInstruction::Encap(Encapsulation::Vxlan(vxlan))); assert_eq!(entry.instructions[1], PktInstruction::Encap(Encapsulation::Mpls(7000))); match num { - 0 => assert_eq!(entry.instructions[3], PktInstruction::Egress(EgressObject::new(Some(2), Some(mk_addr("10.0.0.5")), None))), + 0 => assert_eq!(entry.instructions[3], PktInstruction::Egress(EgressObject::new(InterfaceIndex::try_new(2).ok(), Some(mk_addr("10.0.0.5")), None))), _ => unreachable!(), } } @@ -860,10 +865,10 @@ mod tests { assert_eq!(entry.instructions[0], PktInstruction::Encap(Encapsulation::Vxlan(vxlan))); assert_eq!(entry.instructions[1], PktInstruction::Encap(Encapsulation::Mpls(7000))); match num { - 0 => assert_eq!(entry.instructions[3], PktInstruction::Egress(EgressObject::new(Some(1), Some(mk_addr("10.0.0.1")), None))), - 1 => assert_eq!(entry.instructions[3], PktInstruction::Egress(EgressObject::new(Some(2), Some(mk_addr("10.0.0.5")), None))), - 2 => assert_eq!(entry.instructions[3], PktInstruction::Egress(EgressObject::new(Some(2), Some(mk_addr("10.0.0.5")), None))), - 3 => assert_eq!(entry.instructions[3], PktInstruction::Egress(EgressObject::new(Some(3), Some(mk_addr("10.0.0.9")), None))), + 0 => assert_eq!(entry.instructions[3], PktInstruction::Egress(EgressObject::new(InterfaceIndex::try_new(1).ok(), Some(mk_addr("10.0.0.1")), None))), + 1 => assert_eq!(entry.instructions[3], PktInstruction::Egress(EgressObject::new(InterfaceIndex::try_new(2).ok(), Some(mk_addr("10.0.0.5")), None))), + 2 => assert_eq!(entry.instructions[3], PktInstruction::Egress(EgressObject::new(InterfaceIndex::try_new(2).ok(), Some(mk_addr("10.0.0.5")), None))), + 3 => assert_eq!(entry.instructions[3], PktInstruction::Egress(EgressObject::new(InterfaceIndex::try_new(3).ok(), Some(mk_addr("10.0.0.9")), None))), _ => unreachable!(), } } diff --git a/routing/src/rpc_adapt.rs b/routing/src/rpc_adapt.rs index 6007f4d26..6b6525e44 100644 --- a/routing/src/rpc_adapt.rs +++ b/routing/src/rpc_adapt.rs @@ -21,6 +21,7 @@ use dplane_rpc::msg::{ }; use lpm::prefix::Prefix; use net::eth::mac::Mac; +use net::interface::InterfaceIndex; use net::vxlan::Vni; use std::net::{IpAddr, Ipv4Addr}; use tracing::{error, warn}; @@ -96,12 +97,22 @@ impl TryFrom<&Rmac> for RmacEntry { } impl RouteNhop { + #[tracing::instrument(level = "debug")] fn from_rpc_nhop( nh: &NextHop, origin: RouteOrigin, iftabler: &IfTableReader, ) -> Result { - let mut ifindex = nh.ifindex; + let mut ifindex = nh + .ifindex + .map(|i| match InterfaceIndex::try_new(i) { + Ok(idx) => Ok(idx), + Err(e) => { + error!("unable to build route next hop: {e}"); + return Err(RouterError::Internal("0 is not a valid interface index")); + } + }) + .transpose()?; let encap = match &nh.encap { Some(e) => { let mut enc = Encapsulation::try_from(e)?; @@ -119,7 +130,6 @@ impl RouteNhop { // lookup interface name let ifname = match ifindex { None => None, - Some(0) => None, Some(k) => iftabler .enter() .map(|iftable| iftable.get_interface(k).map(|iface| iface.name.to_owned()))