net: report link state to userspace

This commit is contained in:
Mark Poliakov 2025-02-10 11:42:09 +02:00
parent 8db05f304e
commit b8078561bf
15 changed files with 239 additions and 86 deletions

View File

@ -27,37 +27,6 @@ pub struct L2Packet {
pub data: Arc<DmaBuffer<[u8]>>, pub data: Arc<DmaBuffer<[u8]>>,
} }
/// 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 { impl L2Packet {
pub fn ethernet_frame(&self) -> &EthernetFrame { pub fn ethernet_frame(&self) -> &EthernetFrame {
bytemuck::from_bytes( 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")
}
}
}
}

View File

@ -13,7 +13,7 @@ use libk_util::{
}; };
use yggdrasil_abi::{ use yggdrasil_abi::{
error::Error, error::Error,
net::{protocols::EthernetFrame, MacAddress}, net::{link::LinkState, protocols::EthernetFrame, MacAddress},
}; };
use crate::l3::{arp::ArpTable, Route}; use crate::l3::{arp::ArpTable, Route};
@ -25,6 +25,9 @@ pub trait NetworkDevice: Sync + Send {
fn packet_prefix_size(&self) -> usize; fn packet_prefix_size(&self) -> usize;
fn read_hardware_address(&self) -> MacAddress; fn read_hardware_address(&self) -> MacAddress;
fn link_state(&self) -> LinkState {
LinkState::Other { up: true }
}
} }
pub struct NetworkInterface { pub struct NetworkInterface {
@ -101,6 +104,10 @@ impl NetworkInterface {
ArpTable::insert_address(self.id, self.mac, address, true); 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> { pub fn send_l2(&self, l2_frame: &EthernetFrame, l2_data: &[u8]) -> Result<(), Error> {
let l2_offset = self.device.packet_prefix_size(); let l2_offset = self.device.packet_prefix_size();
let l2_data_offset = l2_offset + size_of::<EthernetFrame>(); let l2_data_offset = l2_offset + size_of::<EthernetFrame>();

View File

@ -250,6 +250,7 @@ fn describe_interface(interface: &NetworkInterface) -> InterfaceInfo {
interface_name: interface.name.clone(), interface_name: interface.name.clone(),
address: interface.address.read().map(Into::into), address: interface.address.read().map(Into::into),
mac: interface.mac, mac: interface.mac,
link: interface.link_state(),
} }
} }

View File

@ -28,7 +28,7 @@ use ygg_driver_pci::{
macros::pci_driver, macros::pci_driver,
PciBaseAddress, PciCommandRegister, PciConfigurationSpace, PciBaseAddress, PciCommandRegister, PciConfigurationSpace,
}; };
use yggdrasil_abi::net::MacAddress; use yggdrasil_abi::net::{link::LinkState, MacAddress};
extern crate alloc; extern crate alloc;
@ -369,6 +369,10 @@ impl NetworkDevice for Igbe {
Ok(()) Ok(())
} }
fn link_state(&self) -> LinkState {
LinkState::Ethernet(self.regs.lock().read_link())
}
} }
pci_driver! { pci_driver! {

View File

@ -4,9 +4,11 @@ use core::{cell::UnsafeCell, marker::PhantomData, time};
use libk::error::Error; use libk::error::Error;
use libk_mm::{address::PhysicalAddress, device::RawDeviceMemoryMapping}; use libk_mm::{address::PhysicalAddress, device::RawDeviceMemoryMapping};
use tock_registers::{fields::FieldValue, register_bitfields, LocalRegisterCopy, RegisterLongName}; use tock_registers::{fields::FieldValue, register_bitfields, LocalRegisterCopy, RegisterLongName};
use ygg_driver_net_core::ethernet::{Duplex, EthernetLinkState, EthernetSpeed};
use ygg_driver_pci::PciBaseAddress; use ygg_driver_pci::PciBaseAddress;
use yggdrasil_abi::net::MacAddress; use yggdrasil_abi::net::{
link::{Duplex, EthernetLinkState, EthernetSpeed},
MacAddress,
};
use TXDCTL::LWTHRESH; use TXDCTL::LWTHRESH;
use crate::{RxRing, TxRing}; use crate::{RxRing, TxRing};

View File

@ -21,12 +21,17 @@ use tock_registers::{
registers::{ReadOnly, ReadWrite, WriteOnly}, registers::{ReadOnly, ReadWrite, WriteOnly},
}; };
use ygg_driver_net_core::{ use ygg_driver_net_core::{
ethernet::{Duplex, EthernetLinkState, EthernetSpeed},
interface::{NetworkDevice, NetworkInterfaceType}, interface::{NetworkDevice, NetworkInterfaceType},
Packet, Packet,
}; };
use ygg_driver_pci::device::{PciDeviceInfo, PreferredInterruptMode}; 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! { register_bitfields! {
u8, u8,
@ -715,6 +720,10 @@ impl NetworkDevice for Rtl8168 {
fn read_hardware_address(&self) -> MacAddress { fn read_hardware_address(&self) -> MacAddress {
self.mac self.mac
} }
fn link_state(&self) -> LinkState {
LinkState::Ethernet(self.regs.lock().get_link_state())
}
} }
impl Revision { impl Revision {

View File

@ -70,11 +70,11 @@ impl Serialize for SocketAddrV6 {
} }
} }
crate::impl_enum_serde!(IpAddr: [ crate::impl_enum1_serde!(IpAddr: [
V4 => 4, V4 => 4,
V6 => 6 V6 => 6
]); ]);
crate::impl_enum_serde!(SocketAddr: [ crate::impl_enum1_serde!(SocketAddr: [
V4 => 4, V4 => 4,
V6 => 6 V6 => 6
]); ]);

View File

@ -20,7 +20,7 @@ pub use des::Deserialize;
pub use ser::Serialize; pub use ser::Serialize;
#[macro_export] #[macro_export]
macro_rules! impl_enum_serde { macro_rules! impl_enum1_serde {
( (
$name:ident $(<$lifetime:lifetime>)? : [ $name:ident $(<$lifetime:lifetime>)? : [
$( $(

View File

@ -1,9 +1,13 @@
/// Helper macro to define primitive enums with integer reprs, as well as their conversion methods /// Helper macro to define primitive enums with integer reprs, as well as their conversion methods
#[macro_export] #[macro_export]
macro_rules! primitive_enum { 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)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr($repr)] #[repr($repr)]
$(#[$struct_meta])* $(#[$struct_meta])*
@ -32,9 +36,36 @@ macro_rules! primitive_enum {
v as $repr v as $repr
} }
} }
$(
$crate::primitive_enum_extra!($enum_extra,
impl abi_serde::Serialize for $name {
fn serialize<S: abi_serde::ser::Serializer>(&self, serializer: &mut S) -> Result<(), <S as abi_serde::ser::Serializer>::Error> {
let repr = *self as $repr;
repr.serialize(serializer)
}
}
impl<'de> abi_serde::Deserialize<'de> for $name {
fn deserialize<D: abi_serde::des::Deserializer<'de>>(deserializer: &mut D) -> Result<Self, <D as abi_serde::des::Deserializer<'de>>::Error> {
let repr = <$repr>::deserialize(deserializer)?;
Self::try_from(repr).map_err(|_| <<D as abi_serde::des::Deserializer<'de>>::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 /// Common implementations for bitflag definitions
#[macro_export] #[macro_export]
macro_rules! bitflags_impl_common { macro_rules! bitflags_impl_common {

161
lib/abi/src/net/link.rs Normal file
View File

@ -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<S: Serializer>(&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<S: Serializer>(&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<D: Deserializer<'de>>(deserializer: &mut D) -> Result<Self, D::Error> {
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<D: Deserializer<'de>>(deserializer: &mut D) -> Result<Self, D::Error> {
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),
}
}
}

View File

@ -5,6 +5,7 @@ pub mod dns;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub mod netconfig; pub mod netconfig;
pub mod link;
pub mod options; pub mod options;
pub mod protocols; pub mod protocols;
pub mod types; pub mod types;
@ -54,7 +55,7 @@ abi_serde::impl_struct_serde!(MessageHeader<'de>: [
payload, payload,
ancillary ancillary
]); ]);
abi_serde::impl_enum_serde!(AncillaryMessage: [ abi_serde::impl_enum1_serde!(AncillaryMessage: [
File => 1 File => 1
]); ]);
@ -80,7 +81,7 @@ impl From<u32> for SocketInterfaceQuery<'_> {
} }
} }
abi_serde::impl_enum_serde!(SocketInterfaceQuery<'de>: [ abi_serde::impl_enum1_serde!(SocketInterfaceQuery<'de>: [
ById => 1, ById => 1,
ByName => 2 ByName => 2
]); ]);

View File

@ -9,7 +9,7 @@ use abi_serde::{
}; };
use alloc::boxed::Box; use alloc::boxed::Box;
use super::{MacAddress, SocketInterfaceQuery, SubnetAddr}; use super::{link::LinkState, MacAddress, SocketInterfaceQuery, SubnetAddr};
/// Describes the binding between interfaces and their IDs /// Describes the binding between interfaces and their IDs
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
@ -80,12 +80,15 @@ pub struct InterfaceInfo {
pub address: Option<IpAddr>, pub address: Option<IpAddr>,
/// Interface hardware address /// Interface hardware address
pub mac: MacAddress, pub mac: MacAddress,
/// Interface link status
pub link: LinkState,
} }
abi_serde::impl_struct_serde!(InterfaceInfo: [ abi_serde::impl_struct_serde!(InterfaceInfo: [
interface_name, interface_name,
interface_id, interface_id,
address, address,
mac mac,
link
]); ]);
/// Describes a request to add a route to an interface /// 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 /// Query the MAC address of the specified IP
ArpQuery(ArpQueryRequest<'a>), ArpQuery(ArpQueryRequest<'a>),
} }
abi_serde::impl_enum_serde!(NetConfigRequest<'de>: [ abi_serde::impl_enum1_serde!(NetConfigRequest<'de>: [
ListRoutes => 1, ListRoutes => 1,
ListInterfaces => 2, ListInterfaces => 2,
DescribeRoutes => 3, DescribeRoutes => 3,

View File

@ -63,7 +63,7 @@ pub enum LocalSocketAddress<'a> {
/// Describes an anonymous socket /// Describes an anonymous socket
Anonymous(u64), Anonymous(u64),
} }
abi_serde::impl_enum_serde!(LocalSocketAddress<'de>: [ abi_serde::impl_enum1_serde!(LocalSocketAddress<'de>: [
Path => 1, Path => 1,
Anonymous => 2, Anonymous => 2,
]); ]);

View File

@ -29,7 +29,7 @@ pub enum SubnetAddr {
/// IPv4 subnetwork /// IPv4 subnetwork
V4(SubnetV4Addr), V4(SubnetV4Addr),
} }
abi_serde::impl_enum_serde!(SubnetAddr: [ abi_serde::impl_enum1_serde!(SubnetAddr: [
V4 => 4 V4 => 4
]); ]);

View File

@ -227,6 +227,7 @@ pub fn run_action(section: Section) -> Result<(), Error> {
fn print_interface_info(info: &InterfaceInfo) { fn print_interface_info(info: &InterfaceInfo) {
println!("{}:", info.interface_name); println!("{}:", info.interface_name);
println!(" Id: #{}", info.interface_id); println!(" Id: #{}", info.interface_id);
println!(" Link: {}", info.link);
if let Some(address) = info.address { if let Some(address) = info.address {
println!(" Address: {}", address); println!(" Address: {}", address);
} else { } else {