diff --git a/kernel/src/net/socket/inet/common/port.rs b/kernel/src/net/socket/inet/common/port.rs index 5f68e427bb..26fcabc652 100644 --- a/kernel/src/net/socket/inet/common/port.rs +++ b/kernel/src/net/socket/inet/common/port.rs @@ -7,7 +7,7 @@ use system_error::SystemError; use crate::{ arch::rand::rand, libs::mutex::Mutex, - process::{ProcessManager, RawPid}, + process::ProcessManager, }; use super::Types::{self, *}; @@ -16,8 +16,8 @@ use super::Types::{self, *}; /// 如果 TCP/UDP 的 socket 绑定了某个端口,它会在对应的表中记录,以检测端口冲突。 #[derive(Debug)] pub struct PortManager { - // TCP 端口记录表 - tcp_port_table: Mutex>, + // TCP 端口记录表。一个端口可以有多条绑定记录(SO_REUSEPORT/SO_REUSEADDR 共享)。 + tcp_port_table: Mutex>>, // UDP 端口记录表 udp_port_table: Mutex>>, } @@ -95,7 +95,14 @@ impl PortManager { } #[inline] - pub fn bind_ephemeral_port(&self, socket_type: Types) -> Result { + pub fn bind_tcp_ephemeral_port( + &self, + addr: IpAddress, + reuseaddr: bool, + reuseport: bool, + iface_nic_id: usize, + handle: smoltcp::iface::SocketHandle, + ) -> Result { let (min, max) = Self::local_port_range(); let range = (max - min) as u32 + 1; if range == 0 { @@ -103,8 +110,8 @@ impl PortManager { } let mut remaining = range; while remaining > 0 { - let port = self.get_ephemeral_port(socket_type)?; - match self.bind_port(socket_type, port) { + let port = self.get_ephemeral_port(Types::Tcp)?; + match self.bind_tcp_port(port, addr, reuseaddr, reuseport, iface_nic_id, handle) { Ok(()) => return Ok(port), Err(SystemError::EADDRINUSE) => { // Race: another thread grabbed the port after we checked. @@ -146,44 +153,52 @@ impl PortManager { Err(SystemError::EADDRINUSE) } - /// @brief 检测给定端口是否已被占用,如果未被占用则在 TCP 对应的表中记录 + /// TCP: 绑定端口,支持 SO_REUSEADDR/SO_REUSEPORT。 /// - /// UDP 复用逻辑请使用 `bind_udp_port` - pub fn bind_port(&self, socket_type: Types, port: u16) -> Result<(), SystemError> { - if port > 0 { - match socket_type { - Udp => { - let mut guard = self.udp_port_table.lock(); - if guard.get(&port).is_some() { - return Err(SystemError::EADDRINUSE); - } - guard.insert(port, Vec::new()); - } - Tcp => { - let mut guard = self.tcp_port_table.lock(); - if guard.get(&port).is_some() { - return Err(SystemError::EADDRINUSE); - } - guard.insert(port, ProcessManager::current_pid()); - } - _ => {} - }; + /// 一条绑定记录以 `(iface_nic_id, handle)` 唯一标识(BoundInner 身份), + /// 因此多个进程/多个 socket 可以共享同一端口而不需要调用方保存额外 id。 + pub fn bind_tcp_port( + &self, + port: u16, + addr: IpAddress, + reuseaddr: bool, + reuseport: bool, + iface_nic_id: usize, + handle: smoltcp::iface::SocketHandle, + ) -> Result<(), SystemError> { + if port == 0 { + return Err(SystemError::EINVAL); + } + let mut guard = self.tcp_port_table.lock(); + let bindings = guard.entry(port).or_default(); + for binding in bindings.iter() { + if !addrs_conflict(addr, binding.addr) { + continue; + } + let share_ok = (reuseport && binding.reuseport) || (reuseaddr && binding.reuseaddr); + if !share_ok { + return Err(SystemError::EADDRINUSE); + } } - return Ok(()); + bindings.push(TcpPortBinding { + addr, + reuseaddr, + reuseport, + iface_nic_id, + handle, + }); + Ok(()) } - /// @brief 在对应的端口记录表中将端口和 socket 解绑 - /// should call this function when socket is closed or aborted - pub fn unbind_port(&self, socket_type: Types, port: u16) { - match socket_type { - Udp => { - self.udp_port_table.lock().remove(&port); - } - Tcp => { - self.tcp_port_table.lock().remove(&port); + /// TCP: 解绑端口(按 BoundInner 身份) + pub fn unbind_tcp_port(&self, port: u16, iface_nic_id: usize, handle: smoltcp::iface::SocketHandle) { + let mut guard = self.tcp_port_table.lock(); + if let Some(list) = guard.get_mut(&port) { + list.retain(|b| b.iface_nic_id != iface_nic_id || b.handle != handle); + if list.is_empty() { + guard.remove(&port); } - _ => {} - }; + } } /// UDP: 绑定端口,支持 SO_REUSEADDR/SO_REUSEPORT @@ -201,7 +216,7 @@ impl PortManager { let mut guard = self.udp_port_table.lock(); let bindings = guard.entry(port).or_default(); for binding in bindings.iter() { - if !udp_addrs_conflict(addr, binding.addr) { + if !addrs_conflict(addr, binding.addr) { continue; } let share_ok = (reuseport && binding.reuseport) || (reuseaddr && binding.reuseaddr); @@ -238,8 +253,18 @@ struct UdpPortBinding { bind_id: usize, } +/// TCP 端口绑定记录。`(iface_nic_id, handle)` 是绑定的 BoundInner 身份。 +#[derive(Debug, Clone)] +struct TcpPortBinding { + addr: IpAddress, + reuseaddr: bool, + reuseport: bool, + iface_nic_id: usize, + handle: smoltcp::iface::SocketHandle, +} + #[inline] -fn udp_addrs_conflict(a: IpAddress, b: IpAddress) -> bool { +fn addrs_conflict(a: IpAddress, b: IpAddress) -> bool { if a.version() != b.version() { return false; } diff --git a/kernel/src/net/socket/inet/mod.rs b/kernel/src/net/socket/inet/mod.rs index aa79de1876..e778006b8e 100644 --- a/kernel/src/net/socket/inet/mod.rs +++ b/kernel/src/net/socket/inet/mod.rs @@ -8,7 +8,7 @@ pub mod stream; pub mod syscall; pub use common::BoundInner; -pub use common::Types; + pub use datagram::UdpSocket; pub use raw::RawSocket; diff --git a/kernel/src/net/socket/inet/stream/inner.rs b/kernel/src/net/socket/inet/stream/inner.rs index 6c7c85aade..1506c2f18e 100644 --- a/kernel/src/net/socket/inet/stream/inner.rs +++ b/kernel/src/net/socket/inet/stream/inner.rs @@ -5,7 +5,7 @@ use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use crate::filesystem::epoll::EPollEventType; use crate::libs::mutex::Mutex; use crate::libs::rwsem::RwSem; -use crate::net::socket::{self, inet::Types}; +use crate::net::socket::{self}; use crate::process::namespace::net_namespace::NetNamespace; use crate::syscall::user_buffer::UserBuffer; use alloc::boxed::Box; @@ -51,11 +51,13 @@ fn new_smoltcp_socket() -> smoltcp::socket::tcp::Socket<'static> { fn new_listen_smoltcp_socket( local_endpoint: T, + reuseport: bool, ) -> Result, SystemError> where T: Into, { let mut socket = new_smoltcp_socket(); + socket.set_reuseport(reuseport); socket.listen(local_endpoint).map_err(|e| match e { tcp::ListenError::InvalidState => SystemError::EINVAL, // TODO: Check is right impl tcp::ListenError::Unaddressable => SystemError::EADDRINUSE, @@ -112,6 +114,8 @@ impl Init { self, local_endpoint: smoltcp::wire::IpEndpoint, netns: Arc, + reuseaddr: bool, + reuseport: bool, ) -> Result { match self { Init::Unbound((socket, ver)) => { @@ -128,7 +132,13 @@ impl Init { // Handle ephemeral port assignment (port 0) let bind_port = if local_endpoint.port == 0 { - match bound.port_manager().bind_ephemeral_port(Types::Tcp) { + match bound.port_manager().bind_tcp_ephemeral_port( + local_endpoint.addr, + reuseaddr, + reuseport, + bound.iface().nic_id(), + bound.handle(), + ) { Ok(port) => port, Err(err) => { let smoltcp::socket::Socket::Tcp(socket) = bound.into_socket() else { @@ -138,10 +148,14 @@ impl Init { } } } else { - if let Err(err) = bound - .port_manager() - .bind_port(Types::Tcp, local_endpoint.port) - { + if let Err(err) = bound.port_manager().bind_tcp_port( + local_endpoint.port, + local_endpoint.addr, + reuseaddr, + reuseport, + bound.iface().nic_id(), + bound.handle(), + ) { let smoltcp::socket::Socket::Tcp(socket) = bound.into_socket() else { unreachable!("TCP BoundInner should contain a TCP socket"); }; @@ -178,7 +192,13 @@ impl Init { return Err((Self::Unbound((Box::new(socket), ver)), err)) } }; - let bound_port = match bound.port_manager().bind_ephemeral_port(Types::Tcp) { + let bound_port = match bound.port_manager().bind_tcp_ephemeral_port( + address, + false, + false, + bound.iface().nic_id(), + bound.handle(), + ) { Ok(port) => port, Err(err) => { let smoltcp::socket::Socket::Tcp(socket) = bound.into_socket() else { @@ -230,6 +250,8 @@ impl Init { self, backlog: usize, netns: Arc, + reuseaddr: bool, + reuseport: bool, ) -> Result { // If unbound, auto-bind to INADDR_ANY:ephemeral (Linux compat). let bound_self = if matches!(self, Init::Unbound(_)) { @@ -246,7 +268,7 @@ impl Init { } }; let auto_bind_ep = smoltcp::wire::IpEndpoint::new(unspec_addr, 0); - match self.bind(auto_bind_ep, netns.clone()) { + match self.bind(auto_bind_ep, netns.clone(), reuseaddr, reuseport) { Ok(bound) => bound, Err((init, err)) => return Err((init, err)), } @@ -298,7 +320,7 @@ impl Init { continue; // primary inner already covers this iface } let new_listen = socket::inet::BoundInner::bind_on_iface( - new_listen_smoltcp_socket(listen_addr)?, + new_listen_smoltcp_socket(listen_addr, reuseport)?, iface.clone(), inner.netns(), )?; @@ -308,7 +330,7 @@ impl Init { let remaining = backlog.saturating_sub(1 + inners.len()); for _ in 0..remaining { let new_listen = socket::inet::BoundInner::bind_on_iface( - new_listen_smoltcp_socket(listen_addr)?, + new_listen_smoltcp_socket(listen_addr, reuseport)?, inner.iface().clone(), inner.netns(), )?; @@ -319,7 +341,7 @@ impl Init { let additional_sockets = backlog.saturating_sub(1); for _ in 0..additional_sockets { let new_listen = socket::inet::BoundInner::bind( - new_listen_smoltcp_socket(listen_addr)?, + new_listen_smoltcp_socket(listen_addr, reuseport)?, listen_addr .addr .as_ref() @@ -337,6 +359,7 @@ impl Init { } if let Err(err) = inner.with_mut::(|socket| { + socket.set_reuseport(reuseport); socket.listen(listen_addr).map_err(|err| match err { tcp::ListenError::InvalidState => SystemError::EINVAL, tcp::ListenError::Unaddressable => SystemError::EINVAL, @@ -350,6 +373,7 @@ impl Init { inners, connect: AtomicUsize::new(0), listen_addr, + reuseport, }); } @@ -357,7 +381,9 @@ impl Init { match self { Init::Unbound(_) => {} Init::Bound((inner, endpoint)) => { - inner.port_manager().unbind_port(Types::Tcp, endpoint.port); + inner + .port_manager() + .unbind_tcp_port(endpoint.port, inner.iface().nic_id(), inner.handle()); inner.with_mut::(|socket| socket.close()); } } @@ -434,9 +460,11 @@ impl Connecting { | ConnectResult::ShutdownReset | ConnectResult::ShutdownResetConsumed => { // unbind port - self.inner - .port_manager() - .unbind_port(Types::Tcp, self.local.port); + self.inner.port_manager().unbind_tcp_port( + self.local.port, + self.inner.iface().nic_id(), + self.inner.handle(), + ); let socket = self.inner.into_socket(); let socket = match socket { smoltcp::socket::Socket::Tcp(s) => s, @@ -690,6 +718,7 @@ pub struct Listening { pub inners: Vec, connect: AtomicUsize, listen_addr: smoltcp::wire::IpListenEndpoint, + reuseport: bool, } impl Listening { @@ -716,13 +745,13 @@ impl Listening { // where each interface has its own listen socket in the smoltcp SocketSet. let mut new_listen = if self.listen_addr.addr.is_none() { socket::inet::BoundInner::bind_on_iface( - new_listen_smoltcp_socket(self.listen_addr)?, + new_listen_smoltcp_socket(self.listen_addr, self.reuseport)?, connected.iface().clone(), connected.netns(), )? } else { socket::inet::BoundInner::bind( - new_listen_smoltcp_socket(self.listen_addr)?, + new_listen_smoltcp_socket(self.listen_addr, self.reuseport)?, self.listen_addr .addr .as_ref() @@ -782,12 +811,14 @@ impl Listening { // (pushed last during listen() construction). We must unbind from its // port_manager, not inners[0] which may belong to a different iface for // INADDR_ANY listeners. - self.inners + let owner = self + .inners .last() - .expect("Listening socket must have at least one inner") + .expect("Listening socket must have at least one inner"); + owner .iface() .port_manager() - .unbind_port(Types::Tcp, port); + .unbind_tcp_port(port, owner.iface().nic_id(), owner.handle()); } pub fn release(&mut self) { diff --git a/kernel/src/net/socket/inet/stream/lifecycle.rs b/kernel/src/net/socket/inet/stream/lifecycle.rs index 708bb44d56..9fbbe6cd67 100644 --- a/kernel/src/net/socket/inet/stream/lifecycle.rs +++ b/kernel/src/net/socket/inet/stream/lifecycle.rs @@ -1,6 +1,5 @@ use crate::net::socket::common::ShutdownBit; use crate::net::socket::inet::InetSocket; -use crate::net::socket::inet::Types; use crate::net::tcp_close_defer::{ DeferredTcpCloseKind, DeferredTcpCloseReason, DeferredTcpCloseRequest, }; @@ -143,20 +142,28 @@ impl TcpSocket { pub fn do_bind(&self, local_endpoint: smoltcp::wire::IpEndpoint) -> Result<(), SystemError> { let mut writer = self.inner.write(); match writer.take().expect("Tcp inner::Inner is None") { - inner::Inner::Init(inner) => match inner.bind(local_endpoint, self.netns()) { - Ok(bound) => { - if let inner::Init::Bound((ref bound, _)) = bound { - bound - .iface() - .common() - .bind_socket(self.self_ref.upgrade().unwrap()); + inner::Inner::Init(inner) => { + let reuseaddr = self + .so_reuseaddr() + .load(core::sync::atomic::Ordering::Relaxed); + let reuseport = self + .so_reuseport() + .load(core::sync::atomic::Ordering::Relaxed); + match inner.bind(local_endpoint, self.netns(), reuseaddr, reuseport) { + Ok(bound) => { + if let inner::Init::Bound((ref bound, _)) = bound { + bound + .iface() + .common() + .bind_socket(self.self_ref.upgrade().unwrap()); + } + writer.replace(inner::Inner::Init(bound)); + Ok(()) + } + Err((inner, err)) => { + writer.replace(inner::Inner::Init(inner)); + Err(err) } - writer.replace(inner::Inner::Init(bound)); - Ok(()) - } - Err((inner, err)) => { - writer.replace(inner::Inner::Init(inner)); - Err(err) } }, any => { @@ -172,7 +179,13 @@ impl TcpSocket { let inner = writer.take().expect("Tcp inner::Inner is None"); let (listening, err) = match inner { inner::Inner::Init(init) => { - let listen_result = init.listen(backlog, self.netns()); + let reuseaddr = self + .so_reuseaddr() + .load(core::sync::atomic::Ordering::Relaxed); + let reuseport = self + .so_reuseport() + .load(core::sync::atomic::Ordering::Relaxed); + let listen_result = init.listen(backlog, self.netns(), reuseaddr, reuseport); match listen_result { Ok(listening) => { // DragonOS backlog emulation: listener is represented by multiple @@ -662,7 +675,9 @@ impl TcpSocket { conn.with_mut(|socket| socket.abort()); let initial_state = conn.with(|socket| socket.state()); if conn.owns_port() { - iface.port_manager().unbind_port(Types::Tcp, local_port); + iface + .port_manager() + .unbind_tcp_port(local_port, iface.nic_id(), handle); } iface.common().defer_tcp_close(DeferredTcpCloseRequest { handle, @@ -688,7 +703,9 @@ impl TcpSocket { es.with_mut(|socket| Self::apply_close_action(socket, close_action)); let initial_state = es.with(|socket| socket.state()); if es.owns_port() { - iface.port_manager().unbind_port(Types::Tcp, local_port); + iface + .port_manager() + .unbind_tcp_port(local_port, iface.nic_id(), es.handle()); } iface.common().defer_tcp_close(DeferredTcpCloseRequest { handle, @@ -712,8 +729,11 @@ impl TcpSocket { }; let port = sc.get_name().port; let iface = sc.iface().clone(); + let handle = sc.handle(); sc.release(); - iface.port_manager().unbind_port(Types::Tcp, port); + iface + .port_manager() + .unbind_tcp_port(port, iface.nic_id(), handle); writer.replace(inner::Inner::Closed(inner::Closed::new(ver))); } inner::Inner::Listening(mut ls) => { diff --git a/kernel/src/net/socket/inet/stream/option.rs b/kernel/src/net/socket/inet/stream/option.rs index 2ab21b6fa1..1caa423ef2 100644 --- a/kernel/src/net/socket/inet/stream/option.rs +++ b/kernel/src/net/socket/inet/stream/option.rs @@ -311,6 +311,7 @@ impl super::TcpSocket { Ok(()) }), PSO::REUSEADDR => Self::set_bool_option(self.so_reuseaddr(), val, |_| Ok(())), + PSO::REUSEPORT => Self::set_bool_option(self.so_reuseport(), val, |_| Ok(())), PSO::BROADCAST => Self::set_bool_option(self.so_broadcast(), val, |_| Ok(())), PSO::PASSCRED => Self::set_bool_option(self.so_passcred(), val, |_| Ok(())), PSO::NO_CHECK => Self::set_bool_option(self.so_no_check(), val, |_| Ok(())), @@ -570,6 +571,7 @@ impl super::TcpSocket { } PSO::KEEPALIVE => Self::write_bool_opt_i32(value, self.so_keepalive_enabled()), PSO::REUSEADDR => Self::write_bool_opt_i32(value, self.so_reuseaddr()), + PSO::REUSEPORT => Self::write_bool_opt_i32(value, self.so_reuseport()), PSO::BROADCAST => Self::write_bool_opt_i32(value, self.so_broadcast()), PSO::PASSCRED => Self::write_bool_opt_i32(value, self.so_passcred()), PSO::NO_CHECK => Self::write_bool_opt_i32(value, self.so_no_check()), diff --git a/kernel/src/net/socket/inet/stream/stream_core.rs b/kernel/src/net/socket/inet/stream/stream_core.rs index b45e6acd24..cba1f39586 100644 --- a/kernel/src/net/socket/inet/stream/stream_core.rs +++ b/kernel/src/net/socket/inet/stream/stream_core.rs @@ -52,6 +52,8 @@ pub struct TcpSocketOptions { pub(crate) so_filter_attached: AtomicBool, /// SO_REUSEADDR pub(crate) so_reuseaddr: AtomicBool, + /// SO_REUSEPORT + pub(crate) so_reuseport: AtomicBool, /// SO_BROADCAST pub(crate) so_broadcast: AtomicBool, /// SO_PASSCRED @@ -108,6 +110,7 @@ impl TcpSocketOptions { tcp_user_timeout: AtomicI32::new(0), so_filter_attached: AtomicBool::new(false), so_reuseaddr: AtomicBool::new(false), + so_reuseport: AtomicBool::new(false), so_broadcast: AtomicBool::new(false), so_passcred: AtomicBool::new(false), so_no_check: AtomicBool::new(false), @@ -325,6 +328,11 @@ impl TcpSocket { &self.options.so_reuseaddr } + #[inline] + pub(crate) fn so_reuseport(&self) -> &AtomicBool { + &self.options.so_reuseport + } + #[inline] pub(crate) fn so_broadcast(&self) -> &AtomicBool { &self.options.so_broadcast