| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use fidl_fuchsia_hardware_ethernet as fidl; |
| |
| use bitflags::bitflags; |
| |
| #[derive(PartialEq, Eq, Debug, Clone, Copy, Hash)] |
| pub struct MacAddress { |
| pub octets: [u8; 6], |
| } |
| |
| impl From<fidl::MacAddress> for MacAddress { |
| fn from(fidl::MacAddress { octets }: fidl::MacAddress) -> Self { |
| Self { octets } |
| } |
| } |
| |
| impl Into<fidl::MacAddress> for MacAddress { |
| fn into(self) -> fidl::MacAddress { |
| let Self { octets } = self; |
| fidl::MacAddress { octets } |
| } |
| } |
| |
| impl std::fmt::Display for MacAddress { |
| fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { |
| let Self { octets } = self; |
| for (i, byte) in octets.iter().enumerate() { |
| if i > 0 { |
| write!(f, ":")?; |
| } |
| write!(f, "{:02x}", byte)?; |
| } |
| Ok(()) |
| } |
| } |
| |
| impl serde::Serialize for MacAddress { |
| fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { |
| serializer.collect_str(&self) |
| } |
| } |
| |
| impl<'de> serde::Deserialize<'de> for MacAddress { |
| fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> { |
| let s = <String as serde::Deserialize>::deserialize(deserializer)?; |
| <Self as std::str::FromStr>::from_str(&s).map_err(serde::de::Error::custom) |
| } |
| } |
| |
| impl std::str::FromStr for MacAddress { |
| type Err = failure::Error; |
| |
| fn from_str(s: &str) -> Result<Self, Self::Err> { |
| use failure::ResultExt; |
| |
| let mut octets = [0; 6]; |
| let mut iter = s.split(':'); |
| for (i, octet) in octets.iter_mut().enumerate() { |
| let next_octet = iter.next().ok_or_else(|| { |
| failure::format_err!("MAC address [{}] only specifies {} out of 6 octets", s, i) |
| })?; |
| *octet = u8::from_str_radix(next_octet, 16) |
| .with_context(|_| format!("could not parse hex integer from {}", next_octet))?; |
| } |
| if iter.next().is_some() { |
| return Err(failure::format_err!( |
| "MAC address has more than six octets: {}", |
| s |
| )); |
| } |
| Ok(MacAddress { octets }) |
| } |
| } |
| |
| bitflags! { |
| /// Features supported by an Ethernet device. |
| #[repr(transparent)] |
| pub struct EthernetFeatures: u32 { |
| /// The Ethernet device is a wireless device. |
| const WLAN = fidl::INFO_FEATURE_WLAN; |
| /// The Ethernet device does not represent a hardware device. |
| const SYNTHETIC = fidl::INFO_FEATURE_SYNTH; |
| /// The Ethernet device is a loopback device. |
| /// |
| /// This bit should not be set outside of network stacks. |
| const LOOPBACK = fidl::INFO_FEATURE_LOOPBACK; |
| } |
| } |
| |
| impl EthernetFeatures { |
| pub fn is_physical(&self) -> bool { |
| !self.intersects(Self::SYNTHETIC | Self::LOOPBACK) |
| } |
| } |
| |
| impl std::str::FromStr for EthernetFeatures { |
| type Err = failure::Error; |
| |
| fn from_str(s: &str) -> Result<Self, Self::Err> { |
| match s.as_ref() { |
| "synthetic" => Ok(Self::SYNTHETIC), |
| "loopback" => Ok(Self::LOOPBACK), |
| "wireless" => Ok(Self::WLAN), |
| s => Err(failure::format_err!( |
| "unknown network interface feature \"{}\"", |
| s |
| )), |
| } |
| } |
| } |
| |
| /// Information retrieved about an Ethernet device. |
| #[derive(Debug)] |
| pub struct EthernetInfo { |
| /// The features supported by the device. |
| pub features: EthernetFeatures, |
| /// The maximum transmission unit (MTU) of the device. |
| pub mtu: u32, |
| /// The MAC address of the device. |
| pub mac: MacAddress, |
| } |
| |
| impl From<fidl::Info> for EthernetInfo { |
| fn from(fidl::Info { features, mtu, mac }: fidl::Info) -> Self { |
| let features = EthernetFeatures::from_bits_truncate(features); |
| let mac = mac.into(); |
| Self { features, mtu, mac } |
| } |
| } |
| |
| bitflags! { |
| /// Status flags for an Ethernet device. |
| #[repr(transparent)] |
| pub struct EthernetStatus: u32 { |
| /// The Ethernet device is online, meaning its physical link is up. |
| const ONLINE = fidl::DEVICE_STATUS_ONLINE; |
| } |
| } |
| |
| bitflags! { |
| /// Status flags describing the result of queueing a packet to an Ethernet device. |
| #[repr(transparent)] |
| pub struct EthernetQueueFlags: u16 { |
| /// The packet was received correctly. |
| const RX_OK = fidl::FIFO_RX_OK as u16; |
| /// The packet was transmitted correctly. |
| const TX_OK = fidl::FIFO_TX_OK as u16; |
| /// The packet was out of the bounds of the memory shared with the Ethernet device driver. |
| const INVALID = fidl::FIFO_INVALID as u16; |
| /// The received packet was sent by this host. |
| /// |
| /// This bit is only set after `tx_listen_start` is called. |
| const TX_ECHO = fidl::FIFO_RX_TX as u16; |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| |
| use super::*; |
| use std::collections::HashMap; |
| use std::str::FromStr; |
| |
| #[test] |
| fn mac_addr_from_str_with_valid_str_returns_mac_addr() { |
| let result = MacAddress::from_str("AA:BB:CC:DD:EE:FF").unwrap(); |
| let expected = MacAddress { |
| octets: [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF], |
| }; |
| |
| assert_eq!(expected, result); |
| } |
| |
| #[test] |
| fn mac_addr_from_str_with_invalid_digit_returns_err() { |
| let result = MacAddress::from_str("11:22:33:44:55:GG"); |
| |
| assert!(result.is_err()); |
| } |
| |
| #[test] |
| fn mac_addr_from_str_with_invalid_format_returns_err() { |
| let result = MacAddress::from_str("11-22-33-44-55-66"); |
| |
| assert!(result.is_err()); |
| } |
| |
| #[test] |
| fn mac_addr_from_str_with_empty_string_returns_err() { |
| let result = MacAddress::from_str(""); |
| |
| assert!(result.is_err()); |
| } |
| |
| #[test] |
| fn mac_addr_from_str_with_extra_quotes_returns_err() { |
| let result = MacAddress::from_str("\"11:22:33:44:55:66\""); |
| |
| assert!(result.is_err()); |
| } |
| |
| #[test] |
| fn valid_mac_addr_array_deserializes_to_vec_of_mac_addrs() { |
| let result: Vec<MacAddress> = |
| serde_json::from_str("[\"11:11:11:11:11:11\", \"AA:AA:AA:AA:AA:AA\"]").unwrap(); |
| let expected = vec![ |
| MacAddress { |
| octets: [0x11, 0x11, 0x11, 0x11, 0x11, 0x11], |
| }, |
| MacAddress { |
| octets: [0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA], |
| }, |
| ]; |
| |
| assert_eq!(expected, result); |
| } |
| |
| #[test] |
| fn mac_addr_to_mac_addr_map_deserializes_to_hashmap() { |
| let result: HashMap<MacAddress, MacAddress> = |
| serde_json::from_str("{\"11:22:33:44:55:66\": \"AA:BB:CC:DD:EE:FF\"}").unwrap(); |
| let mut expected = HashMap::new(); |
| expected.insert( |
| MacAddress { |
| octets: [0x11, 0x22, 0x33, 0x44, 0x55, 0x66], |
| }, |
| MacAddress { |
| octets: [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF], |
| }, |
| ); |
| |
| assert_eq!(expected, result); |
| } |
| |
| #[test] |
| fn mac_addr_to_mac_addr_map_serializes_to_valid_json() { |
| let mut mac_addr_map = HashMap::new(); |
| mac_addr_map.insert( |
| MacAddress { |
| octets: [0x11, 0x22, 0x33, 0x44, 0x55, 0x66], |
| }, |
| MacAddress { |
| octets: [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF], |
| }, |
| ); |
| |
| let result = serde_json::to_string(&mac_addr_map).unwrap(); |
| |
| assert_eq!("{\"11:22:33:44:55:66\":\"aa:bb:cc:dd:ee:ff\"}", result); |
| } |
| } |