diff --git a/Cargo.lock b/Cargo.lock index f5fd22574..3299a2723 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/Cargo.toml b/Cargo.toml index 64ffc7e58..0bda9ac7b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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", diff --git a/Makefile b/Makefile index 5559f12c2..bb2285303 100644 --- a/Makefile +++ b/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 diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 1fbc860c9..d4369885c 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -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 diff --git a/kernel/libs/aster-bigtcp/Cargo.toml b/kernel/libs/aster-bigtcp/Cargo.toml new file mode 100644 index 000000000..793265de4 --- /dev/null +++ b/kernel/libs/aster-bigtcp/Cargo.toml @@ -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", +] } diff --git a/kernel/src/net/iface/device.rs b/kernel/libs/aster-bigtcp/src/device.rs similarity index 92% rename from kernel/src/net/iface/device.rs rename to kernel/libs/aster-bigtcp/src/device.rs index 25725de9c..2b0e509e1 100644 --- a/kernel/src/net/iface/device.rs +++ b/kernel/libs/aster-bigtcp/src/device.rs @@ -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`]. /// diff --git a/kernel/libs/aster-bigtcp/src/errors.rs b/kernel/libs/aster-bigtcp/src/errors.rs new file mode 100644 index 000000000..57d7e75fc --- /dev/null +++ b/kernel/libs/aster-bigtcp/src/errors.rs @@ -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}; +} diff --git a/kernel/src/net/iface/common.rs b/kernel/libs/aster-bigtcp/src/iface/common.rs similarity index 84% rename from kernel/src/net/iface/common.rs rename to kernel/libs/aster-bigtcp/src/iface/common.rs index 492e73294..3ab7f1862 100644 --- a/kernel/src/net/iface/common.rs +++ b/kernel/libs/aster-bigtcp/src/iface/common.rs @@ -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 { +pub struct IfaceCommon { interface: SpinLock, sockets: SpinLock>, used_ports: RwLock>, @@ -44,14 +50,14 @@ impl IfaceCommon { /// Acquires the lock to the interface. /// /// *Lock ordering:* [`Self::sockets`] first, [`Self::interface`] second. - pub(super) fn interface(&self) -> SpinLockGuard { + pub(crate) fn interface(&self) -> SpinLockGuard { 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, LocalIrqDisabled> { self.sockets.disable_irq().lock() @@ -62,34 +68,35 @@ impl IfaceCommon { } /// Alloc an unused port range from 49152 ~ 65535 (According to smoltcp docs) - fn alloc_ephemeral_port(&self) -> Result { + fn alloc_ephemeral_port(&self) -> Option { 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 IfaceCommon { iface: Arc>, socket: Box, config: BindPortConfig, - ) -> core::result::Result, (Error, Box)> { + ) -> core::result::Result, (BindError, Box)> { 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 IfaceCommon { } /// 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 IfaceCommon { assert!(inserted); } - pub(super) fn remove_bound_socket_now(&self, socket: &Arc>) { + pub(crate) fn remove_bound_socket_now(&self, socket: &Arc>) { let keyable_socket = KeyableArc::from(socket.clone()); let removed = self @@ -219,7 +226,7 @@ impl IfaceCommon { assert!(removed); } - pub(super) fn remove_bound_socket_when_closed(&self, socket: &Arc>) { + pub(crate) fn remove_bound_socket_when_closed(&self, socket: &Arc>) { let keyable_socket = KeyableArc::from(socket.clone()); let removed = self diff --git a/kernel/libs/aster-bigtcp/src/iface/iface.rs b/kernel/libs/aster-bigtcp/src/iface/iface.rs new file mode 100644 index 000000000..01d06762f --- /dev/null +++ b/kernel/libs/aster-bigtcp/src/iface/iface.rs @@ -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: internal::IfaceInternal + 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)); +} + +impl dyn Iface { + /// 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 + /// . + pub fn bind_socket( + self: &Arc, + socket: Box, + config: BindPortConfig, + ) -> core::result::Result, (BindError, Box)> { + 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 { + 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 { + fn common(&self) -> &IfaceCommon; + } +} diff --git a/kernel/libs/aster-bigtcp/src/iface/mod.rs b/kernel/libs/aster-bigtcp/src/iface/mod.rs new file mode 100644 index 000000000..744699cbf --- /dev/null +++ b/kernel/libs/aster-bigtcp/src/iface/mod.rs @@ -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; diff --git a/kernel/src/net/iface/ether.rs b/kernel/libs/aster-bigtcp/src/iface/phy/ether.rs similarity index 80% rename from kernel/src/net/iface/ether.rs rename to kernel/libs/aster-bigtcp/src/iface/phy/ether.rs index 045dcf615..dc7016a63 100644 --- a/kernel/src/net/iface/ether.rs +++ b/kernel/libs/aster-bigtcp/src/iface/phy/ether.rs @@ -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 { driver: D, @@ -51,18 +54,15 @@ impl EtherIface { 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 EtherIface { 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) diff --git a/kernel/src/net/iface/ip.rs b/kernel/libs/aster-bigtcp/src/iface/phy/ip.rs similarity index 83% rename from kernel/src/net/iface/ip.rs rename to kernel/libs/aster-bigtcp/src/iface/phy/ip.rs index 950da42c8..d888c5ac3 100644 --- a/kernel/src/net/iface/ip.rs +++ b/kernel/libs/aster-bigtcp/src/iface/phy/ip.rs @@ -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 { driver: D, diff --git a/kernel/libs/aster-bigtcp/src/iface/phy/mod.rs b/kernel/libs/aster-bigtcp/src/iface/phy/mod.rs new file mode 100644 index 000000000..60fc8abf7 --- /dev/null +++ b/kernel/libs/aster-bigtcp/src/iface/phy/mod.rs @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MPL-2.0 + +mod ether; +mod ip; + +pub use ether::EtherIface; +pub use ip::IpIface; diff --git a/kernel/libs/aster-bigtcp/src/iface/port.rs b/kernel/libs/aster-bigtcp/src/iface/port.rs new file mode 100644 index 000000000..88b70d52b --- /dev/null +++ b/kernel/libs/aster-bigtcp/src/iface/port.rs @@ -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 { + match self { + Self::CanReuse(port) | Self::Specified(port) => Some(*port), + Self::Ephemeral => None, + } + } +} diff --git a/kernel/src/net/iface/time.rs b/kernel/libs/aster-bigtcp/src/iface/time.rs similarity index 100% rename from kernel/src/net/iface/time.rs rename to kernel/libs/aster-bigtcp/src/iface/time.rs diff --git a/kernel/libs/aster-bigtcp/src/lib.rs b/kernel/libs/aster-bigtcp/src/lib.rs new file mode 100644 index 000000000..ef6a3be6f --- /dev/null +++ b/kernel/libs/aster-bigtcp/src/lib.rs @@ -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; diff --git a/kernel/src/net/iface/any_socket.rs b/kernel/libs/aster-bigtcp/src/socket/bound.rs similarity index 54% rename from kernel/src/net/iface/any_socket.rs rename to kernel/libs/aster-bigtcp/src/socket/bound.rs index 97379fc5e..cc7b0c303 100644 --- a/kernel/src/net/iface/any_socket.rs +++ b/kernel/libs/aster-bigtcp/src/socket/bound.rs @@ -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>, -} - -#[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>) -> 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>) -> 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>) { - (self.socket_family, self.observer) - } -} - -pub struct AnyBoundSocket(Arc>); +pub struct AnyBoundSocket(Arc>); impl AnyBoundSocket { - pub(super) fn new( + pub(crate) fn new( iface: Arc>, handle: smoltcp::iface::SocketHandle, port: u16, socket_family: SocketFamily, - observer: Weak>, + observer: Weak, ) -> Self { Self(Arc::new(AnyBoundSocketInner { iface, @@ -82,18 +35,18 @@ impl AnyBoundSocket { })) } - pub(super) fn inner(&self) -> &Arc> { + pub(crate) fn inner(&self) -> &Arc> { &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>) { - *self.0.observer.write() = handler; + pub fn set_observer(&self, new_observer: Weak) { + *self.0.observer.write() = new_observer; self.0.on_iface_events(); } @@ -118,7 +71,7 @@ impl AnyBoundSocket { /// # 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 AnyBoundSocket { 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> { @@ -162,22 +101,22 @@ impl Drop for AnyBoundSocket { } } -pub(super) struct AnyBoundSocketInner { +pub(crate) struct AnyBoundSocketInner { iface: Arc>, handle: smoltcp::iface::SocketHandle, port: u16, socket_family: SocketFamily, - observer: RwLock>>, + observer: RwLock>, } impl AnyBoundSocketInner { - 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 Drop for AnyBoundSocketInner { 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; diff --git a/kernel/libs/aster-bigtcp/src/socket/event.rs b/kernel/libs/aster-bigtcp/src/socket/event.rs new file mode 100644 index 000000000..d0770aae7 --- /dev/null +++ b/kernel/libs/aster-bigtcp/src/socket/event.rs @@ -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) {} +} diff --git a/kernel/libs/aster-bigtcp/src/socket/mod.rs b/kernel/libs/aster-bigtcp/src/socket/mod.rs new file mode 100644 index 000000000..4c5929a32 --- /dev/null +++ b/kernel/libs/aster-bigtcp/src/socket/mod.rs @@ -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>; diff --git a/kernel/libs/aster-bigtcp/src/socket/unbound.rs b/kernel/libs/aster-bigtcp/src/socket/unbound.rs new file mode 100644 index 000000000..9fbba4e08 --- /dev/null +++ b/kernel/libs/aster-bigtcp/src/socket/unbound.rs @@ -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, +} + +#[allow(clippy::large_enum_variant)] +pub(crate) enum AnyRawSocket { + Tcp(RawTcpSocket), + Udp(RawUdpSocket), +} + +impl AnyUnboundSocket { + pub fn new_tcp(observer: Weak) -> 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) -> 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) { + (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; diff --git a/kernel/libs/aster-bigtcp/src/wire.rs b/kernel/libs/aster-bigtcp/src/wire.rs new file mode 100644 index 000000000..f640ed907 --- /dev/null +++ b/kernel/libs/aster-bigtcp/src/wire.rs @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: MPL-2.0 + +pub use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, IpEndpoint, Ipv4Address, Ipv4Cidr}; + +pub type PortNum = u16; diff --git a/kernel/src/net/iface/ext.rs b/kernel/src/net/iface/ext.rs index 48ad7da95..07e00b64f 100644 --- a/kernel/src/net/iface/ext.rs +++ b/kernel/src/net/iface/ext.rs @@ -67,7 +67,7 @@ pub trait IfaceEx { fn poll(&self); } -impl IfaceEx for dyn Iface { +impl IfaceEx for Iface { fn name(&self) -> &str { &self.ext().name } diff --git a/kernel/src/net/iface/init.rs b/kernel/src/net/iface/init.rs index bbf406617..afa08f288 100644 --- a/kernel/src/net/iface/init.rs +++ b/kernel/src/net/iface/init.rs @@ -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>> = Once::new(); +pub static IFACES: Once>> = Once::new(); pub fn init() { IFACES.call_once(|| { @@ -34,12 +32,11 @@ pub fn init() { poll_ifaces(); } -fn new_virtio() -> Arc { +fn new_virtio() -> Arc { + 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 { ) } -fn new_loopback() -> Arc { - use smoltcp::phy::{Loopback, Medium}; - - use super::ip::{IpAddress, IpCidr, IpIface, Ipv4Address}; +fn new_loopback() -> Arc { + 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 { 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(); - } -} diff --git a/kernel/src/net/iface/mod.rs b/kernel/src/net/iface/mod.rs index 72c709f60..ce5674fea 100644 --- a/kernel/src/net/iface/mod.rs +++ b/kernel/src/net/iface/mod.rs @@ -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: internal::IfaceInternal + 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)); -} - -impl dyn Iface { - /// 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 - /// . - pub fn bind_socket( - self: &Arc, - socket: Box, - config: BindPortConfig, - ) -> core::result::Result, (Error, Box)> { - 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 { - self.common().ipv4_addr() - } -} - -mod internal { - use super::*; - - /// An internal trait that abstracts the common part of different ifaces. - pub trait IfaceInternal { - fn common(&self) -> &IfaceCommon; - } -} +pub type Iface = dyn aster_bigtcp::iface::Iface; +pub type AnyBoundSocket = aster_bigtcp::socket::AnyBoundSocket; diff --git a/kernel/src/net/iface/util.rs b/kernel/src/net/iface/poll.rs similarity index 68% rename from kernel/src/net/iface/util.rs rename to kernel/src/net/iface/poll.rs index 8416bc023..0a57e9d4d 100644 --- a/kernel/src/net/iface/util.rs +++ b/kernel/src/net/iface/poll.rs @@ -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 { - 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 { - 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) { +pub fn poll_ifaces() { + let ifaces = IFACES.get().unwrap(); + + for iface in ifaces.iter() { + iface.poll(); + } +} + +fn spawn_background_poll_thread(iface: Arc) { let task_fn = move || { trace!("spawn background poll thread for {}", iface.name()); diff --git a/kernel/src/net/socket/ip/addr.rs b/kernel/src/net/socket/ip/addr.rs index 047581138..169a6712d 100644 --- a/kernel/src/net/socket/ip/addr.rs +++ b/kernel/src/net/socket/ip/addr.rs @@ -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 for IpEndpoint { type Error = Error; @@ -29,3 +27,11 @@ impl From 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); diff --git a/kernel/src/net/socket/ip/common.rs b/kernel/src/net/socket/ip/common.rs index c60d33644..113acea8f 100644 --- a/kernel/src/net/socket/ip/common.rs +++ b/kernel/src/net/socket/ip/common.rs @@ -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> { +pub(super) fn get_iface_to_bind(ip_addr: &IpAddress) -> Option> { 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> { /// 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 { +fn get_ephemeral_iface(remote_ip_addr: &IpAddress) -> Arc { 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 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) diff --git a/kernel/src/net/socket/ip/datagram/bound.rs b/kernel/src/net/socket/ip/datagram/bound.rs index 5181a826a..32592a4f6 100644 --- a/kernel/src/net/socket/ip/datagram/bound.rs +++ b/kernel/src/net/socket/ip/datagram/bound.rs @@ -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, }; diff --git a/kernel/src/net/socket/ip/datagram/mod.rs b/kernel/src/net/socket/ip/datagram/mod.rs index 6420919ac..0c97d9381 100644 --- a/kernel/src/net/socket/ip/datagram/mod.rs +++ b/kernel/src/net/socket/ip/datagram/mod.rs @@ -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(); } } diff --git a/kernel/src/net/socket/ip/datagram/unbound.rs b/kernel/src/net/socket/ip/datagram/unbound.rs index 7d363eddc..365233f36 100644 --- a/kernel/src/net/socket/ip/datagram/unbound.rs +++ b/kernel/src/net/socket/ip/datagram/unbound.rs @@ -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>) -> Self { + pub fn new(observer: Weak) -> Self { Self { unbound_socket: Box::new(AnyUnboundSocket::new_udp(observer)), } diff --git a/kernel/src/net/socket/ip/mod.rs b/kernel/src/net/socket/ip/mod.rs index d6c0cb93a..313260606 100644 --- a/kernel/src/net/socket/ip/mod.rs +++ b/kernel/src/net/socket/ip/mod.rs @@ -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); diff --git a/kernel/src/net/socket/ip/stream/connected.rs b/kernel/src/net/socket/ip/stream/connected.rs index d5e337c9d..e0f4fd90b 100644 --- a/kernel/src/net/socket/ip/stream/connected.rs +++ b/kernel/src/net/socket/ip/stream/connected.rs @@ -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>) { + pub(super) fn set_observer(&self, observer: Weak) { self.bound_socket.set_observer(observer) } } diff --git a/kernel/src/net/socket/ip/stream/connecting.rs b/kernel/src/net/socket/ip/stream/connecting.rs index 8833b1560..413e1cb9c 100644 --- a/kernel/src/net/socket/ip/stream/connecting.rs +++ b/kernel/src/net/socket/ip/stream/connecting.rs @@ -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 { - 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, diff --git a/kernel/src/net/socket/ip/stream/init.rs b/kernel/src/net/socket/ip/stream/init.rs index 8f04de7c2..0f312793d 100644 --- a/kernel/src/net/socket/ip/stream/init.rs +++ b/kernel/src/net/socket/ip/stream/init.rs @@ -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>) -> Self { + pub fn new(observer: Weak) -> Self { InitStream::Unbound(Box::new(AnyUnboundSocket::new_tcp(observer))) } diff --git a/kernel/src/net/socket/ip/stream/listen.rs b/kernel/src/net/socket/ip/stream/listen.rs index 9e576e1f2..45b6f7cfa 100644 --- a/kernel/src/net/socket/ip/stream/listen.rs +++ b/kernel/src/net/socket/ip/stream/listen.rs @@ -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)? diff --git a/kernel/src/net/socket/ip/stream/mod.rs b/kernel/src/net/socket/ip/stream/mod.rs index bedd310bb..921233f61 100644 --- a/kernel/src/net/socket/ip/stream/mod.rs +++ b/kernel/src/net/socket/ip/stream/mod.rs @@ -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 { diff --git a/kernel/src/net/socket/util/options.rs b/kernel/src/net/socket/util/options.rs index f7f8ddd52..be3a0541f 100644 --- a/kernel/src/net/socket/util/options.rs +++ b/kernel/src/net/socket/util/options.rs @@ -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::*, }; diff --git a/kernel/src/net/socket/util/socket_addr.rs b/kernel/src/net/socket/util/socket_addr.rs index 445fd6b27..3c1b994f3 100644 --- a/kernel/src/net/socket/util/socket_addr.rs +++ b/kernel/src/net/socket/util/socket_addr.rs @@ -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::*, }; diff --git a/kernel/src/util/net/addr/ip.rs b/kernel/src/util/net/addr/ip.rs index c43dc83be..873cef184 100644 --- a/kernel/src/util/net/addr/ip.rs +++ b/kernel/src/util/net/addr/ip.rs @@ -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. ///