mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-08 21:06:48 +00:00
Move smoltcp-related code to bigtcp
This commit is contained in:
parent
9fba9445bd
commit
67d3682116
11
Cargo.lock
generated
11
Cargo.lock
generated
@ -66,6 +66,15 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16"
|
||||
|
||||
[[package]]
|
||||
name = "aster-bigtcp"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"keyable-arc",
|
||||
"ostd",
|
||||
"smoltcp",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aster-block"
|
||||
version = "0.1.0"
|
||||
@ -144,6 +153,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"align_ext",
|
||||
"ascii",
|
||||
"aster-bigtcp",
|
||||
"aster-block",
|
||||
"aster-console",
|
||||
"aster-framebuffer",
|
||||
@ -179,7 +189,6 @@ dependencies = [
|
||||
"ostd",
|
||||
"paste",
|
||||
"rand",
|
||||
"smoltcp",
|
||||
"spin 0.9.8",
|
||||
"static_assertions",
|
||||
"takeable",
|
||||
|
@ -24,6 +24,7 @@ members = [
|
||||
"kernel/libs/aster-rights",
|
||||
"kernel/libs/aster-rights-proc",
|
||||
"kernel/libs/aster-util",
|
||||
"kernel/libs/aster-bigtcp",
|
||||
"kernel/libs/keyable-arc",
|
||||
"kernel/libs/typeflags",
|
||||
"kernel/libs/typeflags-util",
|
||||
|
3
Makefile
3
Makefile
@ -126,7 +126,8 @@ OSDK_CRATES := \
|
||||
kernel/comps/network \
|
||||
kernel/comps/time \
|
||||
kernel/comps/virtio \
|
||||
kernel/libs/aster-util
|
||||
kernel/libs/aster-util \
|
||||
kernel/libs/aster-bigtcp
|
||||
|
||||
.PHONY: all
|
||||
all: build
|
||||
|
@ -22,6 +22,7 @@ typeflags = { path = "libs/typeflags" }
|
||||
typeflags-util = { path = "libs/typeflags-util" }
|
||||
aster-rights-proc = { path = "libs/aster-rights-proc" }
|
||||
aster-util = { path = "libs/aster-util" }
|
||||
aster-bigtcp = { path = "libs/aster-bigtcp" }
|
||||
id-alloc = { path = "../ostd/libs/id-alloc" }
|
||||
int-to-c-enum = { path = "libs/int-to-c-enum" }
|
||||
cpio-decoder = { path = "libs/cpio-decoder" }
|
||||
@ -29,20 +30,6 @@ ascii = { version = "1.1", default-features = false, features = ["alloc"] }
|
||||
intrusive-collections = "0.9.5"
|
||||
paste = "1.0"
|
||||
time = { version = "0.3", default-features = false, features = ["alloc"] }
|
||||
smoltcp = { git = "https://github.com/smoltcp-rs/smoltcp", rev = "dc08e0b", default-features = false, features = [
|
||||
"alloc",
|
||||
"log",
|
||||
"medium-ethernet",
|
||||
"medium-ip",
|
||||
"proto-dhcpv4",
|
||||
"proto-ipv4",
|
||||
"proto-igmp",
|
||||
"socket-icmp",
|
||||
"socket-udp",
|
||||
"socket-tcp",
|
||||
"socket-raw",
|
||||
"socket-dhcpv4",
|
||||
] }
|
||||
tdx-guest = { version = "0.1.7", optional = true }
|
||||
|
||||
# parse elf file
|
||||
|
24
kernel/libs/aster-bigtcp/Cargo.toml
Normal file
24
kernel/libs/aster-bigtcp/Cargo.toml
Normal file
@ -0,0 +1,24 @@
|
||||
[package]
|
||||
name = "aster-bigtcp"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
keyable-arc = { path = "../keyable-arc" }
|
||||
ostd = { path = "../../../ostd" }
|
||||
smoltcp = { git = "https://github.com/smoltcp-rs/smoltcp", rev = "dc08e0b", default-features = false, features = [
|
||||
"alloc",
|
||||
"log",
|
||||
"medium-ethernet",
|
||||
"medium-ip",
|
||||
"proto-dhcpv4",
|
||||
"proto-ipv4",
|
||||
"proto-igmp",
|
||||
"socket-icmp",
|
||||
"socket-udp",
|
||||
"socket-tcp",
|
||||
"socket-raw",
|
||||
"socket-dhcpv4",
|
||||
] }
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use smoltcp::phy::Device;
|
||||
pub use smoltcp::phy::{Device, Loopback, Medium};
|
||||
|
||||
/// A trait that allows to obtain a mutable reference of [`Device`].
|
||||
///
|
18
kernel/libs/aster-bigtcp/src/errors.rs
Normal file
18
kernel/libs/aster-bigtcp/src/errors.rs
Normal file
@ -0,0 +1,18 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
/// An error describing the reason why `bind` failed.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum BindError {
|
||||
/// All ephemeral ports is exhausted.
|
||||
Exhausted,
|
||||
/// The specified address is in use.
|
||||
InUse,
|
||||
}
|
||||
|
||||
pub mod tcp {
|
||||
pub use smoltcp::socket::tcp::{ConnectError, ListenError, RecvError, SendError};
|
||||
}
|
||||
|
||||
pub mod udp {
|
||||
pub use smoltcp::socket::udp::{RecvError, SendError};
|
||||
}
|
@ -1,24 +1,30 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use alloc::collections::btree_map::Entry;
|
||||
use alloc::{
|
||||
boxed::Box,
|
||||
collections::{
|
||||
btree_map::{BTreeMap, Entry},
|
||||
btree_set::BTreeSet,
|
||||
},
|
||||
sync::Arc,
|
||||
vec::Vec,
|
||||
};
|
||||
|
||||
use keyable_arc::KeyableArc;
|
||||
use ostd::sync::LocalIrqDisabled;
|
||||
use ostd::sync::{LocalIrqDisabled, RwLock, SpinLock, SpinLockGuard};
|
||||
use smoltcp::{
|
||||
iface::{SocketHandle, SocketSet},
|
||||
phy::Device,
|
||||
wire::Ipv4Address,
|
||||
};
|
||||
|
||||
use super::{
|
||||
any_socket::{AnyBoundSocketInner, AnyRawSocket, AnyUnboundSocket, SocketFamily},
|
||||
ext::IfaceExt,
|
||||
time::get_network_timestamp,
|
||||
util::BindPortConfig,
|
||||
AnyBoundSocket, Iface,
|
||||
use super::{port::BindPortConfig, time::get_network_timestamp, Iface};
|
||||
use crate::{
|
||||
errors::BindError,
|
||||
socket::{AnyBoundSocket, AnyBoundSocketInner, AnyRawSocket, AnyUnboundSocket, SocketFamily},
|
||||
};
|
||||
use crate::{net::socket::ip::Ipv4Address, prelude::*};
|
||||
|
||||
pub struct IfaceCommon<E = IfaceExt> {
|
||||
pub struct IfaceCommon<E> {
|
||||
interface: SpinLock<smoltcp::iface::Interface>,
|
||||
sockets: SpinLock<SocketSet<'static>>,
|
||||
used_ports: RwLock<BTreeMap<u16, usize>>,
|
||||
@ -44,14 +50,14 @@ impl<E> IfaceCommon<E> {
|
||||
/// Acquires the lock to the interface.
|
||||
///
|
||||
/// *Lock ordering:* [`Self::sockets`] first, [`Self::interface`] second.
|
||||
pub(super) fn interface(&self) -> SpinLockGuard<smoltcp::iface::Interface, LocalIrqDisabled> {
|
||||
pub(crate) 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(
|
||||
pub(crate) fn sockets(
|
||||
&self,
|
||||
) -> SpinLockGuard<smoltcp::iface::SocketSet<'static>, LocalIrqDisabled> {
|
||||
self.sockets.disable_irq().lock()
|
||||
@ -62,34 +68,35 @@ impl<E> IfaceCommon<E> {
|
||||
}
|
||||
|
||||
/// Alloc an unused port range from 49152 ~ 65535 (According to smoltcp docs)
|
||||
fn alloc_ephemeral_port(&self) -> Result<u16> {
|
||||
fn alloc_ephemeral_port(&self) -> Option<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 Some(port);
|
||||
}
|
||||
}
|
||||
return_errno_with_message!(Errno::EAGAIN, "no ephemeral port is available");
|
||||
None
|
||||
}
|
||||
|
||||
fn bind_port(&self, port: u16, can_reuse: bool) -> Result<()> {
|
||||
#[must_use]
|
||||
fn bind_port(&self, port: u16, can_reuse: bool) -> bool {
|
||||
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");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
used_ports.insert(port, 1);
|
||||
}
|
||||
Ok(())
|
||||
true
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
pub(crate) 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 {
|
||||
@ -103,17 +110,17 @@ impl<E> IfaceCommon<E> {
|
||||
iface: Arc<dyn Iface<E>>,
|
||||
socket: Box<AnyUnboundSocket>,
|
||||
config: BindPortConfig,
|
||||
) -> core::result::Result<AnyBoundSocket<E>, (Error, Box<AnyUnboundSocket>)> {
|
||||
) -> core::result::Result<AnyBoundSocket<E>, (BindError, 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)),
|
||||
Some(port) => port,
|
||||
None => return Err((BindError::Exhausted, socket)),
|
||||
}
|
||||
};
|
||||
if let Some(err) = self.bind_port(port, config.can_reuse()).err() {
|
||||
return Err((err, socket));
|
||||
if !self.bind_port(port, config.can_reuse()) {
|
||||
return Err((BindError::InUse, socket));
|
||||
}
|
||||
|
||||
let (handle, socket_family, observer) = match socket.into_raw() {
|
||||
@ -135,7 +142,7 @@ impl<E> IfaceCommon<E> {
|
||||
}
|
||||
|
||||
/// Remove a socket from the interface
|
||||
pub(super) fn remove_socket(&self, handle: SocketHandle) {
|
||||
pub(crate) fn remove_socket(&self, handle: SocketHandle) {
|
||||
self.sockets.disable_irq().lock().remove(handle);
|
||||
}
|
||||
|
||||
@ -209,7 +216,7 @@ impl<E> IfaceCommon<E> {
|
||||
assert!(inserted);
|
||||
}
|
||||
|
||||
pub(super) fn remove_bound_socket_now(&self, socket: &Arc<AnyBoundSocketInner<E>>) {
|
||||
pub(crate) fn remove_bound_socket_now(&self, socket: &Arc<AnyBoundSocketInner<E>>) {
|
||||
let keyable_socket = KeyableArc::from(socket.clone());
|
||||
|
||||
let removed = self
|
||||
@ -219,7 +226,7 @@ impl<E> IfaceCommon<E> {
|
||||
assert!(removed);
|
||||
}
|
||||
|
||||
pub(super) fn remove_bound_socket_when_closed(&self, socket: &Arc<AnyBoundSocketInner<E>>) {
|
||||
pub(crate) fn remove_bound_socket_when_closed(&self, socket: &Arc<AnyBoundSocketInner<E>>) {
|
||||
let keyable_socket = KeyableArc::from(socket.clone());
|
||||
|
||||
let removed = self
|
69
kernel/libs/aster-bigtcp/src/iface/iface.rs
Normal file
69
kernel/libs/aster-bigtcp/src/iface/iface.rs
Normal file
@ -0,0 +1,69 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use alloc::{boxed::Box, sync::Arc};
|
||||
|
||||
use smoltcp::wire::Ipv4Address;
|
||||
|
||||
use super::port::BindPortConfig;
|
||||
use crate::{
|
||||
errors::BindError,
|
||||
socket::{AnyBoundSocket, AnyUnboundSocket},
|
||||
};
|
||||
|
||||
/// 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>: 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>, (BindError, 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()
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) mod internal {
|
||||
use crate::iface::common::IfaceCommon;
|
||||
|
||||
/// An internal trait that abstracts the common part of different ifaces.
|
||||
pub trait IfaceInternal<E> {
|
||||
fn common(&self) -> &IfaceCommon<E>;
|
||||
}
|
||||
}
|
12
kernel/libs/aster-bigtcp/src/iface/mod.rs
Normal file
12
kernel/libs/aster-bigtcp/src/iface/mod.rs
Normal file
@ -0,0 +1,12 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
mod common;
|
||||
#[allow(clippy::module_inception)]
|
||||
mod iface;
|
||||
mod phy;
|
||||
mod port;
|
||||
mod time;
|
||||
|
||||
pub use iface::Iface;
|
||||
pub use phy::{EtherIface, IpIface};
|
||||
pub use port::BindPortConfig;
|
@ -1,17 +1,20 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
pub use smoltcp::wire::EthernetAddress;
|
||||
use alloc::sync::Arc;
|
||||
|
||||
use ostd::prelude::*;
|
||||
use smoltcp::{
|
||||
iface::{Config, SocketHandle, SocketSet},
|
||||
socket::dhcpv4,
|
||||
wire::{self, IpCidr},
|
||||
wire::{self, EthernetAddress, IpCidr},
|
||||
};
|
||||
|
||||
use super::{
|
||||
common::IfaceCommon, device::WithDevice, internal::IfaceInternal, time::get_network_timestamp,
|
||||
Iface,
|
||||
use crate::{
|
||||
device::WithDevice,
|
||||
iface::{
|
||||
common::IfaceCommon, iface::internal::IfaceInternal, time::get_network_timestamp, Iface,
|
||||
},
|
||||
};
|
||||
use crate::prelude::*;
|
||||
|
||||
pub struct EtherIface<D: WithDevice, E> {
|
||||
driver: D,
|
||||
@ -51,18 +54,15 @@ impl<D: WithDevice, E> EtherIface<D, E> {
|
||||
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 {
|
||||
|
||||
let Some(dhcpv4::Event::Configured(config)) = dhcp_socket.poll() else {
|
||||
return;
|
||||
};
|
||||
let ip_addr = IpCidr::Ipv4(config.address);
|
||||
|
||||
let mut interface = self.common.interface();
|
||||
|
||||
println!("[DHCP] Local IP address: {:?}", ip_addr,);
|
||||
interface.update_ip_addrs(|ipaddrs| {
|
||||
if let Some(addr) = ipaddrs.iter_mut().next() {
|
||||
// already has ipaddrs
|
||||
@ -72,12 +72,9 @@ impl<D: WithDevice, E> EtherIface<D, E> {
|
||||
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);
|
||||
println!("[DHCP] Router IP address: {:?}", router);
|
||||
interface
|
||||
.routes_mut()
|
||||
.add_default_ipv4_route(router)
|
@ -1,10 +1,15 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use smoltcp::iface::Config;
|
||||
pub use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address};
|
||||
use alloc::sync::Arc;
|
||||
|
||||
use super::{common::IfaceCommon, device::WithDevice, internal::IfaceInternal, Iface};
|
||||
use crate::{net::iface::time::get_network_timestamp, prelude::*};
|
||||
use smoltcp::{iface::Config, wire::IpCidr};
|
||||
|
||||
use crate::{
|
||||
device::WithDevice,
|
||||
iface::{
|
||||
common::IfaceCommon, iface::internal::IfaceInternal, time::get_network_timestamp, Iface,
|
||||
},
|
||||
};
|
||||
|
||||
pub struct IpIface<D: WithDevice, E> {
|
||||
driver: D,
|
7
kernel/libs/aster-bigtcp/src/iface/phy/mod.rs
Normal file
7
kernel/libs/aster-bigtcp/src/iface/phy/mod.rs
Normal file
@ -0,0 +1,7 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
mod ether;
|
||||
mod ip;
|
||||
|
||||
pub use ether::EtherIface;
|
||||
pub use ip::IpIface;
|
42
kernel/libs/aster-bigtcp/src/iface/port.rs
Normal file
42
kernel/libs/aster-bigtcp/src/iface/port.rs
Normal file
@ -0,0 +1,42 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
/// The configuration using for bind to a TCP/UDP port.
|
||||
pub enum BindPortConfig {
|
||||
/// Binds to the specified non-reusable port.
|
||||
CanReuse(u16),
|
||||
/// Binds to the specified reusable port.
|
||||
Specified(u16),
|
||||
/// Allocates an ephemeral port to bind.
|
||||
Ephemeral,
|
||||
}
|
||||
|
||||
impl BindPortConfig {
|
||||
/// Creates new configuration using for bind to a TCP/UDP port.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This method will panic if `port` is zero (indicating that an ephemeral port should be
|
||||
/// allocated) and `can_use` is true. This makes no sense because new ephemeral ports are
|
||||
/// always not reused.
|
||||
pub fn new(port: u16, can_reuse: bool) -> Self {
|
||||
match (port, can_reuse) {
|
||||
(0, _) => {
|
||||
assert!(!can_reuse);
|
||||
Self::Ephemeral
|
||||
}
|
||||
(_, true) => Self::CanReuse(port),
|
||||
(_, false) => Self::Specified(port),
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
23
kernel/libs/aster-bigtcp/src/lib.rs
Normal file
23
kernel/libs/aster-bigtcp/src/lib.rs
Normal file
@ -0,0 +1,23 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! _bigtcp_ is a crate that wraps [`smoltcp`].
|
||||
//!
|
||||
//! [`smoltcp`] is designed for embedded systems where the number of sockets is always small. It
|
||||
//! turns out that such a design cannot satisfy the need to implement the network stack of a
|
||||
//! general-purpose OS kernel, in terms of flexibility and efficiency.
|
||||
//!
|
||||
//! The short-term goal of _bigtcp_ is to reuse the powerful TCP implementation of _smoltcp_, while
|
||||
//! reimplementing Ethernet and IP protocols to increase the flexibility and performance of packet
|
||||
//! dispatching.
|
||||
|
||||
#![no_std]
|
||||
#![deny(unsafe_code)]
|
||||
#![feature(btree_extract_if)]
|
||||
|
||||
pub mod device;
|
||||
pub mod errors;
|
||||
pub mod iface;
|
||||
pub mod socket;
|
||||
pub mod wire;
|
||||
|
||||
extern crate alloc;
|
@ -1,77 +1,30 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use super::{ext::IfaceExt, Iface};
|
||||
use crate::{
|
||||
events::Observer,
|
||||
net::socket::ip::{IpAddress, IpEndpoint},
|
||||
prelude::*,
|
||||
use alloc::sync::{Arc, Weak};
|
||||
|
||||
use ostd::sync::RwLock;
|
||||
use smoltcp::{
|
||||
socket::tcp::ConnectError,
|
||||
wire::{IpAddress, IpEndpoint},
|
||||
};
|
||||
|
||||
pub type RawTcpSocket = smoltcp::socket::tcp::Socket<'static>;
|
||||
pub type RawUdpSocket = smoltcp::socket::udp::Socket<'static>;
|
||||
use super::{event::SocketEventObserver, RawTcpSocket, RawUdpSocket};
|
||||
use crate::iface::Iface;
|
||||
|
||||
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 {
|
||||
pub(crate) 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>>);
|
||||
pub struct AnyBoundSocket<E>(Arc<AnyBoundSocketInner<E>>);
|
||||
|
||||
impl<E> AnyBoundSocket<E> {
|
||||
pub(super) fn new(
|
||||
pub(crate) fn new(
|
||||
iface: Arc<dyn Iface<E>>,
|
||||
handle: smoltcp::iface::SocketHandle,
|
||||
port: u16,
|
||||
socket_family: SocketFamily,
|
||||
observer: Weak<dyn Observer<()>>,
|
||||
observer: Weak<dyn SocketEventObserver>,
|
||||
) -> Self {
|
||||
Self(Arc::new(AnyBoundSocketInner {
|
||||
iface,
|
||||
@ -82,18 +35,18 @@ impl<E> AnyBoundSocket<E> {
|
||||
}))
|
||||
}
|
||||
|
||||
pub(super) fn inner(&self) -> &Arc<AnyBoundSocketInner<E>> {
|
||||
pub(crate) fn inner(&self) -> &Arc<AnyBoundSocketInner<E>> {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Set the observer whose `on_events` will be called when certain iface events happen. After
|
||||
/// 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.
|
||||
///
|
||||
/// 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;
|
||||
pub fn set_observer(&self, new_observer: Weak<dyn SocketEventObserver>) {
|
||||
*self.0.observer.write() = new_observer;
|
||||
|
||||
self.0.on_iface_events();
|
||||
}
|
||||
@ -118,7 +71,7 @@ impl<E> AnyBoundSocket<E> {
|
||||
/// # Panics
|
||||
///
|
||||
/// This method will panic if the socket is not a TCP socket.
|
||||
pub fn do_connect(&self, remote_endpoint: IpEndpoint) -> Result<()> {
|
||||
pub fn do_connect(&self, remote_endpoint: IpEndpoint) -> Result<(), ConnectError> {
|
||||
let common = self.iface().common();
|
||||
|
||||
let mut sockets = common.sockets();
|
||||
@ -127,21 +80,7 @@ impl<E> AnyBoundSocket<E> {
|
||||
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(())
|
||||
socket.connect(cx, remote_endpoint, self.0.port)
|
||||
}
|
||||
|
||||
pub fn iface(&self) -> &Arc<dyn Iface<E>> {
|
||||
@ -162,22 +101,22 @@ impl<E> Drop for AnyBoundSocket<E> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct AnyBoundSocketInner<E> {
|
||||
pub(crate) struct AnyBoundSocketInner<E> {
|
||||
iface: Arc<dyn Iface<E>>,
|
||||
handle: smoltcp::iface::SocketHandle,
|
||||
port: u16,
|
||||
socket_family: SocketFamily,
|
||||
observer: RwLock<Weak<dyn Observer<()>>>,
|
||||
observer: RwLock<Weak<dyn SocketEventObserver>>,
|
||||
}
|
||||
|
||||
impl<E> AnyBoundSocketInner<E> {
|
||||
pub(super) fn on_iface_events(&self) {
|
||||
pub(crate) fn on_iface_events(&self) {
|
||||
if let Some(observer) = Weak::upgrade(&*self.observer.read()) {
|
||||
observer.on_events(&())
|
||||
observer.on_events();
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn is_closed(&self) -> bool {
|
||||
pub(crate) fn is_closed(&self) -> bool {
|
||||
match self.socket_family {
|
||||
SocketFamily::Tcp => self.raw_with(|socket: &mut RawTcpSocket| {
|
||||
socket.state() == smoltcp::socket::tcp::State::Closed
|
||||
@ -225,12 +164,3 @@ impl<E> Drop for AnyBoundSocketInner<E> {
|
||||
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;
|
11
kernel/libs/aster-bigtcp/src/socket/event.rs
Normal file
11
kernel/libs/aster-bigtcp/src/socket/event.rs
Normal file
@ -0,0 +1,11 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
/// A observer that will be invoked whenever events occur on the socket.
|
||||
pub trait SocketEventObserver: Send + Sync {
|
||||
/// Notifies that events occurred on the socket.
|
||||
fn on_events(&self);
|
||||
}
|
||||
|
||||
impl SocketEventObserver for () {
|
||||
fn on_events(&self) {}
|
||||
}
|
17
kernel/libs/aster-bigtcp/src/socket/mod.rs
Normal file
17
kernel/libs/aster-bigtcp/src/socket/mod.rs
Normal file
@ -0,0 +1,17 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
mod bound;
|
||||
mod event;
|
||||
mod unbound;
|
||||
|
||||
pub use bound::AnyBoundSocket;
|
||||
pub(crate) use bound::{AnyBoundSocketInner, SocketFamily};
|
||||
pub use event::SocketEventObserver;
|
||||
pub(crate) use unbound::AnyRawSocket;
|
||||
pub use unbound::{
|
||||
AnyUnboundSocket, TCP_RECV_BUF_LEN, TCP_SEND_BUF_LEN, UDP_RECV_PAYLOAD_LEN,
|
||||
UDP_SEND_PAYLOAD_LEN,
|
||||
};
|
||||
|
||||
pub type RawTcpSocket = smoltcp::socket::tcp::Socket<'static>;
|
||||
pub type RawUdpSocket = smoltcp::socket::udp::Socket<'static>;
|
62
kernel/libs/aster-bigtcp/src/socket/unbound.rs
Normal file
62
kernel/libs/aster-bigtcp/src/socket/unbound.rs
Normal file
@ -0,0 +1,62 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use alloc::{sync::Weak, vec};
|
||||
|
||||
use super::{event::SocketEventObserver, RawTcpSocket, RawUdpSocket};
|
||||
|
||||
pub struct AnyUnboundSocket {
|
||||
socket_family: AnyRawSocket,
|
||||
observer: Weak<dyn SocketEventObserver>,
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub(crate) enum AnyRawSocket {
|
||||
Tcp(RawTcpSocket),
|
||||
Udp(RawUdpSocket),
|
||||
}
|
||||
|
||||
impl AnyUnboundSocket {
|
||||
pub fn new_tcp(observer: Weak<dyn SocketEventObserver>) -> 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 SocketEventObserver>) -> 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(crate) fn into_raw(self) -> (AnyRawSocket, Weak<dyn SocketEventObserver>) {
|
||||
(self.socket_family, self.observer)
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
5
kernel/libs/aster-bigtcp/src/wire.rs
Normal file
5
kernel/libs/aster-bigtcp/src/wire.rs
Normal file
@ -0,0 +1,5 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
pub use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, IpEndpoint, Ipv4Address, Ipv4Cidr};
|
||||
|
||||
pub type PortNum = u16;
|
@ -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,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,11 +1,9 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
pub use smoltcp::wire::{IpAddress, IpEndpoint, Ipv4Address};
|
||||
use aster_bigtcp::wire::{IpAddress, IpEndpoint, Ipv4Address};
|
||||
|
||||
use crate::{net::socket::SocketAddr, prelude::*, return_errno_with_message};
|
||||
|
||||
pub type PortNum = u16;
|
||||
|
||||
impl TryFrom<SocketAddr> for IpEndpoint {
|
||||
type Error = Error;
|
||||
|
||||
@ -29,3 +27,11 @@ impl From<IpEndpoint> for SocketAddr {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A local endpoint, which indicates that the local endpoint is unspecified.
|
||||
///
|
||||
/// According to the Linux man pages and the Linux implementation, `getsockname()` will _not_ fail
|
||||
/// even if the socket is unbound. Instead, it will return an unspecified socket address. This
|
||||
/// unspecified endpoint helps with that.
|
||||
pub(super) const UNSPECIFIED_LOCAL_ENDPOINT: IpEndpoint =
|
||||
IpEndpoint::new(IpAddress::Ipv4(Ipv4Address::UNSPECIFIED), 0);
|
||||
|
@ -1,12 +1,18 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use super::{IpAddress, IpEndpoint};
|
||||
use aster_bigtcp::{
|
||||
errors::BindError,
|
||||
iface::BindPortConfig,
|
||||
socket::AnyUnboundSocket,
|
||||
wire::{IpAddress, IpEndpoint},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
net::iface::{AnyBoundSocket, AnyUnboundSocket, BindPortConfig, Iface, IFACES},
|
||||
net::iface::{AnyBoundSocket, Iface, IFACES},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
pub fn get_iface_to_bind(ip_addr: &IpAddress) -> Option<Arc<dyn Iface>> {
|
||||
pub(super) fn get_iface_to_bind(ip_addr: &IpAddress) -> Option<Arc<Iface>> {
|
||||
let ifaces = IFACES.get().unwrap();
|
||||
let IpAddress::Ipv4(ipv4_addr) = ip_addr;
|
||||
ifaces
|
||||
@ -24,7 +30,7 @@ pub fn get_iface_to_bind(ip_addr: &IpAddress) -> Option<Arc<dyn Iface>> {
|
||||
/// Get a suitable iface to deal with sendto/connect request if the socket is not bound to an iface.
|
||||
/// If the remote address is the same as that of some iface, we will use the iface.
|
||||
/// Otherwise, we will use a default interface.
|
||||
fn get_ephemeral_iface(remote_ip_addr: &IpAddress) -> Arc<dyn Iface> {
|
||||
fn get_ephemeral_iface(remote_ip_addr: &IpAddress) -> Arc<Iface> {
|
||||
let ifaces = IFACES.get().unwrap();
|
||||
let IpAddress::Ipv4(remote_ipv4_addr) = remote_ip_addr;
|
||||
if let Some(iface) = ifaces.iter().find(|iface| {
|
||||
@ -48,18 +54,35 @@ pub(super) fn bind_socket(
|
||||
let iface = match get_iface_to_bind(&endpoint.addr) {
|
||||
Some(iface) => iface,
|
||||
None => {
|
||||
let err = Error::with_message(Errno::EADDRNOTAVAIL, "Request iface is not available");
|
||||
let err = Error::with_message(
|
||||
Errno::EADDRNOTAVAIL,
|
||||
"the address is not available from the local machine",
|
||||
);
|
||||
return Err((err, unbound_socket));
|
||||
}
|
||||
};
|
||||
let bind_port_config = match BindPortConfig::new(endpoint.port, can_reuse) {
|
||||
Ok(config) => config,
|
||||
Err(e) => return Err((e, unbound_socket)),
|
||||
};
|
||||
iface.bind_socket(unbound_socket, bind_port_config)
|
||||
|
||||
let bind_port_config = BindPortConfig::new(endpoint.port, can_reuse);
|
||||
|
||||
iface
|
||||
.bind_socket(unbound_socket, bind_port_config)
|
||||
.map_err(|(err, unbound)| (err.into(), unbound))
|
||||
}
|
||||
|
||||
pub fn get_ephemeral_endpoint(remote_endpoint: &IpEndpoint) -> IpEndpoint {
|
||||
impl From<BindError> for Error {
|
||||
fn from(value: BindError) -> Self {
|
||||
match value {
|
||||
BindError::Exhausted => {
|
||||
Error::with_message(Errno::EAGAIN, "no ephemeral port is available")
|
||||
}
|
||||
BindError::InUse => {
|
||||
Error::with_message(Errno::EADDRINUSE, "the address is already in use")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn get_ephemeral_endpoint(remote_endpoint: &IpEndpoint) -> IpEndpoint {
|
||||
let iface = get_ephemeral_iface(&remote_endpoint.addr);
|
||||
let ip_addr = iface.ipv4_addr().unwrap();
|
||||
IpEndpoint::new(IpAddress::Ipv4(ip_addr), 0)
|
||||
|
@ -1,14 +1,14 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use smoltcp::socket::udp::{RecvError, SendError};
|
||||
use aster_bigtcp::{
|
||||
errors::udp::{RecvError, SendError},
|
||||
socket::RawUdpSocket,
|
||||
wire::IpEndpoint,
|
||||
};
|
||||
|
||||
use super::IpEndpoint;
|
||||
use crate::{
|
||||
events::IoEvents,
|
||||
net::{
|
||||
iface::{AnyBoundSocket, RawUdpSocket},
|
||||
socket::util::send_recv_flags::SendRecvFlags,
|
||||
},
|
||||
net::{iface::AnyBoundSocket, socket::util::send_recv_flags::SendRecvFlags},
|
||||
prelude::*,
|
||||
process::signal::Pollee,
|
||||
};
|
||||
|
@ -2,10 +2,11 @@
|
||||
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use aster_bigtcp::{socket::SocketEventObserver, wire::IpEndpoint};
|
||||
use takeable::Takeable;
|
||||
|
||||
use self::{bound::BoundDatagram, unbound::UnboundDatagram};
|
||||
use super::{common::get_ephemeral_endpoint, IpEndpoint, UNSPECIFIED_LOCAL_ENDPOINT};
|
||||
use super::{common::get_ephemeral_endpoint, UNSPECIFIED_LOCAL_ENDPOINT};
|
||||
use crate::{
|
||||
events::{IoEvents, Observer},
|
||||
fs::{file_handle::FileLike, utils::StatusFlags},
|
||||
@ -393,8 +394,8 @@ impl Socket for DatagramSocket {
|
||||
}
|
||||
}
|
||||
|
||||
impl Observer<()> for DatagramSocket {
|
||||
fn on_events(&self, _events: &()) {
|
||||
impl SocketEventObserver for DatagramSocket {
|
||||
fn on_events(&self) {
|
||||
self.update_io_events();
|
||||
}
|
||||
}
|
||||
|
@ -2,15 +2,14 @@
|
||||
|
||||
use alloc::sync::Weak;
|
||||
|
||||
use super::{bound::BoundDatagram, IpEndpoint};
|
||||
use aster_bigtcp::{
|
||||
socket::{AnyUnboundSocket, RawUdpSocket, SocketEventObserver},
|
||||
wire::IpEndpoint,
|
||||
};
|
||||
|
||||
use super::bound::BoundDatagram;
|
||||
use crate::{
|
||||
events::{IoEvents, Observer},
|
||||
net::{
|
||||
iface::{AnyUnboundSocket, RawUdpSocket},
|
||||
socket::ip::common::bind_socket,
|
||||
},
|
||||
prelude::*,
|
||||
process::signal::Pollee,
|
||||
events::IoEvents, net::socket::ip::common::bind_socket, prelude::*, process::signal::Pollee,
|
||||
};
|
||||
|
||||
pub struct UnboundDatagram {
|
||||
@ -18,7 +17,7 @@ pub struct UnboundDatagram {
|
||||
}
|
||||
|
||||
impl UnboundDatagram {
|
||||
pub fn new(observer: Weak<dyn Observer<()>>) -> Self {
|
||||
pub fn new(observer: Weak<dyn SocketEventObserver>) -> Self {
|
||||
Self {
|
||||
unbound_socket: Box::new(AnyUnboundSocket::new_udp(observer)),
|
||||
}
|
||||
|
@ -5,14 +5,6 @@ mod common;
|
||||
mod datagram;
|
||||
pub mod stream;
|
||||
|
||||
pub use addr::{IpAddress, IpEndpoint, Ipv4Address, PortNum};
|
||||
use addr::UNSPECIFIED_LOCAL_ENDPOINT;
|
||||
pub use datagram::DatagramSocket;
|
||||
pub use stream::StreamSocket;
|
||||
|
||||
/// A local endpoint, which indicates that the local endpoint is unspecified.
|
||||
///
|
||||
/// According to the Linux man pages and the Linux implementation, `getsockname()` will _not_ fail
|
||||
/// even if the socket is unbound. Instead, it will return an unspecified socket address. This
|
||||
/// unspecified endpoint helps with that.
|
||||
const UNSPECIFIED_LOCAL_ENDPOINT: IpEndpoint =
|
||||
IpEndpoint::new(IpAddress::Ipv4(Ipv4Address::UNSPECIFIED), 0);
|
||||
|
@ -2,13 +2,16 @@
|
||||
|
||||
use alloc::sync::Weak;
|
||||
|
||||
use smoltcp::socket::tcp::{RecvError, SendError};
|
||||
use aster_bigtcp::{
|
||||
errors::tcp::{RecvError, SendError},
|
||||
socket::{RawTcpSocket, SocketEventObserver},
|
||||
wire::IpEndpoint,
|
||||
};
|
||||
|
||||
use super::IpEndpoint;
|
||||
use crate::{
|
||||
events::{IoEvents, Observer},
|
||||
events::IoEvents,
|
||||
net::{
|
||||
iface::{AnyBoundSocket, RawTcpSocket},
|
||||
iface::AnyBoundSocket,
|
||||
socket::util::{send_recv_flags::SendRecvFlags, shutdown_cmd::SockShutdownCmd},
|
||||
},
|
||||
prelude::*,
|
||||
@ -122,7 +125,7 @@ impl ConnectedStream {
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn set_observer(&self, observer: Weak<dyn Observer<()>>) {
|
||||
pub(super) fn set_observer(&self, observer: Weak<dyn SocketEventObserver>) {
|
||||
self.bound_socket.set_observer(observer)
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,9 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use super::{connected::ConnectedStream, init::InitStream, IpEndpoint};
|
||||
use crate::{
|
||||
net::iface::{AnyBoundSocket, RawTcpSocket},
|
||||
prelude::*,
|
||||
process::signal::Pollee,
|
||||
};
|
||||
use aster_bigtcp::{socket::RawTcpSocket, wire::IpEndpoint};
|
||||
|
||||
use super::{connected::ConnectedStream, init::InitStream};
|
||||
use crate::{net::iface::AnyBoundSocket, prelude::*, process::signal::Pollee};
|
||||
|
||||
pub struct ConnectingStream {
|
||||
bound_socket: AnyBoundSocket,
|
||||
@ -29,9 +27,21 @@ impl ConnectingStream {
|
||||
bound_socket: AnyBoundSocket,
|
||||
remote_endpoint: IpEndpoint,
|
||||
) -> core::result::Result<Self, (Error, AnyBoundSocket)> {
|
||||
if let Err(err) = bound_socket.do_connect(remote_endpoint) {
|
||||
return Err((err, bound_socket));
|
||||
// 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`.
|
||||
if bound_socket.do_connect(remote_endpoint).is_err() {
|
||||
return Err((
|
||||
Error::with_message(
|
||||
Errno::ECONNREFUSED,
|
||||
"connecting to an unspecified address is not supported",
|
||||
),
|
||||
bound_socket,
|
||||
));
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
bound_socket,
|
||||
remote_endpoint,
|
||||
|
@ -2,11 +2,16 @@
|
||||
|
||||
use alloc::sync::Weak;
|
||||
|
||||
use super::{connecting::ConnectingStream, listen::ListenStream, IpEndpoint};
|
||||
use aster_bigtcp::{
|
||||
socket::{AnyUnboundSocket, SocketEventObserver},
|
||||
wire::IpEndpoint,
|
||||
};
|
||||
|
||||
use super::{connecting::ConnectingStream, listen::ListenStream};
|
||||
use crate::{
|
||||
events::{IoEvents, Observer},
|
||||
events::IoEvents,
|
||||
net::{
|
||||
iface::{AnyBoundSocket, AnyUnboundSocket},
|
||||
iface::AnyBoundSocket,
|
||||
socket::ip::common::{bind_socket, get_ephemeral_endpoint},
|
||||
},
|
||||
prelude::*,
|
||||
@ -19,7 +24,7 @@ pub enum InitStream {
|
||||
}
|
||||
|
||||
impl InitStream {
|
||||
pub fn new(observer: Weak<dyn Observer<()>>) -> Self {
|
||||
pub fn new(observer: Weak<dyn SocketEventObserver>) -> Self {
|
||||
InitStream::Unbound(Box::new(AnyUnboundSocket::new_tcp(observer)))
|
||||
}
|
||||
|
||||
|
@ -1,15 +1,15 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use smoltcp::socket::tcp::ListenError;
|
||||
|
||||
use super::{connected::ConnectedStream, IpEndpoint};
|
||||
use crate::{
|
||||
events::IoEvents,
|
||||
net::iface::{AnyBoundSocket, AnyUnboundSocket, BindPortConfig, RawTcpSocket},
|
||||
prelude::*,
|
||||
process::signal::Pollee,
|
||||
use aster_bigtcp::{
|
||||
errors::tcp::ListenError,
|
||||
iface::BindPortConfig,
|
||||
socket::{AnyUnboundSocket, RawTcpSocket},
|
||||
wire::IpEndpoint,
|
||||
};
|
||||
|
||||
use super::connected::ConnectedStream;
|
||||
use crate::{events::IoEvents, net::iface::AnyBoundSocket, prelude::*, process::signal::Pollee};
|
||||
|
||||
pub struct ListenStream {
|
||||
backlog: usize,
|
||||
/// A bound socket held to ensure the TCP port cannot be released
|
||||
@ -117,7 +117,7 @@ impl BacklogSocket {
|
||||
let unbound_socket = Box::new(AnyUnboundSocket::new_tcp(Weak::<()>::new()));
|
||||
let bound_socket = {
|
||||
let iface = bound_socket.iface();
|
||||
let bind_port_config = BindPortConfig::new(local_endpoint.port, true)?;
|
||||
let bind_port_config = BindPortConfig::new(local_endpoint.port, true);
|
||||
iface
|
||||
.bind_socket(unbound_socket, bind_port_config)
|
||||
.map_err(|(err, _)| err)?
|
||||
|
@ -2,12 +2,12 @@
|
||||
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use aster_bigtcp::{socket::SocketEventObserver, wire::IpEndpoint};
|
||||
use connected::ConnectedStream;
|
||||
use connecting::ConnectingStream;
|
||||
use init::InitStream;
|
||||
use listen::ListenStream;
|
||||
use options::{Congestion, MaxSegment, NoDelay, WindowClamp};
|
||||
use smoltcp::wire::IpEndpoint;
|
||||
use takeable::Takeable;
|
||||
use util::{TcpOptionSet, DEFAULT_MAXSEG};
|
||||
|
||||
@ -647,8 +647,8 @@ impl Socket for StreamSocket {
|
||||
}
|
||||
}
|
||||
|
||||
impl Observer<()> for StreamSocket {
|
||||
fn on_events(&self, _events: &()) {
|
||||
impl SocketEventObserver for StreamSocket {
|
||||
fn on_events(&self) {
|
||||
let conn_ready = self.update_io_events();
|
||||
|
||||
if conn_ready {
|
||||
|
@ -2,13 +2,14 @@
|
||||
|
||||
use core::time::Duration;
|
||||
|
||||
use aster_bigtcp::socket::{
|
||||
TCP_RECV_BUF_LEN, TCP_SEND_BUF_LEN, UDP_RECV_PAYLOAD_LEN, UDP_SEND_PAYLOAD_LEN,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
match_sock_option_mut, match_sock_option_ref,
|
||||
net::{
|
||||
iface::{TCP_RECV_BUF_LEN, TCP_SEND_BUF_LEN, UDP_RECV_PAYLOAD_LEN, UDP_SEND_PAYLOAD_LEN},
|
||||
socket::options::{
|
||||
Error as SocketError, Linger, RecvBuf, ReuseAddr, ReusePort, SendBuf, SocketOption,
|
||||
},
|
||||
net::socket::options::{
|
||||
Error as SocketError, Linger, RecvBuf, ReuseAddr, ReusePort, SendBuf, SocketOption,
|
||||
},
|
||||
prelude::*,
|
||||
};
|
||||
|
@ -1,11 +1,9 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use aster_bigtcp::wire::{Ipv4Address, PortNum};
|
||||
|
||||
use crate::{
|
||||
net::socket::{
|
||||
ip::{Ipv4Address, PortNum},
|
||||
unix::UnixSocketAddr,
|
||||
vsock::addr::VsockSocketAddr,
|
||||
},
|
||||
net::socket::{unix::UnixSocketAddr, vsock::addr::VsockSocketAddr},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
|
@ -1,10 +1,9 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use aster_bigtcp::wire::{Ipv4Address, PortNum};
|
||||
|
||||
use super::family::CSocketAddrFamily;
|
||||
use crate::{
|
||||
net::socket::ip::{Ipv4Address, PortNum},
|
||||
prelude::*,
|
||||
};
|
||||
use crate::prelude::*;
|
||||
|
||||
/// IPv4 socket address.
|
||||
///
|
||||
|
Loading…
x
Reference in New Issue
Block a user