diff --git a/src/link/link_info/bond.rs b/src/link/link_info/bond.rs index dd9e1d5e..e6c225b6 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,67 @@ impl std::fmt::Display for BondMode { } } +#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] +pub enum BondArpValidate { + #[default] + None, + Active, + Backup, + All, + Filter, + FilterActive, + FilterBackup, + Other(u32), +} + +impl From for u32 { + fn from(value: BondArpValidate) -> Self { + match value { + BondArpValidate::None => BOND_ARP_VALIDATE_NONE, + BondArpValidate::Active => BOND_ARP_VALIDATE_ACTIVE, + BondArpValidate::Backup => BOND_ARP_VALIDATE_BACKUP, + BondArpValidate::All => BOND_ARP_VALIDATE_ALL, + BondArpValidate::Filter => BOND_ARP_FILTER, + BondArpValidate::FilterActive => BOND_ARP_FILTER_ACTIVE, + BondArpValidate::FilterBackup => BOND_ARP_FILTER_BACKUP, + BondArpValidate::Other(d) => d, + } + } +} + +impl From for BondArpValidate { + fn from(value: u32) -> Self { + match value { + BOND_ARP_VALIDATE_NONE => BondArpValidate::None, + BOND_ARP_VALIDATE_ACTIVE => BondArpValidate::Active, + BOND_ARP_VALIDATE_BACKUP => BondArpValidate::Backup, + BOND_ARP_VALIDATE_ALL => BondArpValidate::All, + BOND_ARP_FILTER => BondArpValidate::Filter, + BOND_ARP_FILTER_ACTIVE => BondArpValidate::FilterActive, + BOND_ARP_FILTER_BACKUP => BondArpValidate::FilterBackup, + d => BondArpValidate::Other(d), + } + } +} + +impl std::fmt::Display for BondArpValidate { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let kernel_name = match self { + BondArpValidate::None => "none", + BondArpValidate::Active => "active", + BondArpValidate::Backup => "backup", + BondArpValidate::All => "all", + BondArpValidate::Filter => "filter", + BondArpValidate::FilterActive => "filter_active", + BondArpValidate::FilterBackup => "filter_backup", + BondArpValidate::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 +349,7 @@ pub enum InfoBond { UseCarrier(u8), ArpInterval(u32), ArpIpTarget(Vec), - ArpValidate(u32), + ArpValidate(BondArpValidate), ArpAllTargets(u32), Primary(u32), PrimaryReselect(u8), @@ -360,12 +433,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 +545,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 6a989027..647069f7 100644 --- a/src/link/link_info/mod.rs +++ b/src/link/link_info/mod.rs @@ -28,7 +28,7 @@ mod vxlan; mod xfrm; mod xstats; -pub use self::bond::{BondAdInfo, BondMode, InfoBond}; +pub use self::bond::{BondArpValidate, 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 3b218983..d0d17b55 100644 --- a/src/link/mod.rs +++ b/src/link/mod.rs @@ -38,16 +38,16 @@ 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, - BridgePortMulticastRouter, BridgePortState, BridgeQuerierState, GeneveDf, - HsrProtocol, InfoBond, InfoBondPort, InfoBridge, InfoBridgePort, InfoData, - InfoGeneve, InfoGreTap, InfoGreTap6, InfoGreTun, InfoGreTun6, InfoGtp, - InfoHsr, InfoIpVlan, InfoIpVtap, InfoIpoib, InfoKind, InfoMacSec, - InfoMacVlan, InfoMacVtap, InfoPortData, InfoPortKind, InfoSitTun, InfoTun, - InfoVeth, InfoVlan, InfoVrf, InfoVrfPort, InfoVti, InfoVxlan, InfoXfrm, - IpVlanMode, IpVtapMode, LinkInfo, LinkXstats, MacSecCipherId, - MacSecOffload, MacSecValidate, MacVlanMode, MacVtapMode, MiiStatus, - VlanQosMapping, + BondAdInfo, BondArpValidate, BondMode, BondPortState, BridgeId, + BridgeIdBuffer, BridgePortMulticastRouter, BridgePortState, + BridgeQuerierState, GeneveDf, HsrProtocol, InfoBond, InfoBondPort, + InfoBridge, InfoBridgePort, InfoData, InfoGeneve, InfoGreTap, InfoGreTap6, + InfoGreTun, InfoGreTun6, InfoGtp, InfoHsr, InfoIpVlan, InfoIpVtap, + InfoIpoib, InfoKind, InfoMacSec, InfoMacVlan, InfoMacVtap, InfoPortData, + InfoPortKind, InfoSitTun, InfoTun, InfoVeth, InfoVlan, InfoVrf, + InfoVrfPort, InfoVti, InfoVxlan, InfoXfrm, IpVlanMode, IpVtapMode, + LinkInfo, LinkXstats, MacSecCipherId, MacSecOffload, MacSecValidate, + MacVlanMode, MacVtapMode, MiiStatus, VlanQosMapping, }; pub use self::link_layer_type::LinkLayerType; pub use self::link_state::State; diff --git a/src/link/tests/bond.rs b/src/link/tests/bond.rs index 264c3aca..a002faf6 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, + BondArpValidate, 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() { @@ -63,7 +65,7 @@ fn test_bond_link_info() { InfoBond::PeerNotifDelay(0), InfoBond::UseCarrier(1), InfoBond::ArpInterval(0), - InfoBond::ArpValidate(0), + InfoBond::ArpValidate(BondArpValidate::None), InfoBond::ArpAllTargets(0), InfoBond::PrimaryReselect(0), InfoBond::FailOverMac(0), @@ -149,3 +151,77 @@ fn test_bond_port_link_info() { assert_eq!(buf, raw); } + +#[test] +fn test_bond_arp_validate() { + let raw: Vec = vec![ + 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 packet = RouteNetlinkMessage::NewLink(expected); + let mut buf = vec![0u8; packet.buffer_len()]; + packet.emit(&mut buf); + + assert_eq!(raw, buf); +}