Move smoltcp-related code to bigtcp

This commit is contained in:
Ruihan Li 2024-09-06 18:49:37 +08:00 committed by Tate, Hongliang Tian
parent 9fba9445bd
commit 67d3682116
39 changed files with 542 additions and 382 deletions

11
Cargo.lock generated
View File

@ -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",

View File

@ -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",

View File

@ -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

View File

@ -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

View 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",
] }

View File

@ -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`].
///

View 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};
}

View File

@ -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

View 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>;
}
}

View 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;

View File

@ -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)

View File

@ -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,

View File

@ -0,0 +1,7 @@
// SPDX-License-Identifier: MPL-2.0
mod ether;
mod ip;
pub use ether::EtherIface;
pub use ip::IpIface;

View 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,
}
}
}

View 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;

View File

@ -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;

View 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) {}
}

View 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>;

View 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;

View 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;

View File

@ -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
}

View File

@ -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();
}
}

View File

@ -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>;

View File

@ -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());

View File

@ -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);

View File

@ -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)

View File

@ -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,
};

View File

@ -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();
}
}

View File

@ -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)),
}

View File

@ -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);

View File

@ -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)
}
}

View File

@ -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,

View File

@ -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)))
}

View File

@ -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)?

View File

@ -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 {

View File

@ -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::*,
};

View File

@ -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::*,
};

View File

@ -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.
///