From b8078561bfa7980e713418b5c624f6cd7f256a11 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Mon, 10 Feb 2025 11:42:09 +0200 Subject: [PATCH] net: report link state to userspace --- kernel/driver/net/core/src/ethernet.rs | 67 -------- kernel/driver/net/core/src/interface.rs | 9 +- kernel/driver/net/core/src/socket/config.rs | 1 + kernel/driver/net/igbe/src/lib.rs | 6 +- kernel/driver/net/igbe/src/regs.rs | 6 +- kernel/driver/net/rtl81xx/src/rtl8168.rs | 13 +- lib/abi-serde/src/impls/net.rs | 4 +- lib/abi-serde/src/lib.rs | 2 +- lib/abi/src/macros.rs | 37 ++++- lib/abi/src/net/link.rs | 161 ++++++++++++++++++++ lib/abi/src/net/mod.rs | 5 +- lib/abi/src/net/netconfig.rs | 9 +- lib/abi/src/net/types/mod.rs | 2 +- lib/abi/src/net/types/subnet_addr.rs | 2 +- userspace/netutils/src/netconf.rs | 1 + 15 files changed, 239 insertions(+), 86 deletions(-) create mode 100644 lib/abi/src/net/link.rs diff --git a/kernel/driver/net/core/src/ethernet.rs b/kernel/driver/net/core/src/ethernet.rs index 8f2b49c7..b0ef54de 100644 --- a/kernel/driver/net/core/src/ethernet.rs +++ b/kernel/driver/net/core/src/ethernet.rs @@ -27,37 +27,6 @@ pub struct L2Packet { pub data: Arc>, } -/// Defines an Ethernet link speed -#[derive(Debug, Clone, Copy)] -pub enum EthernetSpeed { - /// 1Gbps link - Speed1000, - /// 100Mbps link - Speed100, - /// 10Mbps link - Speed10, - /// Link speed not available/unknown - Unknown, -} - -/// Defines whether an Ethernet link is capable of transmiting data both ways simultaneously -#[derive(Debug, Clone, Copy)] -pub enum Duplex { - /// Half-duplex link with multiplexed Tx and Rx - Half, - /// Full-duplex link capable of simultaneous Tx and Rx - Full, - /// Duplex mode not available/unknown - Unknown, -} - -/// Represents the state of an Ethernet link -#[derive(Debug, Clone, Copy)] -pub enum EthernetLinkState { - Up(EthernetSpeed, Duplex), - Down, -} - impl L2Packet { pub fn ethernet_frame(&self) -> &EthernetFrame { bytemuck::from_bytes( @@ -109,39 +78,3 @@ pub fn handle(packet: L2Packet) { } } } - -impl fmt::Display for EthernetSpeed { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let text = match self { - Self::Speed10 => "10Mbps", - Self::Speed100 => "100Mbps", - Self::Speed1000 => "1Gbps", - Self::Unknown => "N/A", - }; - f.write_str(text) - } -} - -impl fmt::Display for Duplex { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let text = match self { - Self::Half => "half-duplex", - Self::Full => "full-duplex", - Self::Unknown => "N/A duplex mode", - }; - f.write_str(text) - } -} - -impl fmt::Display for EthernetLinkState { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Up(speed, duplex) => { - write!(f, "up, speed {speed}, {duplex}") - } - Self::Down => { - write!(f, "down") - } - } - } -} diff --git a/kernel/driver/net/core/src/interface.rs b/kernel/driver/net/core/src/interface.rs index 2200083e..fd3a5cff 100644 --- a/kernel/driver/net/core/src/interface.rs +++ b/kernel/driver/net/core/src/interface.rs @@ -13,7 +13,7 @@ use libk_util::{ }; use yggdrasil_abi::{ error::Error, - net::{protocols::EthernetFrame, MacAddress}, + net::{link::LinkState, protocols::EthernetFrame, MacAddress}, }; use crate::l3::{arp::ArpTable, Route}; @@ -25,6 +25,9 @@ pub trait NetworkDevice: Sync + Send { fn packet_prefix_size(&self) -> usize; fn read_hardware_address(&self) -> MacAddress; + fn link_state(&self) -> LinkState { + LinkState::Other { up: true } + } } pub struct NetworkInterface { @@ -101,6 +104,10 @@ impl NetworkInterface { ArpTable::insert_address(self.id, self.mac, address, true); } + pub fn link_state(&self) -> LinkState { + self.device.link_state() + } + pub fn send_l2(&self, l2_frame: &EthernetFrame, l2_data: &[u8]) -> Result<(), Error> { let l2_offset = self.device.packet_prefix_size(); let l2_data_offset = l2_offset + size_of::(); diff --git a/kernel/driver/net/core/src/socket/config.rs b/kernel/driver/net/core/src/socket/config.rs index ccc858ae..3705b90d 100644 --- a/kernel/driver/net/core/src/socket/config.rs +++ b/kernel/driver/net/core/src/socket/config.rs @@ -250,6 +250,7 @@ fn describe_interface(interface: &NetworkInterface) -> InterfaceInfo { interface_name: interface.name.clone(), address: interface.address.read().map(Into::into), mac: interface.mac, + link: interface.link_state(), } } diff --git a/kernel/driver/net/igbe/src/lib.rs b/kernel/driver/net/igbe/src/lib.rs index 932574ad..10692e2d 100644 --- a/kernel/driver/net/igbe/src/lib.rs +++ b/kernel/driver/net/igbe/src/lib.rs @@ -28,7 +28,7 @@ use ygg_driver_pci::{ macros::pci_driver, PciBaseAddress, PciCommandRegister, PciConfigurationSpace, }; -use yggdrasil_abi::net::MacAddress; +use yggdrasil_abi::net::{link::LinkState, MacAddress}; extern crate alloc; @@ -369,6 +369,10 @@ impl NetworkDevice for Igbe { Ok(()) } + + fn link_state(&self) -> LinkState { + LinkState::Ethernet(self.regs.lock().read_link()) + } } pci_driver! { diff --git a/kernel/driver/net/igbe/src/regs.rs b/kernel/driver/net/igbe/src/regs.rs index 722d3b84..39318595 100644 --- a/kernel/driver/net/igbe/src/regs.rs +++ b/kernel/driver/net/igbe/src/regs.rs @@ -4,9 +4,11 @@ use core::{cell::UnsafeCell, marker::PhantomData, time}; use libk::error::Error; use libk_mm::{address::PhysicalAddress, device::RawDeviceMemoryMapping}; use tock_registers::{fields::FieldValue, register_bitfields, LocalRegisterCopy, RegisterLongName}; -use ygg_driver_net_core::ethernet::{Duplex, EthernetLinkState, EthernetSpeed}; use ygg_driver_pci::PciBaseAddress; -use yggdrasil_abi::net::MacAddress; +use yggdrasil_abi::net::{ + link::{Duplex, EthernetLinkState, EthernetSpeed}, + MacAddress, +}; use TXDCTL::LWTHRESH; use crate::{RxRing, TxRing}; diff --git a/kernel/driver/net/rtl81xx/src/rtl8168.rs b/kernel/driver/net/rtl81xx/src/rtl8168.rs index 59a3a88f..5cf94d08 100644 --- a/kernel/driver/net/rtl81xx/src/rtl8168.rs +++ b/kernel/driver/net/rtl81xx/src/rtl8168.rs @@ -21,12 +21,17 @@ use tock_registers::{ registers::{ReadOnly, ReadWrite, WriteOnly}, }; use ygg_driver_net_core::{ - ethernet::{Duplex, EthernetLinkState, EthernetSpeed}, interface::{NetworkDevice, NetworkInterfaceType}, Packet, }; use ygg_driver_pci::device::{PciDeviceInfo, PreferredInterruptMode}; -use yggdrasil_abi::{bitflags, net::MacAddress}; +use yggdrasil_abi::{ + bitflags, + net::{ + link::{Duplex, EthernetLinkState, EthernetSpeed, LinkState}, + MacAddress, + }, +}; register_bitfields! { u8, @@ -715,6 +720,10 @@ impl NetworkDevice for Rtl8168 { fn read_hardware_address(&self) -> MacAddress { self.mac } + + fn link_state(&self) -> LinkState { + LinkState::Ethernet(self.regs.lock().get_link_state()) + } } impl Revision { diff --git a/lib/abi-serde/src/impls/net.rs b/lib/abi-serde/src/impls/net.rs index f4fdd0b1..1ab47a4f 100644 --- a/lib/abi-serde/src/impls/net.rs +++ b/lib/abi-serde/src/impls/net.rs @@ -70,11 +70,11 @@ impl Serialize for SocketAddrV6 { } } -crate::impl_enum_serde!(IpAddr: [ +crate::impl_enum1_serde!(IpAddr: [ V4 => 4, V6 => 6 ]); -crate::impl_enum_serde!(SocketAddr: [ +crate::impl_enum1_serde!(SocketAddr: [ V4 => 4, V6 => 6 ]); diff --git a/lib/abi-serde/src/lib.rs b/lib/abi-serde/src/lib.rs index 9add94bd..b8b65f77 100644 --- a/lib/abi-serde/src/lib.rs +++ b/lib/abi-serde/src/lib.rs @@ -20,7 +20,7 @@ pub use des::Deserialize; pub use ser::Serialize; #[macro_export] -macro_rules! impl_enum_serde { +macro_rules! impl_enum1_serde { ( $name:ident $(<$lifetime:lifetime>)? : [ $( diff --git a/lib/abi/src/macros.rs b/lib/abi/src/macros.rs index dea3ad1f..34e94e06 100644 --- a/lib/abi/src/macros.rs +++ b/lib/abi/src/macros.rs @@ -1,9 +1,13 @@ /// Helper macro to define primitive enums with integer reprs, as well as their conversion methods #[macro_export] macro_rules! primitive_enum { - ($(#[$struct_meta:meta])* $vis:vis enum $name:ident: $repr:ty { - $( $(#[$variant_meta:meta])* $variant:ident = $discriminant:literal ),+ $(,)? - }) => { + ( + $(#[$struct_meta:meta])* + $vis:vis enum $name:ident: $repr:ty { + $( $(#[$variant_meta:meta])* $variant:ident = $discriminant:literal ),+ $(,)? + } + $([ $enum_extra:tt ]),* $(,)? + ) => { #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[repr($repr)] $(#[$struct_meta])* @@ -32,9 +36,36 @@ macro_rules! primitive_enum { v as $repr } } + + $( + $crate::primitive_enum_extra!($enum_extra, + impl abi_serde::Serialize for $name { + fn serialize(&self, serializer: &mut S) -> Result<(), ::Error> { + let repr = *self as $repr; + repr.serialize(serializer) + } + } + + impl<'de> abi_serde::Deserialize<'de> for $name { + fn deserialize>(deserializer: &mut D) -> Result>::Error> { + let repr = <$repr>::deserialize(deserializer)?; + Self::try_from(repr).map_err(|_| <>::Error as abi_serde::des::DeserializeError>::INVALID_ENUM_VARIANT) + } + } + ); + )* }; } +#[doc(hidden)] +#[macro_export] +macro_rules! primitive_enum_extra { + (with_serde, $($inner:item)+) => { + $($inner)+ + }; + ($any_other:ident, $($inner:item)+) => {}; +} + /// Common implementations for bitflag definitions #[macro_export] macro_rules! bitflags_impl_common { diff --git a/lib/abi/src/net/link.rs b/lib/abi/src/net/link.rs new file mode 100644 index 00000000..ffd3c255 --- /dev/null +++ b/lib/abi/src/net/link.rs @@ -0,0 +1,161 @@ +//! Types for handling network interface link state information + +use core::fmt; + +use abi_serde::{ + des::{DeserializeError, Deserializer}, + ser::Serializer, + Deserialize, Serialize, +}; + +use crate::primitive_enum; + +primitive_enum! { + /// Defines an Ethernet link speed + pub enum EthernetSpeed: u8 { + /// 1Gbps link + Speed1000 = 3, + /// 100Mbps link + Speed100 = 2, + /// 10Mbps link + Speed10 = 1, + /// Link speed not available/unknown + Unknown = 0, + } + [with_serde] +} + +primitive_enum! { + /// Defines whether an Ethernet link is capable of transmiting data both ways simultaneously + pub enum Duplex: u32 { + /// Half-duplex mode + Half = 1, + /// Full-duplex mode + Full = 2, + /// Duplex mode information not available/unknown + Unknown = 0, + } + [with_serde] +} + +/// Represents the state of an Ethernet link +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum EthernetLinkState { + /// Link is up, fields indicate the link configuration + Up(EthernetSpeed, Duplex), + /// Link is down + Down, +} + +/// Represents the state of network interface's link +#[derive(Debug, Clone, PartialEq)] +pub enum LinkState { + /// Ethernet link state + Ethernet(EthernetLinkState), + /// Any other device, just indicates whether the link is up or down + Other { + /// If `true`, the link is up + up: bool, + }, +} + +impl Serialize for EthernetLinkState { + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> { + match self { + Self::Up(speed, duplex) => { + serializer.write_u8(0x01)?; + speed.serialize(serializer)?; + duplex.serialize(serializer) + } + Self::Down => serializer.write_u8(0x00), + } + } +} + +impl Serialize for LinkState { + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> { + match self { + Self::Ethernet(state) => { + serializer.write_u8(0xEE)?; + state.serialize(serializer) + } + Self::Other { up } => { + serializer.write_u8(0xFF)?; + up.serialize(serializer) + } + } + } +} + +impl<'de> Deserialize<'de> for EthernetLinkState { + fn deserialize>(deserializer: &mut D) -> Result { + match deserializer.read_u8()? { + 0x01 => { + let speed = EthernetSpeed::deserialize(deserializer)?; + let duplex = Duplex::deserialize(deserializer)?; + Ok(Self::Up(speed, duplex)) + } + 0x00 => Ok(Self::Down), + _ => Err(D::Error::INVALID_ENUM_VARIANT), + } + } +} + +impl<'de> Deserialize<'de> for LinkState { + fn deserialize>(deserializer: &mut D) -> Result { + match deserializer.read_u8()? { + 0xEE => EthernetLinkState::deserialize(deserializer).map(Self::Ethernet), + 0xFF => { + let up = bool::deserialize(deserializer)?; + Ok(Self::Other { up }) + } + _ => Err(D::Error::INVALID_ENUM_VARIANT), + } + } +} + +impl fmt::Display for EthernetSpeed { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let text = match self { + Self::Speed10 => "10Mbps", + Self::Speed100 => "100Mbps", + Self::Speed1000 => "1Gbps", + Self::Unknown => "N/A", + }; + f.write_str(text) + } +} + +impl fmt::Display for Duplex { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let text = match self { + Self::Half => "half-duplex", + Self::Full => "full-duplex", + Self::Unknown => "N/A duplex mode", + }; + f.write_str(text) + } +} + +impl fmt::Display for EthernetLinkState { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Up(speed, duplex) => { + write!(f, "up, speed {speed}, {duplex}") + } + Self::Down => { + write!(f, "down") + } + } + } +} + +impl fmt::Display for LinkState { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Other { up: true } => write!(f, "up"), + Self::Other { up: false } => write!(f, "down"), + Self::Ethernet(ethernet) => fmt::Display::fmt(ethernet, f), + } + } +} diff --git a/lib/abi/src/net/mod.rs b/lib/abi/src/net/mod.rs index 6c5b0fa0..322cf79a 100644 --- a/lib/abi/src/net/mod.rs +++ b/lib/abi/src/net/mod.rs @@ -5,6 +5,7 @@ pub mod dns; #[cfg(feature = "alloc")] pub mod netconfig; +pub mod link; pub mod options; pub mod protocols; pub mod types; @@ -54,7 +55,7 @@ abi_serde::impl_struct_serde!(MessageHeader<'de>: [ payload, ancillary ]); -abi_serde::impl_enum_serde!(AncillaryMessage: [ +abi_serde::impl_enum1_serde!(AncillaryMessage: [ File => 1 ]); @@ -80,7 +81,7 @@ impl From for SocketInterfaceQuery<'_> { } } -abi_serde::impl_enum_serde!(SocketInterfaceQuery<'de>: [ +abi_serde::impl_enum1_serde!(SocketInterfaceQuery<'de>: [ ById => 1, ByName => 2 ]); diff --git a/lib/abi/src/net/netconfig.rs b/lib/abi/src/net/netconfig.rs index afe9a1e6..6461a32f 100644 --- a/lib/abi/src/net/netconfig.rs +++ b/lib/abi/src/net/netconfig.rs @@ -9,7 +9,7 @@ use abi_serde::{ }; use alloc::boxed::Box; -use super::{MacAddress, SocketInterfaceQuery, SubnetAddr}; +use super::{link::LinkState, MacAddress, SocketInterfaceQuery, SubnetAddr}; /// Describes the binding between interfaces and their IDs #[derive(Clone, Debug, PartialEq)] @@ -80,12 +80,15 @@ pub struct InterfaceInfo { pub address: Option, /// Interface hardware address pub mac: MacAddress, + /// Interface link status + pub link: LinkState, } abi_serde::impl_struct_serde!(InterfaceInfo: [ interface_name, interface_id, address, - mac + mac, + link ]); /// Describes a request to add a route to an interface @@ -155,7 +158,7 @@ pub enum NetConfigRequest<'a> { /// Query the MAC address of the specified IP ArpQuery(ArpQueryRequest<'a>), } -abi_serde::impl_enum_serde!(NetConfigRequest<'de>: [ +abi_serde::impl_enum1_serde!(NetConfigRequest<'de>: [ ListRoutes => 1, ListInterfaces => 2, DescribeRoutes => 3, diff --git a/lib/abi/src/net/types/mod.rs b/lib/abi/src/net/types/mod.rs index 6eb5c7f3..226248de 100644 --- a/lib/abi/src/net/types/mod.rs +++ b/lib/abi/src/net/types/mod.rs @@ -63,7 +63,7 @@ pub enum LocalSocketAddress<'a> { /// Describes an anonymous socket Anonymous(u64), } -abi_serde::impl_enum_serde!(LocalSocketAddress<'de>: [ +abi_serde::impl_enum1_serde!(LocalSocketAddress<'de>: [ Path => 1, Anonymous => 2, ]); diff --git a/lib/abi/src/net/types/subnet_addr.rs b/lib/abi/src/net/types/subnet_addr.rs index e3b06ee2..69489f75 100644 --- a/lib/abi/src/net/types/subnet_addr.rs +++ b/lib/abi/src/net/types/subnet_addr.rs @@ -29,7 +29,7 @@ pub enum SubnetAddr { /// IPv4 subnetwork V4(SubnetV4Addr), } -abi_serde::impl_enum_serde!(SubnetAddr: [ +abi_serde::impl_enum1_serde!(SubnetAddr: [ V4 => 4 ]); diff --git a/userspace/netutils/src/netconf.rs b/userspace/netutils/src/netconf.rs index c90a3256..be3417cc 100644 --- a/userspace/netutils/src/netconf.rs +++ b/userspace/netutils/src/netconf.rs @@ -227,6 +227,7 @@ pub fn run_action(section: Section) -> Result<(), Error> { fn print_interface_info(info: &InterfaceInfo) { println!("{}:", info.interface_name); println!(" Id: #{}", info.interface_id); + println!(" Link: {}", info.link); if let Some(address) = info.address { println!(" Address: {}", address); } else {