Make Ext a trait

This commit is contained in:
Ruihan Li
2024-12-03 09:47:47 +08:00
committed by Tate, Hongliang Tian
parent 20ee05e8e0
commit fa76afb3a9
16 changed files with 194 additions and 160 deletions

View File

@ -0,0 +1,16 @@
// SPDX-License-Identifier: MPL-2.0
use crate::iface::ScheduleNextPoll;
/// Extension to be implemented by users of this crate.
///
/// This should be implemented on an empty type that carries no data, since the type will never
/// actually be instantiated.
///
/// The purpose of having this trait is to allow users of this crate to inject multiple types
/// without the hassle of writing multiple trait bounds, which can be achieved by using the types
/// associated with this trait.
pub trait Ext {
/// The type for ifaces to schedule the next poll.
type ScheduleNextPoll: ScheduleNextPoll;
}

View File

@ -6,6 +6,7 @@ use alloc::{
btree_map::{BTreeMap, Entry},
btree_set::BTreeSet,
},
string::String,
sync::Arc,
};
@ -25,41 +26,52 @@ use super::{
};
use crate::{
errors::BindError,
ext::Ext,
socket::{
BoundTcpSocket, BoundTcpSocketInner, BoundUdpSocket, BoundUdpSocketInner, UnboundTcpSocket,
UnboundUdpSocket,
},
};
pub struct IfaceCommon<E> {
pub struct IfaceCommon<E: Ext> {
name: String,
interface: SpinLock<smoltcp::iface::Interface, LocalIrqDisabled>,
used_ports: SpinLock<BTreeMap<u16, usize>, PreemptDisabled>,
tcp_sockets: SpinLock<BTreeSet<KeyableArc<BoundTcpSocketInner<E>>>, LocalIrqDisabled>,
udp_sockets: SpinLock<BTreeSet<KeyableArc<BoundUdpSocketInner<E>>>, LocalIrqDisabled>,
ext: E,
sched_poll: E::ScheduleNextPoll,
}
impl<E> IfaceCommon<E> {
pub(super) fn new(interface: smoltcp::iface::Interface, ext: E) -> Self {
impl<E: Ext> IfaceCommon<E> {
pub(super) fn new(
name: String,
interface: smoltcp::iface::Interface,
sched_poll: E::ScheduleNextPoll,
) -> Self {
Self {
name,
interface: SpinLock::new(interface),
used_ports: SpinLock::new(BTreeMap::new()),
tcp_sockets: SpinLock::new(BTreeSet::new()),
udp_sockets: SpinLock::new(BTreeSet::new()),
ext,
sched_poll,
}
}
pub(super) fn name(&self) -> &str {
&self.name
}
pub(super) fn ipv4_addr(&self) -> Option<Ipv4Address> {
self.interface.lock().ipv4_addr()
}
pub(super) fn ext(&self) -> &E {
&self.ext
pub(super) fn sched_poll(&self) -> &E::ScheduleNextPoll {
&self.sched_poll
}
}
impl<E> IfaceCommon<E> {
impl<E: Ext> IfaceCommon<E> {
/// Acquires the lock to the interface.
pub(crate) fn interface(&self) -> SpinLockGuard<smoltcp::iface::Interface, LocalIrqDisabled> {
self.interface.lock()
@ -69,7 +81,7 @@ impl<E> IfaceCommon<E> {
const IP_LOCAL_PORT_START: u16 = 32768;
const IP_LOCAL_PORT_END: u16 = 60999;
impl<E> IfaceCommon<E> {
impl<E: Ext> IfaceCommon<E> {
pub(super) fn bind_tcp(
&self,
iface: Arc<dyn Iface<E>>,
@ -159,7 +171,7 @@ impl<E> IfaceCommon<E> {
}
}
impl<E> IfaceCommon<E> {
impl<E: Ext> IfaceCommon<E> {
#[allow(clippy::mutable_key_type)]
fn remove_dead_tcp_sockets(&self, sockets: &mut BTreeSet<KeyableArc<BoundTcpSocketInner<E>>>) {
sockets.retain(|socket| {
@ -192,7 +204,7 @@ impl<E> IfaceCommon<E> {
}
}
impl<E> IfaceCommon<E> {
impl<E: Ext> IfaceCommon<E> {
pub(super) fn poll<D, P, Q>(
&self,
device: &mut D,

View File

@ -7,6 +7,7 @@ use smoltcp::wire::Ipv4Address;
use super::port::BindPortConfig;
use crate::{
errors::BindError,
ext::Ext,
socket::{BoundTcpSocket, BoundUdpSocket, UnboundTcpSocket, UnboundUdpSocket},
};
@ -18,19 +19,10 @@ use crate::{
/// private network (VPN) connections.
pub trait Iface<E>: internal::IfaceInternal<E> + Send + Sync {
/// Transmits or receives packets queued in the iface, and updates socket status accordingly.
///
/// The `schedule_next_poll` callback is invoked with the time at which the next poll should be
/// performed, or `None` if no next poll is required. It's up to the caller to determine the
/// mechanism to ensure that the next poll happens at the right time (e.g. by setting a timer).
fn raw_poll(&self, schedule_next_poll: &dyn Fn(Option<u64>));
fn poll(&self);
}
impl<E> dyn Iface<E> {
/// Gets the extension of the iface.
pub fn ext(&self) -> &E {
self.common().ext()
}
impl<E: Ext> dyn Iface<E> {
/// Binds a socket to the iface.
///
/// After binding the socket to the iface, the iface will handle all packets to and from the
@ -60,19 +52,33 @@ impl<E> dyn Iface<E> {
common.bind_udp(self.clone(), socket, config)
}
/// Gets the name of the iface.
///
/// In Linux, the name is usually the driver name followed by a unit number.
pub fn name(&self) -> &str {
self.common().name()
}
/// Gets the IPv4 address of the iface, if any.
///
/// FIXME: One iface may have multiple IPv4 addresses.
pub fn ipv4_addr(&self) -> Option<Ipv4Address> {
self.common().ipv4_addr()
}
/// Returns a reference to the associated [`ScheduleNextPoll`].
pub fn sched_poll(&self) -> &E::ScheduleNextPoll {
self.common().sched_poll()
}
}
pub(super) mod internal {
use crate::iface::common::IfaceCommon;
use crate::{ext::Ext, iface::common::IfaceCommon};
/// An internal trait that abstracts the common part of different ifaces.
pub trait IfaceInternal<E> {
fn common(&self) -> &IfaceCommon<E>;
fn common(&self) -> &IfaceCommon<E>
where
E: Ext;
}
}

View File

@ -6,8 +6,10 @@ mod iface;
mod phy;
mod poll;
mod port;
mod sched;
mod time;
pub use iface::Iface;
pub use phy::{EtherIface, IpIface};
pub use port::BindPortConfig;
pub use sched::ScheduleNextPoll;

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: MPL-2.0
use alloc::{collections::btree_map::BTreeMap, sync::Arc};
use alloc::{collections::btree_map::BTreeMap, string::String, sync::Arc};
use ostd::sync::{LocalIrqDisabled, SpinLock};
use smoltcp::{
@ -14,25 +14,28 @@ use smoltcp::{
use crate::{
device::WithDevice,
ext::Ext,
iface::{
common::IfaceCommon, iface::internal::IfaceInternal, time::get_network_timestamp, Iface,
ScheduleNextPoll,
},
};
pub struct EtherIface<D, E> {
pub struct EtherIface<D, E: Ext> {
driver: D,
common: IfaceCommon<E>,
ether_addr: EthernetAddress,
arp_table: SpinLock<BTreeMap<Ipv4Address, EthernetAddress>, LocalIrqDisabled>,
}
impl<D: WithDevice, E> EtherIface<D, E> {
impl<D: WithDevice, E: Ext> EtherIface<D, E> {
pub fn new(
driver: D,
ether_addr: EthernetAddress,
ip_cidr: Ipv4Cidr,
gateway: Ipv4Address,
ext: E,
name: String,
sched_poll: E::ScheduleNextPoll,
) -> Arc<Self> {
let interface = driver.with(|device| {
let config = Config::new(wire::HardwareAddress::Ethernet(ether_addr));
@ -50,7 +53,7 @@ impl<D: WithDevice, E> EtherIface<D, E> {
interface
});
let common = IfaceCommon::new(interface, ext);
let common = IfaceCommon::new(name, interface, sched_poll);
Arc::new(Self {
driver,
@ -61,26 +64,26 @@ impl<D: WithDevice, E> EtherIface<D, E> {
}
}
impl<D, E> IfaceInternal<E> for EtherIface<D, E> {
impl<D, E: Ext> IfaceInternal<E> for EtherIface<D, E> {
fn common(&self) -> &IfaceCommon<E> {
&self.common
}
}
impl<D: WithDevice + 'static, E: Send + Sync> Iface<E> for EtherIface<D, E> {
fn raw_poll(&self, schedule_next_poll: &dyn Fn(Option<u64>)) {
impl<D: WithDevice + 'static, E: Ext> Iface<E> for EtherIface<D, E> {
fn poll(&self) {
self.driver.with(|device| {
let next_poll = self.common.poll(
&mut *device,
|data, iface_cx, tx_token| self.process(data, iface_cx, tx_token),
|pkt, iface_cx, tx_token| self.dispatch(pkt, iface_cx, tx_token),
);
schedule_next_poll(next_poll);
self.common.sched_poll().schedule_next_poll(next_poll);
});
}
}
impl<D, E> EtherIface<D, E> {
impl<D, E: Ext> EtherIface<D, E> {
fn process<'pkt, T: TxToken>(
&self,
data: &'pkt [u8],

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: MPL-2.0
use alloc::sync::Arc;
use alloc::{string::String, sync::Arc};
use smoltcp::{
iface::Config,
@ -10,18 +10,25 @@ use smoltcp::{
use crate::{
device::WithDevice,
ext::Ext,
iface::{
common::IfaceCommon, iface::internal::IfaceInternal, time::get_network_timestamp, Iface,
ScheduleNextPoll,
},
};
pub struct IpIface<D, E> {
pub struct IpIface<D, E: Ext> {
driver: D,
common: IfaceCommon<E>,
}
impl<D: WithDevice, E> IpIface<D, E> {
pub fn new(driver: D, ip_cidr: Ipv4Cidr, ext: E) -> Arc<Self> {
impl<D: WithDevice, E: Ext> IpIface<D, E> {
pub fn new(
driver: D,
ip_cidr: Ipv4Cidr,
name: String,
sched_poll: E::ScheduleNextPoll,
) -> Arc<Self> {
let interface = driver.with(|device| {
let config = Config::new(smoltcp::wire::HardwareAddress::Ip);
let now = get_network_timestamp();
@ -34,20 +41,20 @@ impl<D: WithDevice, E> IpIface<D, E> {
interface
});
let common = IfaceCommon::new(interface, ext);
let common = IfaceCommon::new(name, interface, sched_poll);
Arc::new(Self { driver, common })
}
}
impl<D, E> IfaceInternal<E> for IpIface<D, E> {
impl<D, E: Ext> IfaceInternal<E> for IpIface<D, E> {
fn common(&self) -> &IfaceCommon<E> {
&self.common
}
}
impl<D: WithDevice + 'static, E: Send + Sync> Iface<E> for IpIface<D, E> {
fn raw_poll(&self, schedule_next_poll: &dyn Fn(Option<u64>)) {
impl<D: WithDevice + 'static, E: Ext> Iface<E> for IpIface<D, E> {
fn poll(&self) {
self.driver.with(|device| {
let next_poll = self.common.poll(
device,
@ -64,7 +71,7 @@ impl<D: WithDevice + 'static, E: Send + Sync> Iface<E> for IpIface<D, E> {
});
},
);
schedule_next_poll(next_poll);
self.common.sched_poll().schedule_next_poll(next_poll);
});
}
}

View File

@ -0,0 +1,11 @@
// SPDX-License-Identifier: MPL-2.0
/// A trait to provide the `schedule_next_poll` method for ifaces.
pub trait ScheduleNextPoll: Send + Sync {
/// Schedules the next poll at the specific time.
///
/// This is invoked with the time at which the next poll should be performed, or `None` if no
/// next poll is required. It's up to the caller to determine the mechanism to ensure that the
/// next poll happens at the right time (e.g. by setting a timer).
fn schedule_next_poll(&self, ms: Option<u64>);
}

View File

@ -16,6 +16,7 @@
pub mod device;
pub mod errors;
pub mod ext;
pub mod iface;
pub mod socket;
pub mod time;

View File

@ -21,9 +21,9 @@ use super::{
event::{SocketEventObserver, SocketEvents},
RawTcpSocket, RawUdpSocket, TcpStateCheck,
};
use crate::iface::Iface;
use crate::{ext::Ext, iface::Iface};
pub struct BoundSocket<T: AnySocket, E>(Arc<BoundSocketInner<T, E>>);
pub struct BoundSocket<T: AnySocket, E: Ext>(Arc<BoundSocketInner<T, E>>);
/// [`TcpSocket`] or [`UdpSocket`].
pub trait AnySocket {
@ -33,7 +33,7 @@ pub trait AnySocket {
fn new(socket: Box<Self::RawSocket>) -> Self;
/// Called by [`BoundSocket::drop`].
fn on_drop<E>(this: &Arc<BoundSocketInner<Self, E>>)
fn on_drop<E: Ext>(this: &Arc<BoundSocketInner<Self, E>>)
where
Self: Sized;
}
@ -176,7 +176,7 @@ impl AnySocket for UdpSocket {
Self::new(socket)
}
fn on_drop<E>(this: &Arc<BoundSocketInner<Self, E>>) {
fn on_drop<E: Ext>(this: &Arc<BoundSocketInner<Self, E>>) {
this.socket.lock().close();
// A UDP socket can be removed immediately.
@ -184,7 +184,7 @@ impl AnySocket for UdpSocket {
}
}
impl<T: AnySocket, E> Drop for BoundSocket<T, E> {
impl<T: AnySocket, E: Ext> Drop for BoundSocket<T, E> {
fn drop(&mut self) {
T::on_drop(&self.0);
}
@ -193,7 +193,7 @@ impl<T: AnySocket, E> Drop for BoundSocket<T, E> {
pub(crate) type BoundTcpSocketInner<E> = BoundSocketInner<TcpSocket, E>;
pub(crate) type BoundUdpSocketInner<E> = BoundSocketInner<UdpSocket, E>;
impl<T: AnySocket, E> BoundSocket<T, E> {
impl<T: AnySocket, E: Ext> BoundSocket<T, E> {
pub(crate) fn new(
iface: Arc<dyn Iface<E>>,
port: u16,
@ -215,7 +215,7 @@ impl<T: AnySocket, E> BoundSocket<T, E> {
}
}
impl<T: AnySocket, E> BoundSocket<T, E> {
impl<T: AnySocket, E: Ext> BoundSocket<T, E> {
/// Sets the observer whose `on_events` will be called when certain iface events happen. After
/// setting, the new observer will fire once immediately to avoid missing any events.
///
@ -269,7 +269,7 @@ impl Deref for NeedIfacePoll {
}
}
impl<E> BoundTcpSocket<E> {
impl<E: Ext> BoundTcpSocket<E> {
/// Connects to a remote endpoint.
///
/// Polling the iface is _always_ required after this method succeeds.
@ -378,7 +378,7 @@ impl<E> BoundTcpSocket<E> {
}
}
impl<E> BoundUdpSocket<E> {
impl<E: Ext> BoundUdpSocket<E> {
/// Binds to a specified endpoint.
///
/// Polling the iface is _not_ required after this method succeeds.