//! K422: narrow Ethernet MAC acceptance rule for the first virtio-net fixture. //! This validates a six-byte unicast address; it does not negotiate a driver. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Error { MissingFeature, ZeroAddress, Multicast, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum DestinationError { InvalidOwn, AlienUnicast, UnsupportedMulticast, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Destination { OwnUnicast, Broadcast, } pub const VIRTIO_NET_F_MAC: u32 = 1 << 5; pub fn validate(feature_low: u32, mac: [u8; 6]) -> Result<[u8; 6], Error> { if feature_low & VIRTIO_NET_F_MAC == 0 { return Err(Error::MissingFeature); } if mac == [0; 6] { return Err(Error::ZeroAddress); } if mac[0] & 1 != 0 { return Err(Error::Multicast); } Ok(mac) } /// K431 local receive policy. A device filter is only best effort; callers /// must independently classify the destination copied from completed DMA. pub fn classify_destination( destination: [u8; 6], own: [u8; 6], ) -> Result { validate(VIRTIO_NET_F_MAC, own).map_err(|_| DestinationError::InvalidOwn)?; if destination == own { return Ok(Destination::OwnUnicast); } if destination == [0xff; 6] { return Ok(Destination::Broadcast); } if destination[0] & 1 != 0 { return Err(DestinationError::UnsupportedMulticast); } Err(DestinationError::AlienUnicast) } #[cfg(test)] mod tests { use super::*; #[test] fn k422_mac_feature_and_unicast_gate() { let first = [0x52, 0x54, 0x00, 0x12, 0x34, 0x56]; assert_eq!(validate(VIRTIO_NET_F_MAC, first), Ok(first)); assert_eq!(validate(0, first), Err(Error::MissingFeature)); assert_eq!(validate(VIRTIO_NET_F_MAC, [0; 6]), Err(Error::ZeroAddress)); assert_eq!( validate(VIRTIO_NET_F_MAC, [0x01, 0, 0x5e, 0, 0, 1]), Err(Error::Multicast) ); assert_eq!(validate(VIRTIO_NET_F_MAC, [0xff; 6]), Err(Error::Multicast)); let second = [0x52, 0x54, 0, 0xaa, 0xbb, 0xcc]; assert_eq!(validate(VIRTIO_NET_F_MAC, second), Ok(second)); } #[test] fn k431_accepts_own_unicast_and_broadcast_but_not_other_destinations() { let own = [0x52, 0x54, 0, 0x12, 0x34, 0x56]; assert_eq!(classify_destination(own, own), Ok(Destination::OwnUnicast)); assert_eq!( classify_destination([0xff; 6], own), Ok(Destination::Broadcast) ); assert_eq!( classify_destination([0x52, 0x54, 0, 0x12, 0x34, 0x57], own), Err(DestinationError::AlienUnicast) ); assert_eq!( classify_destination([0x01, 0, 0x5e, 0, 0, 1], own), Err(DestinationError::UnsupportedMulticast) ); assert_eq!( classify_destination([0xff; 6], [0; 6]), Err(DestinationError::InvalidOwn) ); } #[test] fn k432_own_unicast_requires_exact_six_byte_equality() { let own = [0x52, 0x54, 0, 0x12, 0x34, 0x56]; for index in 0..6 { let mut changed = own; changed[index] ^= 0x02; assert_ne!( classify_destination(changed, own), Ok(Destination::OwnUnicast) ); } assert_eq!(classify_destination(own, own), Ok(Destination::OwnUnicast)); assert_eq!( classify_destination([0xff; 6], own), Ok(Destination::Broadcast) ); assert_eq!( classify_destination(own, [0; 6]), Err(DestinationError::InvalidOwn) ); } }