Expose more methods on bigtcp iface

This commit is contained in:
jiangjianfeng 2025-03-31 07:07:47 +00:00 committed by Tate, Hongliang Tian
parent 5e9f537222
commit 2c41055470
9 changed files with 201 additions and 14 deletions

1
Cargo.lock generated
View File

@ -82,6 +82,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"aster-softirq", "aster-softirq",
"bitflags 1.3.2", "bitflags 1.3.2",
"int-to-c-enum",
"jhash", "jhash",
"ostd", "ostd",
"smoltcp", "smoltcp",

View File

@ -8,6 +8,7 @@ edition = "2021"
[dependencies] [dependencies]
aster-softirq = { path = "../../comps/softirq" } aster-softirq = { path = "../../comps/softirq" }
bitflags = "1.3" bitflags = "1.3"
int-to-c-enum = { path = "../int-to-c-enum" }
jhash = { path = "../jhash" } jhash = { path = "../jhash" }
ostd = { path = "../../../ostd" } ostd = { path = "../../../ostd" }
smoltcp = { git = "https://github.com/asterinas/smoltcp", tag = "r_2024-11-08_f07e5b5", default-features = false, features = [ smoltcp = { git = "https://github.com/asterinas/smoltcp", tag = "r_2024-11-08_f07e5b5", default-features = false, features = [

View File

@ -6,8 +6,11 @@ use alloc::{
sync::Arc, sync::Arc,
vec::Vec, vec::Vec,
}; };
use core::sync::atomic::{AtomicU32, Ordering};
use aster_softirq::BottomHalfDisabled; use aster_softirq::BottomHalfDisabled;
use bitflags::bitflags;
use int_to_c_enum::TryFromInt;
use ostd::sync::{SpinLock, SpinLockGuard}; use ostd::sync::{SpinLock, SpinLockGuard};
use smoltcp::{ use smoltcp::{
iface::{packet::Packet, Context}, iface::{packet::Packet, Context},
@ -30,7 +33,11 @@ use crate::{
}; };
pub struct IfaceCommon<E: Ext> { pub struct IfaceCommon<E: Ext> {
index: u32,
name: String, name: String,
type_: InterfaceType,
flags: InterfaceFlags,
interface: SpinLock<PollableIface<E>, BottomHalfDisabled>, interface: SpinLock<PollableIface<E>, BottomHalfDisabled>,
used_ports: SpinLock<BTreeMap<u16, usize>, BottomHalfDisabled>, used_ports: SpinLock<BTreeMap<u16, usize>, BottomHalfDisabled>,
sockets: SpinLock<SocketTable<E>, BottomHalfDisabled>, sockets: SpinLock<SocketTable<E>, BottomHalfDisabled>,
@ -40,11 +47,18 @@ pub struct IfaceCommon<E: Ext> {
impl<E: Ext> IfaceCommon<E> { impl<E: Ext> IfaceCommon<E> {
pub(super) fn new( pub(super) fn new(
name: String, name: String,
type_: InterfaceType,
flags: InterfaceFlags,
interface: smoltcp::iface::Interface, interface: smoltcp::iface::Interface,
sched_poll: E::ScheduleNextPoll, sched_poll: E::ScheduleNextPoll,
) -> Self { ) -> Self {
let index = INTERFACE_INDEX_ALLOCATOR.fetch_add(1, Ordering::Relaxed);
Self { Self {
index,
name, name,
type_,
flags,
interface: SpinLock::new(PollableIface::new(interface)), interface: SpinLock::new(PollableIface::new(interface)),
used_ports: SpinLock::new(BTreeMap::new()), used_ports: SpinLock::new(BTreeMap::new()),
sockets: SpinLock::new(SocketTable::new()), sockets: SpinLock::new(SocketTable::new()),
@ -52,19 +66,40 @@ impl<E: Ext> IfaceCommon<E> {
} }
} }
pub(super) fn index(&self) -> u32 {
self.index
}
pub(super) fn name(&self) -> &str { pub(super) fn name(&self) -> &str {
&self.name &self.name
} }
pub(super) fn type_(&self) -> InterfaceType {
self.type_
}
pub(super) fn flags(&self) -> InterfaceFlags {
self.flags
}
pub(super) fn ipv4_addr(&self) -> Option<Ipv4Address> { pub(super) fn ipv4_addr(&self) -> Option<Ipv4Address> {
self.interface.lock().ipv4_addr() self.interface.lock().ipv4_addr()
} }
pub(super) fn prefix_len(&self) -> Option<u8> {
self.interface.lock().prefix_len()
}
pub(super) fn sched_poll(&self) -> &E::ScheduleNextPoll { pub(super) fn sched_poll(&self) -> &E::ScheduleNextPoll {
&self.sched_poll &self.sched_poll
} }
} }
/// An allocator that allocates a unique index for each interface.
//
// FIXME: This allocator is specific to each network namespace.
pub static INTERFACE_INDEX_ALLOCATOR: AtomicU32 = AtomicU32::new(1);
// Lock order: `interface` -> `sockets` // Lock order: `interface` -> `sockets`
impl<E: Ext> IfaceCommon<E> { impl<E: Ext> IfaceCommon<E> {
/// Acquires the lock to the interface. /// Acquires the lock to the interface.
@ -245,3 +280,79 @@ impl<E: Ext> Drop for BoundPort<E> {
self.iface.common().release_port(self.port); self.iface.common().release_port(self.port);
} }
} }
/// Interface type.
///
/// Reference: <https://elixir.bootlin.com/linux/v6.0.18/source/include/uapi/linux/if_arp.h#L30>
#[repr(u16)]
#[derive(Debug, Clone, Copy, TryFromInt, PartialEq, Eq)]
pub enum InterfaceType {
// Arp protocol hardware identifiers
/// from KA9Q: NET/ROM pseudo
NETROM = 0,
/// Ethernet 10Mbps
ETHER = 1,
/// Experimental Ethernet
EETHER = 2,
// Dummy types for non ARP hardware
/// IPIP tunnel
TUNNEL = 768,
/// IP6IP6 tunnel
TUNNEL6 = 769,
/// Frame Relay Access Device
FRAD = 770,
/// SKIP vif
SKIP = 771,
/// Loopback device
LOOPBACK = 772,
/// Localtalk device
LOCALTALK = 773,
// TODO: This enum is not exhaustive
}
bitflags! {
/// Interface flags.
///
/// Reference: <https://elixir.bootlin.com/linux/v6.0.18/source/include/uapi/linux/if.h#L82>
pub struct InterfaceFlags: u32 {
/// Interface is up
const UP = 1<<0;
/// Broadcast address valid
const BROADCAST = 1<<1;
/// Turn on debugging
const DEBUG = 1<<2;
/// Loopback net
const LOOPBACK = 1<<3;
/// Interface is has p-p link
const POINTOPOINT = 1<<4;
/// Avoid use of trailers
const NOTRAILERS = 1<<5;
/// Interface RFC2863 OPER_UP
const RUNNING = 1<<6;
/// No ARP protocol
const NOARP = 1<<7;
/// Receive all packets
const PROMISC = 1<<8;
/// Receive all multicast packets
const ALLMULTI = 1<<9;
/// Master of a load balancer
const MASTER = 1<<10;
/// Slave of a load balancer
const SLAVE = 1<<11;
/// Supports multicast
const MULTICAST = 1<<12;
/// Can set media type
const PORTSEL = 1<<13;
/// Auto media select active
const AUTOMEDIA = 1<<14;
/// Dialup device with changing addresses
const DYNAMIC = 1<<15;
/// Driver signals L1 up
const LOWER_UP = 1<<16;
/// Driver signals dormant
const DORMANT = 1<<17;
/// Echo sent packets
const ECHO = 1<<18;
}
}

View File

@ -4,7 +4,7 @@ use alloc::sync::Arc;
use smoltcp::wire::Ipv4Address; use smoltcp::wire::Ipv4Address;
use super::{port::BindPortConfig, BoundPort}; use super::{port::BindPortConfig, BoundPort, InterfaceFlags, InterfaceType};
use crate::{errors::BindError, ext::Ext}; use crate::{errors::BindError, ext::Ext};
/// A network interface. /// A network interface.
@ -16,6 +16,9 @@ use crate::{errors::BindError, ext::Ext};
pub trait Iface<E>: internal::IfaceInternal<E> + Send + Sync { pub trait Iface<E>: internal::IfaceInternal<E> + Send + Sync {
/// Transmits or receives packets queued in the iface, and updates socket status accordingly. /// Transmits or receives packets queued in the iface, and updates socket status accordingly.
fn poll(&self); fn poll(&self);
/// Returns the maximum transmission unit.
fn mtu(&self) -> usize;
} }
impl<E: Ext> dyn Iface<E> { impl<E: Ext> dyn Iface<E> {
@ -38,6 +41,11 @@ impl<E: Ext> dyn Iface<E> {
common.bind(self.clone(), config) common.bind(self.clone(), config)
} }
/// Returns the interface index.
pub fn index(&self) -> u32 {
self.common().index()
}
/// Gets the name of the iface. /// Gets the name of the iface.
/// ///
/// In Linux, the name is usually the driver name followed by a unit number. /// In Linux, the name is usually the driver name followed by a unit number.
@ -45,6 +53,16 @@ impl<E: Ext> dyn Iface<E> {
self.common().name() self.common().name()
} }
/// Returns the interface type.
pub fn type_(&self) -> InterfaceType {
self.common().type_()
}
/// Returns the interface flags.
pub fn flags(&self) -> InterfaceFlags {
self.common().flags()
}
/// Gets the IPv4 address of the iface, if any. /// Gets the IPv4 address of the iface, if any.
/// ///
/// FIXME: One iface may have multiple IPv4 addresses. /// FIXME: One iface may have multiple IPv4 addresses.
@ -52,6 +70,14 @@ impl<E: Ext> dyn Iface<E> {
self.common().ipv4_addr() self.common().ipv4_addr()
} }
/// Retrieves the prefix length of the interface's IPv4 address.
///
/// Both [`Self::ipv4_addr`] and this method will either return `Some(_)`
/// or both will return `None`.
pub fn prefix_len(&self) -> Option<u8> {
self.common().prefix_len()
}
/// Returns a reference to the associated [`ScheduleNextPoll`]. /// Returns a reference to the associated [`ScheduleNextPoll`].
pub fn sched_poll(&self) -> &E::ScheduleNextPoll { pub fn sched_poll(&self) -> &E::ScheduleNextPoll {
self.common().sched_poll() self.common().sched_poll()

View File

@ -10,7 +10,7 @@ mod port;
mod sched; mod sched;
mod time; mod time;
pub use common::BoundPort; pub use common::{BoundPort, InterfaceFlags, InterfaceType};
pub use iface::Iface; pub use iface::Iface;
pub use phy::{EtherIface, IpIface}; pub use phy::{EtherIface, IpIface};
pub(crate) use poll_iface::{PollKey, PollableIfaceMut}; pub(crate) use poll_iface::{PollKey, PollableIfaceMut};

View File

@ -6,7 +6,7 @@ use aster_softirq::BottomHalfDisabled;
use ostd::sync::SpinLock; use ostd::sync::SpinLock;
use smoltcp::{ use smoltcp::{
iface::{packet::Packet, Config, Context}, iface::{packet::Packet, Config, Context},
phy::{DeviceCapabilities, TxToken}, phy::{Device, DeviceCapabilities, TxToken},
wire::{ wire::{
self, ArpOperation, ArpPacket, ArpRepr, EthernetAddress, EthernetFrame, EthernetProtocol, self, ArpOperation, ArpPacket, ArpRepr, EthernetAddress, EthernetFrame, EthernetProtocol,
EthernetRepr, IpAddress, Ipv4Address, Ipv4AddressExt, Ipv4Cidr, Ipv4Packet, EthernetRepr, IpAddress, Ipv4Address, Ipv4AddressExt, Ipv4Cidr, Ipv4Packet,
@ -17,8 +17,10 @@ use crate::{
device::{NotifyDevice, WithDevice}, device::{NotifyDevice, WithDevice},
ext::Ext, ext::Ext,
iface::{ iface::{
common::IfaceCommon, iface::internal::IfaceInternal, time::get_network_timestamp, Iface, common::{IfaceCommon, InterfaceType},
ScheduleNextPoll, iface::internal::IfaceInternal,
time::get_network_timestamp,
Iface, InterfaceFlags, ScheduleNextPoll,
}, },
}; };
@ -37,6 +39,7 @@ impl<D: WithDevice, E: Ext> EtherIface<D, E> {
gateway: Ipv4Address, gateway: Ipv4Address,
name: String, name: String,
sched_poll: E::ScheduleNextPoll, sched_poll: E::ScheduleNextPoll,
flags: InterfaceFlags,
) -> Arc<Self> { ) -> Arc<Self> {
let interface = driver.with(|device| { let interface = driver.with(|device| {
let config = Config::new(wire::HardwareAddress::Ethernet(ether_addr)); let config = Config::new(wire::HardwareAddress::Ethernet(ether_addr));
@ -54,7 +57,7 @@ impl<D: WithDevice, E: Ext> EtherIface<D, E> {
interface interface
}); });
let common = IfaceCommon::new(name, interface, sched_poll); let common = IfaceCommon::new(name, InterfaceType::ETHER, flags, interface, sched_poll);
Arc::new(Self { Arc::new(Self {
driver, driver,
@ -86,6 +89,11 @@ where
self.common.sched_poll().schedule_next_poll(next_poll); self.common.sched_poll().schedule_next_poll(next_poll);
}); });
} }
fn mtu(&self) -> usize {
self.driver
.with(|device| device.capabilities().max_transmission_unit)
}
} }
impl<D, E: Ext> EtherIface<D, E> { impl<D, E: Ext> EtherIface<D, E> {

View File

@ -4,7 +4,7 @@ use alloc::{string::String, sync::Arc};
use smoltcp::{ use smoltcp::{
iface::Config, iface::Config,
phy::TxToken, phy::{Device, TxToken},
wire::{self, Ipv4Cidr, Ipv4Packet}, wire::{self, Ipv4Cidr, Ipv4Packet},
}; };
@ -12,8 +12,10 @@ use crate::{
device::WithDevice, device::WithDevice,
ext::Ext, ext::Ext,
iface::{ iface::{
common::IfaceCommon, iface::internal::IfaceInternal, time::get_network_timestamp, Iface, common::{IfaceCommon, InterfaceFlags, InterfaceType},
ScheduleNextPoll, iface::internal::IfaceInternal,
time::get_network_timestamp,
Iface, ScheduleNextPoll,
}, },
}; };
@ -28,6 +30,8 @@ impl<D: WithDevice, E: Ext> IpIface<D, E> {
ip_cidr: Ipv4Cidr, ip_cidr: Ipv4Cidr,
name: String, name: String,
sched_poll: E::ScheduleNextPoll, sched_poll: E::ScheduleNextPoll,
type_: InterfaceType,
flags: InterfaceFlags,
) -> Arc<Self> { ) -> Arc<Self> {
let interface = driver.with(|device| { let interface = driver.with(|device| {
let config = Config::new(smoltcp::wire::HardwareAddress::Ip); let config = Config::new(smoltcp::wire::HardwareAddress::Ip);
@ -41,7 +45,7 @@ impl<D: WithDevice, E: Ext> IpIface<D, E> {
interface interface
}); });
let common = IfaceCommon::new(name, interface, sched_poll); let common = IfaceCommon::new(name, type_, flags, interface, sched_poll);
Arc::new(Self { driver, common }) Arc::new(Self { driver, common })
} }
@ -74,4 +78,9 @@ impl<D: WithDevice + 'static, E: Ext> Iface<E> for IpIface<D, E> {
self.common.sched_poll().schedule_next_poll(next_poll); self.common.sched_poll().schedule_next_poll(next_poll);
}); });
} }
fn mtu(&self) -> usize {
self.driver
.with(|device| device.capabilities().max_transmission_unit)
}
} }

View File

@ -39,6 +39,13 @@ impl<E: Ext> PollableIface<E> {
self.interface.ipv4_addr() self.interface.ipv4_addr()
} }
pub(super) fn prefix_len(&self) -> Option<u8> {
self.interface
.ip_addrs()
.first()
.map(|ip_addr| ip_addr.prefix_len())
}
/// Returns the next poll time. /// Returns the next poll time.
pub(super) fn next_poll_at_ms(&self) -> Option<u64> { pub(super) fn next_poll_at_ms(&self) -> Option<u64> {
self.pending_conns.next_poll_at_ms() self.pending_conns.next_poll_at_ms()

View File

@ -2,7 +2,10 @@
use alloc::{borrow::ToOwned, sync::Arc}; use alloc::{borrow::ToOwned, sync::Arc};
use aster_bigtcp::device::WithDevice; use aster_bigtcp::{
device::WithDevice,
iface::{InterfaceFlags, InterfaceType},
};
use aster_softirq::BottomHalfDisabled; use aster_softirq::BottomHalfDisabled;
use spin::Once; use spin::Once;
@ -15,10 +18,13 @@ pub fn init() {
IFACES.call_once(|| { IFACES.call_once(|| {
let mut ifaces = Vec::with_capacity(2); let mut ifaces = Vec::with_capacity(2);
// Initialize loopback before virtio
// to ensure the loopback interface index is ahead of virtio.
ifaces.push(new_loopback());
if let Some(iface_virtio) = new_virtio() { if let Some(iface_virtio) = new_virtio() {
ifaces.push(iface_virtio); ifaces.push(iface_virtio);
} }
ifaces.push(new_loopback());
ifaces ifaces
}); });
@ -66,13 +72,22 @@ fn new_virtio() -> Option<Arc<Iface>> {
} }
} }
// FIXME: These flags are currently hardcoded.
// In the future, we should set appropriate values.
let flags = InterfaceFlags::UP
| InterfaceFlags::BROADCAST
| InterfaceFlags::RUNNING
| InterfaceFlags::MULTICAST
| InterfaceFlags::LOWER_UP;
Some(EtherIface::new( Some(EtherIface::new(
Wrapper(virtio_net), Wrapper(virtio_net),
EthernetAddress(ether_addr), EthernetAddress(ether_addr),
Ipv4Cidr::new(VIRTIO_ADDRESS, VIRTIO_ADDRESS_PREFIX_LEN), Ipv4Cidr::new(VIRTIO_ADDRESS, VIRTIO_ADDRESS_PREFIX_LEN),
VIRTIO_GATEWAY, VIRTIO_GATEWAY,
"virtio".to_owned(), "eth0".to_owned(),
PollScheduler::new(), PollScheduler::new(),
flags,
)) ))
} }
@ -100,10 +115,19 @@ fn new_loopback() -> Arc<Iface> {
} }
} }
// FIXME: These flags are currently hardcoded.
// In the future, we should set appropriate values.
let flags = InterfaceFlags::UP
| InterfaceFlags::LOOPBACK
| InterfaceFlags::RUNNING
| InterfaceFlags::LOWER_UP;
IpIface::new( IpIface::new(
Wrapper(Mutex::new(Loopback::new(Medium::Ip))), Wrapper(Mutex::new(Loopback::new(Medium::Ip))),
Ipv4Cidr::new(LOOPBACK_ADDRESS, LOOPBACK_ADDRESS_PREFIX_LEN), Ipv4Cidr::new(LOOPBACK_ADDRESS, LOOPBACK_ADDRESS_PREFIX_LEN),
"lo".to_owned(), "lo".to_owned(),
PollScheduler::new(), PollScheduler::new(),
) as _ InterfaceType::LOOPBACK,
flags,
) as Arc<Iface>
} }