mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-29 04:13:24 +00:00
Move smoltcp-related code to bigtcp
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
9fba9445bd
commit
67d3682116
@ -1,236 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use super::{ext::IfaceExt, Iface};
|
||||
use crate::{
|
||||
events::Observer,
|
||||
net::socket::ip::{IpAddress, IpEndpoint},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
pub type RawTcpSocket = smoltcp::socket::tcp::Socket<'static>;
|
||||
pub type RawUdpSocket = smoltcp::socket::udp::Socket<'static>;
|
||||
|
||||
pub struct AnyUnboundSocket {
|
||||
socket_family: AnyRawSocket,
|
||||
observer: Weak<dyn Observer<()>>,
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub(super) enum AnyRawSocket {
|
||||
Tcp(RawTcpSocket),
|
||||
Udp(RawUdpSocket),
|
||||
}
|
||||
|
||||
pub(super) enum SocketFamily {
|
||||
Tcp,
|
||||
Udp,
|
||||
}
|
||||
|
||||
impl AnyUnboundSocket {
|
||||
pub fn new_tcp(observer: Weak<dyn Observer<()>>) -> Self {
|
||||
let raw_tcp_socket = {
|
||||
let rx_buffer = smoltcp::socket::tcp::SocketBuffer::new(vec![0u8; TCP_RECV_BUF_LEN]);
|
||||
let tx_buffer = smoltcp::socket::tcp::SocketBuffer::new(vec![0u8; TCP_SEND_BUF_LEN]);
|
||||
RawTcpSocket::new(rx_buffer, tx_buffer)
|
||||
};
|
||||
AnyUnboundSocket {
|
||||
socket_family: AnyRawSocket::Tcp(raw_tcp_socket),
|
||||
observer,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_udp(observer: Weak<dyn Observer<()>>) -> Self {
|
||||
let raw_udp_socket = {
|
||||
let metadata = smoltcp::socket::udp::PacketMetadata::EMPTY;
|
||||
let rx_buffer = smoltcp::socket::udp::PacketBuffer::new(
|
||||
vec![metadata; UDP_METADATA_LEN],
|
||||
vec![0u8; UDP_RECV_PAYLOAD_LEN],
|
||||
);
|
||||
let tx_buffer = smoltcp::socket::udp::PacketBuffer::new(
|
||||
vec![metadata; UDP_METADATA_LEN],
|
||||
vec![0u8; UDP_SEND_PAYLOAD_LEN],
|
||||
);
|
||||
RawUdpSocket::new(rx_buffer, tx_buffer)
|
||||
};
|
||||
AnyUnboundSocket {
|
||||
socket_family: AnyRawSocket::Udp(raw_udp_socket),
|
||||
observer,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn into_raw(self) -> (AnyRawSocket, Weak<dyn Observer<()>>) {
|
||||
(self.socket_family, self.observer)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AnyBoundSocket<E = IfaceExt>(Arc<AnyBoundSocketInner<E>>);
|
||||
|
||||
impl<E> AnyBoundSocket<E> {
|
||||
pub(super) fn new(
|
||||
iface: Arc<dyn Iface<E>>,
|
||||
handle: smoltcp::iface::SocketHandle,
|
||||
port: u16,
|
||||
socket_family: SocketFamily,
|
||||
observer: Weak<dyn Observer<()>>,
|
||||
) -> Self {
|
||||
Self(Arc::new(AnyBoundSocketInner {
|
||||
iface,
|
||||
handle,
|
||||
port,
|
||||
socket_family,
|
||||
observer: RwLock::new(observer),
|
||||
}))
|
||||
}
|
||||
|
||||
pub(super) fn inner(&self) -> &Arc<AnyBoundSocketInner<E>> {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Set 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.
|
||||
///
|
||||
/// If there is an existing observer, due to race conditions, this function does not guarantee
|
||||
/// that the old observer will never be called after the setting. Users should be aware of this
|
||||
/// and proactively handle the race conditions if necessary.
|
||||
pub fn set_observer(&self, handler: Weak<dyn Observer<()>>) {
|
||||
*self.0.observer.write() = handler;
|
||||
|
||||
self.0.on_iface_events();
|
||||
}
|
||||
|
||||
pub fn local_endpoint(&self) -> Option<IpEndpoint> {
|
||||
let ip_addr = {
|
||||
let ipv4_addr = self.0.iface.ipv4_addr()?;
|
||||
IpAddress::Ipv4(ipv4_addr)
|
||||
};
|
||||
Some(IpEndpoint::new(ip_addr, self.0.port))
|
||||
}
|
||||
|
||||
pub fn raw_with<T: smoltcp::socket::AnySocket<'static>, R, F: FnMut(&mut T) -> R>(
|
||||
&self,
|
||||
f: F,
|
||||
) -> R {
|
||||
self.0.raw_with(f)
|
||||
}
|
||||
|
||||
/// Connects to a remote endpoint.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This method will panic if the socket is not a TCP socket.
|
||||
pub fn do_connect(&self, remote_endpoint: IpEndpoint) -> Result<()> {
|
||||
let common = self.iface().common();
|
||||
|
||||
let mut sockets = common.sockets();
|
||||
let socket = sockets.get_mut::<RawTcpSocket>(self.0.handle);
|
||||
|
||||
let mut iface = common.interface();
|
||||
let cx = iface.context();
|
||||
|
||||
// The only reason this method might fail is because we're trying to connect to an
|
||||
// unspecified address (i.e. 0.0.0.0). We currently have no support for binding to,
|
||||
// listening on, or connecting to the unspecified address.
|
||||
//
|
||||
// We assume the remote will just refuse to connect, so we return `ECONNREFUSED`.
|
||||
socket
|
||||
.connect(cx, remote_endpoint, self.0.port)
|
||||
.map_err(|_| {
|
||||
Error::with_message(
|
||||
Errno::ECONNREFUSED,
|
||||
"connecting to an unspecified address is not supported",
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn iface(&self) -> &Arc<dyn Iface<E>> {
|
||||
&self.0.iface
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> Drop for AnyBoundSocket<E> {
|
||||
fn drop(&mut self) {
|
||||
if self.0.start_closing() {
|
||||
self.0.iface.common().remove_bound_socket_now(&self.0);
|
||||
} else {
|
||||
self.0
|
||||
.iface
|
||||
.common()
|
||||
.remove_bound_socket_when_closed(&self.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct AnyBoundSocketInner<E> {
|
||||
iface: Arc<dyn Iface<E>>,
|
||||
handle: smoltcp::iface::SocketHandle,
|
||||
port: u16,
|
||||
socket_family: SocketFamily,
|
||||
observer: RwLock<Weak<dyn Observer<()>>>,
|
||||
}
|
||||
|
||||
impl<E> AnyBoundSocketInner<E> {
|
||||
pub(super) fn on_iface_events(&self) {
|
||||
if let Some(observer) = Weak::upgrade(&*self.observer.read()) {
|
||||
observer.on_events(&())
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn is_closed(&self) -> bool {
|
||||
match self.socket_family {
|
||||
SocketFamily::Tcp => self.raw_with(|socket: &mut RawTcpSocket| {
|
||||
socket.state() == smoltcp::socket::tcp::State::Closed
|
||||
}),
|
||||
SocketFamily::Udp => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Starts closing the socket and returns whether the socket is closed.
|
||||
///
|
||||
/// For sockets that can be closed immediately, such as UDP sockets and TCP listening sockets,
|
||||
/// this method will always return `true`.
|
||||
///
|
||||
/// For other sockets, such as TCP connected sockets, they cannot be closed immediately because
|
||||
/// we at least need to send the FIN packet and wait for the remote end to send an ACK packet.
|
||||
/// In this case, this method will return `false` and [`Self::is_closed`] can be used to
|
||||
/// determine if the closing process is complete.
|
||||
fn start_closing(&self) -> bool {
|
||||
match self.socket_family {
|
||||
SocketFamily::Tcp => self.raw_with(|socket: &mut RawTcpSocket| {
|
||||
socket.close();
|
||||
socket.state() == smoltcp::socket::tcp::State::Closed
|
||||
}),
|
||||
SocketFamily::Udp => {
|
||||
self.raw_with(|socket: &mut RawUdpSocket| socket.close());
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn raw_with<T: smoltcp::socket::AnySocket<'static>, R, F: FnMut(&mut T) -> R>(
|
||||
&self,
|
||||
mut f: F,
|
||||
) -> R {
|
||||
let mut sockets = self.iface.common().sockets();
|
||||
let socket = sockets.get_mut::<T>(self.handle);
|
||||
f(socket)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> Drop for AnyBoundSocketInner<E> {
|
||||
fn drop(&mut self) {
|
||||
let iface_common = self.iface.common();
|
||||
iface_common.remove_socket(self.handle);
|
||||
iface_common.release_port(self.port);
|
||||
}
|
||||
}
|
||||
|
||||
// For TCP
|
||||
pub const TCP_RECV_BUF_LEN: usize = 65536;
|
||||
pub const TCP_SEND_BUF_LEN: usize = 65536;
|
||||
|
||||
// For UDP
|
||||
pub const UDP_SEND_PAYLOAD_LEN: usize = 65536;
|
||||
pub const UDP_RECV_PAYLOAD_LEN: usize = 65536;
|
||||
const UDP_METADATA_LEN: usize = 256;
|
@ -1,244 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use alloc::collections::btree_map::Entry;
|
||||
|
||||
use keyable_arc::KeyableArc;
|
||||
use ostd::sync::LocalIrqDisabled;
|
||||
use smoltcp::{
|
||||
iface::{SocketHandle, SocketSet},
|
||||
phy::Device,
|
||||
};
|
||||
|
||||
use super::{
|
||||
any_socket::{AnyBoundSocketInner, AnyRawSocket, AnyUnboundSocket, SocketFamily},
|
||||
ext::IfaceExt,
|
||||
time::get_network_timestamp,
|
||||
util::BindPortConfig,
|
||||
AnyBoundSocket, Iface,
|
||||
};
|
||||
use crate::{net::socket::ip::Ipv4Address, prelude::*};
|
||||
|
||||
pub struct IfaceCommon<E = IfaceExt> {
|
||||
interface: SpinLock<smoltcp::iface::Interface>,
|
||||
sockets: SpinLock<SocketSet<'static>>,
|
||||
used_ports: RwLock<BTreeMap<u16, usize>>,
|
||||
bound_sockets: RwLock<BTreeSet<KeyableArc<AnyBoundSocketInner<E>>>>,
|
||||
closing_sockets: SpinLock<BTreeSet<KeyableArc<AnyBoundSocketInner<E>>>>,
|
||||
ext: E,
|
||||
}
|
||||
|
||||
impl<E> IfaceCommon<E> {
|
||||
pub(super) fn new(interface: smoltcp::iface::Interface, ext: E) -> Self {
|
||||
let socket_set = SocketSet::new(Vec::new());
|
||||
let used_ports = BTreeMap::new();
|
||||
Self {
|
||||
interface: SpinLock::new(interface),
|
||||
sockets: SpinLock::new(socket_set),
|
||||
used_ports: RwLock::new(used_ports),
|
||||
bound_sockets: RwLock::new(BTreeSet::new()),
|
||||
closing_sockets: SpinLock::new(BTreeSet::new()),
|
||||
ext,
|
||||
}
|
||||
}
|
||||
|
||||
/// Acquires the lock to the interface.
|
||||
///
|
||||
/// *Lock ordering:* [`Self::sockets`] first, [`Self::interface`] second.
|
||||
pub(super) fn interface(&self) -> SpinLockGuard<smoltcp::iface::Interface, LocalIrqDisabled> {
|
||||
self.interface.disable_irq().lock()
|
||||
}
|
||||
|
||||
/// Acuqires the lock to the sockets.
|
||||
///
|
||||
/// *Lock ordering:* [`Self::sockets`] first, [`Self::interface`] second.
|
||||
pub(super) fn sockets(
|
||||
&self,
|
||||
) -> SpinLockGuard<smoltcp::iface::SocketSet<'static>, LocalIrqDisabled> {
|
||||
self.sockets.disable_irq().lock()
|
||||
}
|
||||
|
||||
pub(super) fn ipv4_addr(&self) -> Option<Ipv4Address> {
|
||||
self.interface.disable_irq().lock().ipv4_addr()
|
||||
}
|
||||
|
||||
/// Alloc an unused port range from 49152 ~ 65535 (According to smoltcp docs)
|
||||
fn alloc_ephemeral_port(&self) -> Result<u16> {
|
||||
let mut used_ports = self.used_ports.write();
|
||||
for port in IP_LOCAL_PORT_START..=IP_LOCAL_PORT_END {
|
||||
if let Entry::Vacant(e) = used_ports.entry(port) {
|
||||
e.insert(0);
|
||||
return Ok(port);
|
||||
}
|
||||
}
|
||||
return_errno_with_message!(Errno::EAGAIN, "no ephemeral port is available");
|
||||
}
|
||||
|
||||
fn bind_port(&self, port: u16, can_reuse: bool) -> Result<()> {
|
||||
let mut used_ports = self.used_ports.write();
|
||||
if let Some(used_times) = used_ports.get_mut(&port) {
|
||||
if *used_times == 0 || can_reuse {
|
||||
// FIXME: Check if the previous socket was bound with SO_REUSEADDR.
|
||||
*used_times += 1;
|
||||
} else {
|
||||
return_errno_with_message!(Errno::EADDRINUSE, "the address is already in use");
|
||||
}
|
||||
} else {
|
||||
used_ports.insert(port, 1);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Release port number so the port can be used again. For reused port, the port may still be in use.
|
||||
pub(super) fn release_port(&self, port: u16) {
|
||||
let mut used_ports = self.used_ports.write();
|
||||
if let Some(used_times) = used_ports.remove(&port) {
|
||||
if used_times != 1 {
|
||||
used_ports.insert(port, used_times - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn bind_socket(
|
||||
&self,
|
||||
iface: Arc<dyn Iface<E>>,
|
||||
socket: Box<AnyUnboundSocket>,
|
||||
config: BindPortConfig,
|
||||
) -> core::result::Result<AnyBoundSocket<E>, (Error, Box<AnyUnboundSocket>)> {
|
||||
let port = if let Some(port) = config.port() {
|
||||
port
|
||||
} else {
|
||||
match self.alloc_ephemeral_port() {
|
||||
Ok(port) => port,
|
||||
Err(err) => return Err((err, socket)),
|
||||
}
|
||||
};
|
||||
if let Some(err) = self.bind_port(port, config.can_reuse()).err() {
|
||||
return Err((err, socket));
|
||||
}
|
||||
|
||||
let (handle, socket_family, observer) = match socket.into_raw() {
|
||||
(AnyRawSocket::Tcp(tcp_socket), observer) => (
|
||||
self.sockets.disable_irq().lock().add(tcp_socket),
|
||||
SocketFamily::Tcp,
|
||||
observer,
|
||||
),
|
||||
(AnyRawSocket::Udp(udp_socket), observer) => (
|
||||
self.sockets.disable_irq().lock().add(udp_socket),
|
||||
SocketFamily::Udp,
|
||||
observer,
|
||||
),
|
||||
};
|
||||
let bound_socket = AnyBoundSocket::new(iface, handle, port, socket_family, observer);
|
||||
self.insert_bound_socket(bound_socket.inner());
|
||||
|
||||
Ok(bound_socket)
|
||||
}
|
||||
|
||||
/// Remove a socket from the interface
|
||||
pub(super) fn remove_socket(&self, handle: SocketHandle) {
|
||||
self.sockets.disable_irq().lock().remove(handle);
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub(super) fn poll<D: Device + ?Sized>(&self, device: &mut D) -> Option<u64> {
|
||||
let mut sockets = self.sockets.disable_irq().lock();
|
||||
let mut interface = self.interface.disable_irq().lock();
|
||||
|
||||
let timestamp = get_network_timestamp();
|
||||
let (has_events, poll_at) = {
|
||||
let mut has_events = false;
|
||||
let mut poll_at;
|
||||
|
||||
loop {
|
||||
// `poll` transmits and receives a bounded number of packets. This loop ensures
|
||||
// that all packets are transmitted and received. For details, see
|
||||
// <https://github.com/smoltcp-rs/smoltcp/blob/8e3ea5c7f09a76f0a4988fda20cadc74eacdc0d8/src/iface/interface/mod.rs#L400-L405>.
|
||||
while interface.poll(timestamp, device, &mut sockets) {
|
||||
has_events = true;
|
||||
}
|
||||
|
||||
// `poll_at` can return `Some(Instant::from_millis(0))`, which means `PollAt::Now`.
|
||||
// For details, see
|
||||
// <https://github.com/smoltcp-rs/smoltcp/blob/8e3ea5c7f09a76f0a4988fda20cadc74eacdc0d8/src/iface/interface/mod.rs#L478>.
|
||||
poll_at = interface.poll_at(timestamp, &sockets);
|
||||
let Some(instant) = poll_at else {
|
||||
break;
|
||||
};
|
||||
if instant > timestamp {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
(has_events, poll_at)
|
||||
};
|
||||
|
||||
// drop sockets here to avoid deadlock
|
||||
drop(interface);
|
||||
drop(sockets);
|
||||
|
||||
if has_events {
|
||||
// We never try to hold the write lock in the IRQ context, and we disable IRQ when
|
||||
// holding the write lock. So we don't need to disable IRQ when holding the read lock.
|
||||
self.bound_sockets.read().iter().for_each(|bound_socket| {
|
||||
bound_socket.on_iface_events();
|
||||
});
|
||||
|
||||
let closed_sockets = self
|
||||
.closing_sockets
|
||||
.disable_irq()
|
||||
.lock()
|
||||
.extract_if(|closing_socket| closing_socket.is_closed())
|
||||
.collect::<Vec<_>>();
|
||||
drop(closed_sockets);
|
||||
}
|
||||
|
||||
poll_at.map(|at| smoltcp::time::Instant::total_millis(&at) as u64)
|
||||
}
|
||||
|
||||
pub(super) fn ext(&self) -> &E {
|
||||
&self.ext
|
||||
}
|
||||
|
||||
fn insert_bound_socket(&self, socket: &Arc<AnyBoundSocketInner<E>>) {
|
||||
let keyable_socket = KeyableArc::from(socket.clone());
|
||||
|
||||
let inserted = self
|
||||
.bound_sockets
|
||||
.write_irq_disabled()
|
||||
.insert(keyable_socket);
|
||||
assert!(inserted);
|
||||
}
|
||||
|
||||
pub(super) fn remove_bound_socket_now(&self, socket: &Arc<AnyBoundSocketInner<E>>) {
|
||||
let keyable_socket = KeyableArc::from(socket.clone());
|
||||
|
||||
let removed = self
|
||||
.bound_sockets
|
||||
.write_irq_disabled()
|
||||
.remove(&keyable_socket);
|
||||
assert!(removed);
|
||||
}
|
||||
|
||||
pub(super) fn remove_bound_socket_when_closed(&self, socket: &Arc<AnyBoundSocketInner<E>>) {
|
||||
let keyable_socket = KeyableArc::from(socket.clone());
|
||||
|
||||
let removed = self
|
||||
.bound_sockets
|
||||
.write_irq_disabled()
|
||||
.remove(&keyable_socket);
|
||||
assert!(removed);
|
||||
|
||||
let mut closing_sockets = self.closing_sockets.disable_irq().lock();
|
||||
|
||||
// Check `is_closed` after holding the lock to avoid race conditions.
|
||||
if keyable_socket.is_closed() {
|
||||
return;
|
||||
}
|
||||
|
||||
let inserted = closing_sockets.insert(keyable_socket);
|
||||
assert!(inserted);
|
||||
}
|
||||
}
|
||||
|
||||
const IP_LOCAL_PORT_START: u16 = 49152;
|
||||
const IP_LOCAL_PORT_END: u16 = 65535;
|
@ -1,18 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use smoltcp::phy::Device;
|
||||
|
||||
/// A trait that allows to obtain a mutable reference of [`Device`].
|
||||
///
|
||||
/// A [`Device`] is usually protected by a lock (e.g., a spin lock or a mutex), and it may be
|
||||
/// stored behind a shared type (e.g., an `Arc`). This property abstracts this fact by providing a
|
||||
/// method that the caller can use to get the mutable reference without worrying about how the
|
||||
/// reference is obtained.
|
||||
pub trait WithDevice: Send + Sync {
|
||||
type Device: Device + ?Sized;
|
||||
|
||||
/// Calls the closure with a mutable reference of [`Device`].
|
||||
fn with<F, R>(&self, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut Self::Device) -> R;
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
pub use smoltcp::wire::EthernetAddress;
|
||||
use smoltcp::{
|
||||
iface::{Config, SocketHandle, SocketSet},
|
||||
socket::dhcpv4,
|
||||
wire::{self, IpCidr},
|
||||
};
|
||||
|
||||
use super::{
|
||||
common::IfaceCommon, device::WithDevice, internal::IfaceInternal, time::get_network_timestamp,
|
||||
Iface,
|
||||
};
|
||||
use crate::prelude::*;
|
||||
|
||||
pub struct EtherIface<D: WithDevice, E> {
|
||||
driver: D,
|
||||
common: IfaceCommon<E>,
|
||||
dhcp_handle: SocketHandle,
|
||||
}
|
||||
|
||||
impl<D: WithDevice, E> EtherIface<D, E> {
|
||||
pub fn new(driver: D, ether_addr: EthernetAddress, ext: E) -> Arc<Self> {
|
||||
let interface = driver.with(|device| {
|
||||
let ip_addr = IpCidr::new(wire::IpAddress::Ipv4(wire::Ipv4Address::UNSPECIFIED), 0);
|
||||
let config = Config::new(wire::HardwareAddress::Ethernet(ether_addr));
|
||||
let now = get_network_timestamp();
|
||||
|
||||
let mut interface = smoltcp::iface::Interface::new(config, device, now);
|
||||
interface.update_ip_addrs(|ip_addrs| {
|
||||
debug_assert!(ip_addrs.is_empty());
|
||||
ip_addrs.push(ip_addr).unwrap();
|
||||
});
|
||||
interface
|
||||
});
|
||||
|
||||
let common = IfaceCommon::new(interface, ext);
|
||||
|
||||
let mut socket_set = common.sockets();
|
||||
let dhcp_handle = init_dhcp_client(&mut socket_set);
|
||||
drop(socket_set);
|
||||
|
||||
Arc::new(Self {
|
||||
driver,
|
||||
common,
|
||||
dhcp_handle,
|
||||
})
|
||||
}
|
||||
|
||||
/// FIXME: Once we have user program dhcp client, we may remove dhcp logic from kernel.
|
||||
pub fn process_dhcp(&self) {
|
||||
let mut socket_set = self.common.sockets();
|
||||
let dhcp_socket: &mut dhcpv4::Socket = socket_set.get_mut(self.dhcp_handle);
|
||||
let config = if let Some(event) = dhcp_socket.poll() {
|
||||
debug!("event = {:?}", event);
|
||||
if let dhcpv4::Event::Configured(config) = event {
|
||||
config
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
let ip_addr = IpCidr::Ipv4(config.address);
|
||||
let mut interface = self.common.interface();
|
||||
interface.update_ip_addrs(|ipaddrs| {
|
||||
if let Some(addr) = ipaddrs.iter_mut().next() {
|
||||
// already has ipaddrs
|
||||
*addr = ip_addr
|
||||
} else {
|
||||
// does not has ip addr
|
||||
ipaddrs.push(ip_addr).unwrap();
|
||||
}
|
||||
});
|
||||
println!(
|
||||
"DHCP update IP address: {:?}",
|
||||
interface.ipv4_addr().unwrap()
|
||||
);
|
||||
if let Some(router) = config.router {
|
||||
println!("Default router address: {:?}", router);
|
||||
interface
|
||||
.routes_mut()
|
||||
.add_default_ipv4_route(router)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: WithDevice, E> IfaceInternal<E> for EtherIface<D, E> {
|
||||
fn common(&self) -> &IfaceCommon<E> {
|
||||
&self.common
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: WithDevice, E: Send + Sync> Iface<E> for EtherIface<D, E> {
|
||||
fn raw_poll(&self, schedule_next_poll: &dyn Fn(Option<u64>)) {
|
||||
self.driver.with(|device| {
|
||||
let next_poll = self.common.poll(&mut *device);
|
||||
schedule_next_poll(next_poll);
|
||||
|
||||
self.process_dhcp();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Register a dhcp socket.
|
||||
fn init_dhcp_client(socket_set: &mut SocketSet) -> SocketHandle {
|
||||
let dhcp_socket = dhcpv4::Socket::new();
|
||||
socket_set.add(dhcp_socket)
|
||||
}
|
@ -67,7 +67,7 @@ pub trait IfaceEx {
|
||||
fn poll(&self);
|
||||
}
|
||||
|
||||
impl IfaceEx for dyn Iface<IfaceExt> {
|
||||
impl IfaceEx for Iface {
|
||||
fn name(&self) -> &str {
|
||||
&self.ext().name
|
||||
}
|
||||
|
@ -2,19 +2,17 @@
|
||||
|
||||
use alloc::{borrow::ToOwned, sync::Arc};
|
||||
|
||||
use aster_bigtcp::device::WithDevice;
|
||||
use ostd::sync::PreemptDisabled;
|
||||
use spin::Once;
|
||||
|
||||
use super::{spawn_background_poll_thread, Iface};
|
||||
use super::{poll_ifaces, Iface};
|
||||
use crate::{
|
||||
net::iface::{
|
||||
device::WithDevice,
|
||||
ext::{IfaceEx, IfaceExt},
|
||||
},
|
||||
net::iface::ext::{IfaceEx, IfaceExt},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
pub static IFACES: Once<Vec<Arc<dyn Iface>>> = Once::new();
|
||||
pub static IFACES: Once<Vec<Arc<Iface>>> = Once::new();
|
||||
|
||||
pub fn init() {
|
||||
IFACES.call_once(|| {
|
||||
@ -34,12 +32,11 @@ pub fn init() {
|
||||
poll_ifaces();
|
||||
}
|
||||
|
||||
fn new_virtio() -> Arc<dyn Iface> {
|
||||
fn new_virtio() -> Arc<Iface> {
|
||||
use aster_bigtcp::{iface::EtherIface, wire::EthernetAddress};
|
||||
use aster_network::AnyNetworkDevice;
|
||||
use aster_virtio::device::network::DEVICE_NAME;
|
||||
|
||||
use super::ether::{EtherIface, EthernetAddress};
|
||||
|
||||
let virtio_net = aster_network::get_device(DEVICE_NAME).unwrap();
|
||||
|
||||
let ether_addr = virtio_net.disable_irq().lock().mac_addr().0;
|
||||
@ -65,10 +62,12 @@ fn new_virtio() -> Arc<dyn Iface> {
|
||||
)
|
||||
}
|
||||
|
||||
fn new_loopback() -> Arc<dyn Iface> {
|
||||
use smoltcp::phy::{Loopback, Medium};
|
||||
|
||||
use super::ip::{IpAddress, IpCidr, IpIface, Ipv4Address};
|
||||
fn new_loopback() -> Arc<Iface> {
|
||||
use aster_bigtcp::{
|
||||
device::{Loopback, Medium},
|
||||
iface::IpIface,
|
||||
wire::{IpAddress, IpCidr, Ipv4Address},
|
||||
};
|
||||
|
||||
const LOOPBACK_ADDRESS: IpAddress = {
|
||||
let ipv4_addr = Ipv4Address::new(127, 0, 0, 1);
|
||||
@ -96,17 +95,3 @@ fn new_loopback() -> Arc<dyn Iface> {
|
||||
IfaceExt::new("lo".to_owned()),
|
||||
) as _
|
||||
}
|
||||
|
||||
pub fn lazy_init() {
|
||||
for iface in IFACES.get().unwrap() {
|
||||
spawn_background_poll_thread(iface.clone());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poll_ifaces() {
|
||||
let ifaces = IFACES.get().unwrap();
|
||||
|
||||
for iface in ifaces.iter() {
|
||||
iface.poll();
|
||||
}
|
||||
}
|
||||
|
@ -1,47 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use smoltcp::iface::Config;
|
||||
pub use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address};
|
||||
|
||||
use super::{common::IfaceCommon, device::WithDevice, internal::IfaceInternal, Iface};
|
||||
use crate::{net::iface::time::get_network_timestamp, prelude::*};
|
||||
|
||||
pub struct IpIface<D: WithDevice, E> {
|
||||
driver: D,
|
||||
common: IfaceCommon<E>,
|
||||
}
|
||||
|
||||
impl<D: WithDevice, E> IpIface<D, E> {
|
||||
pub fn new(driver: D, ip_cidr: IpCidr, ext: E) -> Arc<Self> {
|
||||
let interface = driver.with(|device| {
|
||||
let config = Config::new(smoltcp::wire::HardwareAddress::Ip);
|
||||
let now = get_network_timestamp();
|
||||
|
||||
let mut interface = smoltcp::iface::Interface::new(config, device, now);
|
||||
interface.update_ip_addrs(|ip_addrs| {
|
||||
debug_assert!(ip_addrs.is_empty());
|
||||
ip_addrs.push(ip_cidr).unwrap();
|
||||
});
|
||||
interface
|
||||
});
|
||||
|
||||
let common = IfaceCommon::new(interface, ext);
|
||||
|
||||
Arc::new(Self { driver, common })
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: WithDevice, E> IfaceInternal<E> for IpIface<D, E> {
|
||||
fn common(&self) -> &IfaceCommon<E> {
|
||||
&self.common
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: WithDevice, E: Send + Sync> Iface<E> for IpIface<D, E> {
|
||||
fn raw_poll(&self, schedule_next_poll: &dyn Fn(Option<u64>)) {
|
||||
self.driver.with(|device| {
|
||||
let next_poll = self.common.poll(device);
|
||||
schedule_next_poll(next_poll);
|
||||
});
|
||||
}
|
||||
}
|
@ -1,82 +1,11 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use self::common::IfaceCommon;
|
||||
use crate::prelude::*;
|
||||
|
||||
mod any_socket;
|
||||
mod common;
|
||||
mod device;
|
||||
mod ether;
|
||||
mod ext;
|
||||
mod init;
|
||||
mod ip;
|
||||
mod time;
|
||||
mod util;
|
||||
mod poll;
|
||||
|
||||
pub use any_socket::{
|
||||
AnyBoundSocket, AnyUnboundSocket, RawTcpSocket, RawUdpSocket, TCP_RECV_BUF_LEN,
|
||||
TCP_SEND_BUF_LEN, UDP_RECV_PAYLOAD_LEN, UDP_SEND_PAYLOAD_LEN,
|
||||
};
|
||||
pub use init::{init, lazy_init, poll_ifaces, IFACES};
|
||||
pub use smoltcp::wire::EthernetAddress;
|
||||
pub use util::{spawn_background_poll_thread, BindPortConfig};
|
||||
pub use init::{init, IFACES};
|
||||
pub use poll::{lazy_init, poll_ifaces};
|
||||
|
||||
use crate::net::socket::ip::Ipv4Address;
|
||||
|
||||
/// A network interface.
|
||||
///
|
||||
/// A network interface (abbreviated as iface) is a hardware or software component that connects a
|
||||
/// computer to a network. Network interfaces can be physical components like Ethernet ports or
|
||||
/// wireless adapters. They can also be virtual interfaces created by software, such as virtual
|
||||
/// private network (VPN) connections.
|
||||
pub trait Iface<E = ext::IfaceExt>: 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>));
|
||||
}
|
||||
|
||||
impl<E> dyn Iface<E> {
|
||||
/// Gets the extension of the iface.
|
||||
pub fn ext(&self) -> &E {
|
||||
self.common().ext()
|
||||
}
|
||||
|
||||
/// Binds a socket to the iface.
|
||||
///
|
||||
/// After binding the socket to the iface, the iface will handle all packets to and from the
|
||||
/// socket.
|
||||
///
|
||||
/// If [`BindPortConfig::Ephemeral`] is specified, the iface will pick up an ephemeral port for
|
||||
/// the socket.
|
||||
///
|
||||
/// FIXME: The reason for binding the socket and the iface together is because there are
|
||||
/// limitations inside smoltcp. See discussion at
|
||||
/// <https://github.com/smoltcp-rs/smoltcp/issues/779>.
|
||||
pub fn bind_socket(
|
||||
self: &Arc<Self>,
|
||||
socket: Box<AnyUnboundSocket>,
|
||||
config: BindPortConfig,
|
||||
) -> core::result::Result<AnyBoundSocket<E>, (Error, Box<AnyUnboundSocket>)> {
|
||||
let common = self.common();
|
||||
common.bind_socket(self.clone(), socket, config)
|
||||
}
|
||||
|
||||
/// 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()
|
||||
}
|
||||
}
|
||||
|
||||
mod internal {
|
||||
use super::*;
|
||||
|
||||
/// An internal trait that abstracts the common part of different ifaces.
|
||||
pub trait IfaceInternal<E> {
|
||||
fn common(&self) -> &IfaceCommon<E>;
|
||||
}
|
||||
}
|
||||
pub type Iface = dyn aster_bigtcp::iface::Iface<ext::IfaceExt>;
|
||||
pub type AnyBoundSocket = aster_bigtcp::socket::AnyBoundSocket<ext::IfaceExt>;
|
||||
|
@ -1,53 +1,35 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use core::time::Duration;
|
||||
|
||||
use log::trace;
|
||||
use ostd::{arch::timer::Jiffies, task::Priority};
|
||||
|
||||
use super::{ext::IfaceEx, Iface};
|
||||
use super::{ext::IfaceEx, Iface, IFACES};
|
||||
use crate::{
|
||||
prelude::*,
|
||||
thread::{
|
||||
kernel_thread::{KernelThreadExt, ThreadOptions},
|
||||
Thread,
|
||||
},
|
||||
WaitTimeout,
|
||||
};
|
||||
|
||||
pub enum BindPortConfig {
|
||||
CanReuse(u16),
|
||||
Specified(u16),
|
||||
Ephemeral,
|
||||
}
|
||||
|
||||
impl BindPortConfig {
|
||||
pub fn new(port: u16, can_reuse: bool) -> Result<Self> {
|
||||
let config = if port != 0 {
|
||||
if can_reuse {
|
||||
Self::CanReuse(port)
|
||||
} else {
|
||||
Self::Specified(port)
|
||||
}
|
||||
} else if can_reuse {
|
||||
return_errno_with_message!(Errno::EINVAL, "invalid bind port config");
|
||||
} else {
|
||||
Self::Ephemeral
|
||||
};
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
pub(super) fn can_reuse(&self) -> bool {
|
||||
matches!(self, Self::CanReuse(_))
|
||||
}
|
||||
|
||||
pub(super) fn port(&self) -> Option<u16> {
|
||||
match self {
|
||||
Self::CanReuse(port) | Self::Specified(port) => Some(*port),
|
||||
Self::Ephemeral => None,
|
||||
}
|
||||
pub fn lazy_init() {
|
||||
for iface in IFACES.get().unwrap() {
|
||||
spawn_background_poll_thread(iface.clone());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spawn_background_poll_thread(iface: Arc<dyn Iface>) {
|
||||
pub fn poll_ifaces() {
|
||||
let ifaces = IFACES.get().unwrap();
|
||||
|
||||
for iface in ifaces.iter() {
|
||||
iface.poll();
|
||||
}
|
||||
}
|
||||
|
||||
fn spawn_background_poll_thread(iface: Arc<Iface>) {
|
||||
let task_fn = move || {
|
||||
trace!("spawn background poll thread for {}", iface.name());
|
||||
|
@ -1,8 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use ostd::arch::timer::Jiffies;
|
||||
|
||||
pub(super) fn get_network_timestamp() -> smoltcp::time::Instant {
|
||||
let millis = Jiffies::elapsed().as_duration().as_millis();
|
||||
smoltcp::time::Instant::from_millis(millis as i64)
|
||||
}
|
Reference in New Issue
Block a user