diff --git a/src/link/link_info/bond.rs b/src/link/link_info/bond.rs index dd9e1d5e..e4ff2384 100644 --- a/src/link/link_info/bond.rs +++ b/src/link/link_info/bond.rs @@ -60,6 +60,18 @@ const BOND_MODE_8023AD: u8 = 4; const BOND_MODE_TLB: u8 = 5; const BOND_MODE_ALB: u8 = 6; +const BOND_STATE_ACTIVE: u8 = 0; +const BOND_STATE_BACKUP: u8 = 1; + +const BOND_ARP_VALIDATE_NONE: u32 = 0; +const BOND_ARP_VALIDATE_ACTIVE: u32 = 1 << BOND_STATE_ACTIVE as u32; +const BOND_ARP_VALIDATE_BACKUP: u32 = 1 << BOND_STATE_BACKUP as u32; +const BOND_ARP_VALIDATE_ALL: u32 = + BOND_ARP_VALIDATE_ACTIVE | BOND_ARP_VALIDATE_BACKUP; +const BOND_ARP_FILTER: u32 = BOND_ARP_VALIDATE_ALL + 1; +const BOND_ARP_FILTER_ACTIVE: u32 = BOND_ARP_FILTER | BOND_ARP_VALIDATE_ACTIVE; +const BOND_ARP_FILTER_BACKUP: u32 = BOND_ARP_FILTER | BOND_ARP_VALIDATE_BACKUP; + #[derive(Debug, Clone, Eq, PartialEq)] #[non_exhaustive] pub enum BondAdInfo { @@ -199,6 +211,65 @@ impl std::fmt::Display for BondMode { } } +#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] +pub enum ArpValidate { + #[default] + None, + Active, + Backup, + All, + Filter, + FilterActive, + FilterBackup, + Other(u32), +} + +impl From for u32 { + fn from(value: ArpValidate) -> Self { + match value { + ArpValidate::None => BOND_ARP_VALIDATE_NONE, + ArpValidate::Active => BOND_ARP_VALIDATE_ACTIVE, + ArpValidate::Backup => BOND_ARP_VALIDATE_BACKUP, + ArpValidate::All => BOND_ARP_VALIDATE_ALL, + ArpValidate::Filter => BOND_ARP_FILTER, + ArpValidate::FilterActive => BOND_ARP_FILTER_ACTIVE, + ArpValidate::FilterBackup => BOND_ARP_FILTER_BACKUP, + ArpValidate::Other(d) => d, + } + } +} + +impl From for ArpValidate { + fn from(value: u32) -> Self { + match value { + BOND_ARP_VALIDATE_NONE => ArpValidate::None, + BOND_ARP_VALIDATE_ACTIVE => ArpValidate::Active, + BOND_ARP_VALIDATE_BACKUP => ArpValidate::Backup, + BOND_ARP_VALIDATE_ALL => ArpValidate::All, + BOND_ARP_FILTER => ArpValidate::Filter, + BOND_ARP_FILTER_ACTIVE => ArpValidate::FilterActive, + BOND_ARP_FILTER_BACKUP => ArpValidate::FilterBackup, + d => ArpValidate::Other(d), + } + } +} + +impl std::fmt::Display for ArpValidate { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let kernel_name = match self { + ArpValidate::None => "none", + ArpValidate::Active => "active", + ArpValidate::Backup => "backup", + ArpValidate::All => "all", + ArpValidate::Filter => "filter", + ArpValidate::FilterActive => "filter_active", + ArpValidate::FilterBackup => "filter_backup", + ArpValidate::Other(d) => return write!(f, "unknown-variant ({d})"), + }; + f.write_str(kernel_name) + } +} + // Some attributes (ARP_IP_TARGET, NS_IP6_TARGET) contain a nested // list of IP addresses, where each element uses the index as NLA kind // and the address as value. InfoBond exposes vectors of IP addresses, @@ -276,7 +347,7 @@ pub enum InfoBond { UseCarrier(u8), ArpInterval(u32), ArpIpTarget(Vec), - ArpValidate(u32), + ArpValidate(ArpValidate), ArpAllTargets(u32), Primary(u32), PrimaryReselect(u8), @@ -360,12 +431,14 @@ impl Nla for InfoBond { Self::AdActorSysPrio(value) | Self::AdUserPortKey(value) => { NativeEndian::write_u16(buffer, *value) } + Self::ArpValidate(value) => { + NativeEndian::write_u32(buffer, (*value).into()) + } Self::ActivePort(value) | Self::MiiMon(value) | Self::UpDelay(value) | Self::DownDelay(value) | Self::ArpInterval(value) - | Self::ArpValidate(value) | Self::ArpAllTargets(value) | Self::Primary(value) | Self::ResendIgmp(value) @@ -470,7 +543,8 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoBond { } IFLA_BOND_ARP_VALIDATE => Self::ArpValidate( parse_u32(payload) - .context("invalid IFLA_BOND_ARP_VALIDATE value")?, + .context("invalid IFLA_BOND_ARP_VALIDATE value")? + .into(), ), IFLA_BOND_ARP_ALL_TARGETS => Self::ArpAllTargets( parse_u32(payload) diff --git a/src/link/link_info/mod.rs b/src/link/link_info/mod.rs index b3145218..32c8246d 100644 --- a/src/link/link_info/mod.rs +++ b/src/link/link_info/mod.rs @@ -27,7 +27,7 @@ mod vxlan; mod xfrm; mod xstats; -pub use self::bond::{BondAdInfo, BondMode, InfoBond}; +pub use self::bond::{ArpValidate, BondAdInfo, BondMode, InfoBond}; pub use self::bond_port::{BondPortState, InfoBondPort, MiiStatus}; pub use self::bridge::{ BridgeId, BridgeIdBuffer, BridgeQuerierState, InfoBridge, diff --git a/src/link/mod.rs b/src/link/mod.rs index ae69f161..f12daeb2 100644 --- a/src/link/mod.rs +++ b/src/link/mod.rs @@ -38,7 +38,7 @@ pub use self::ext_mask::LinkExtentMask; pub use self::header::{LinkHeader, LinkMessageBuffer}; pub use self::link_flag::LinkFlags; pub use self::link_info::{ - BondAdInfo, BondMode, BondPortState, BridgeId, BridgeIdBuffer, + ArpValidate, BondAdInfo, BondMode, BondPortState, BridgeId, BridgeIdBuffer, BridgePortMulticastRouter, BridgePortState, BridgeQuerierState, HsrProtocol, InfoBond, InfoBondPort, InfoBridge, InfoBridgePort, InfoData, InfoGreTap, InfoGreTap6, InfoGreTun, InfoGreTun6, InfoGtp, InfoHsr, diff --git a/src/link/tests/bond.rs b/src/link/tests/bond.rs index 264c3aca..479bce49 100644 --- a/src/link/tests/bond.rs +++ b/src/link/tests/bond.rs @@ -1,14 +1,16 @@ // SPDX-License-Identifier: MIT +use netlink_packet_core::{NetlinkHeader, NetlinkMessage, NetlinkPayload}; use netlink_packet_utils::{Emitable, Parseable}; use crate::link::link_flag::LinkFlags; use crate::link::{ - BondMode, BondPortState, InfoBond, InfoBondPort, InfoData, InfoKind, - InfoPortData, InfoPortKind, LinkAttribute, LinkHeader, LinkInfo, - LinkLayerType, LinkMessage, LinkMessageBuffer, MiiStatus, + ArpValidate, BondMode, BondPortState, InfoBond, InfoBondPort, InfoData, + InfoKind, InfoPortData, InfoPortKind, LinkAttribute, LinkHeader, LinkInfo, + LinkLayerType, LinkMessage, LinkMessageBuffer, LinkXdp, Map, MiiStatus, + State, Stats, Stats64, }; -use crate::AddressFamily; +use crate::{AddressFamily, RouteNetlinkMessage}; #[test] fn test_bond_link_info() { @@ -56,14 +58,14 @@ fn test_bond_link_info() { attributes: vec![LinkAttribute::LinkInfo(vec![ LinkInfo::Kind(InfoKind::Bond), LinkInfo::Data(InfoData::Bond(vec![ - InfoBond::Mode(BondMode::BalanceRr), + InfoBond::Mode(BondMode::default()), InfoBond::MiiMon(0), InfoBond::UpDelay(0), InfoBond::DownDelay(0), InfoBond::PeerNotifDelay(0), InfoBond::UseCarrier(1), InfoBond::ArpInterval(0), - InfoBond::ArpValidate(0), + InfoBond::ArpValidate(ArpValidate::default()), InfoBond::ArpAllTargets(0), InfoBond::PrimaryReselect(0), InfoBond::FailOverMac(0), @@ -149,3 +151,83 @@ fn test_bond_port_link_info() { assert_eq!(buf, raw); } + +#[test] +fn test_bond_arp_validate() { + let raw: Vec = vec![ + 0xfc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xa5, 0xb9, 0x52, 0x66, + 0x37, 0xa7, 0x3d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x02, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x03, 0x00, + 0x62, 0x6f, 0x6e, 0x64, 0x30, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0d, 0x00, + 0xe8, 0x03, 0x00, 0x00, 0x05, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0xdc, 0x05, 0x00, 0x00, 0x08, 0x00, 0x32, 0x00, 0x44, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x33, 0x00, 0xff, 0xff, 0x00, 0x00, 0x08, 0x00, 0x1b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x1f, 0x00, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x28, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x08, 0x00, 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x08, 0x00, 0x20, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x21, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x06, 0x00, 0x6e, 0x6f, 0x6f, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x23, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x30, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x01, 0x00, 0x92, 0xb3, 0xba, 0x20, 0xd7, 0xa0, 0x00, 0x00, + 0x0a, 0x00, 0x02, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + ]; + let expected = LinkMessage { + header: LinkHeader { + interface_family: AddressFamily::Unspec, + index: 5, + link_layer_type: LinkLayerType::Ether, + flags: LinkFlags::Broadcast + | LinkFlags::Controller + | LinkFlags::Multicast, + change_mask: LinkFlags::empty(), + }, + attributes: vec![ + LinkAttribute::IfName("bond0".into()), + LinkAttribute::TxQueueLen(1000), + LinkAttribute::OperState(State::Down), + LinkAttribute::Mode(0), + LinkAttribute::Mtu(1500), + LinkAttribute::MinMtu(68), + LinkAttribute::MaxMtu(65535), + LinkAttribute::Group(0), + LinkAttribute::Promiscuity(0), + LinkAttribute::NumTxQueues(16), + LinkAttribute::GsoMaxSegs(65535), + LinkAttribute::GsoMaxSize(65536), + LinkAttribute::NumRxQueues(16), + LinkAttribute::Carrier(0), + LinkAttribute::Qdisc("noop".into()), + LinkAttribute::CarrierChanges(1), + LinkAttribute::CarrierUpCount(0), + LinkAttribute::CarrierDownCount(1), + LinkAttribute::ProtoDown(0), + LinkAttribute::Map(Map { + memory_start: 0, + memory_end: 0, + base_address: 0, + irq: 0, + dma: 0, + port: 0, + }), + LinkAttribute::Address(vec![0x92, 0xb3, 0xba, 0x20, 0xd7, 0xa0]), + LinkAttribute::Broadcast(vec![0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), + ], + }; + let mut packet = NetlinkMessage::new( + NetlinkHeader::default(), + NetlinkPayload::from(RouteNetlinkMessage::NewLink(expected)), + ); + packet.header.sequence_number = 1716697509; + packet.header.port_number = 4040503; + packet.finalize(); + let mut buf = vec![0u8; packet.buffer_len()]; + packet.serialize(&mut buf); + + assert_eq!(raw, buf); +}