-
-
Notifications
You must be signed in to change notification settings - Fork 189
feat(net): enhance Linux compatibility for socket operations #1818
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -36,17 +36,27 @@ 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); | ||
| let listen_result = init.listen(backlog, self.netns()); | ||
| match listen_result { | ||
| Ok(listening) => { | ||
| // DragonOS backlog emulation: listener is represented by multiple | ||
| // smoltcp TCP sockets. When all LISTEN sockets are consumed, | ||
| // Linux commonly drops incoming SYN (no RST). To implement this | ||
| // without changing smoltcp semantics, register the active listen port | ||
| // in the iface common registry. | ||
| // | ||
| // For INADDR_ANY listeners, listen sockets span multiple interfaces, | ||
| // so register on each unique interface. | ||
| let port = listening.get_name().port; | ||
| if let Some(b) = listening.inners.first() { | ||
| b.iface().common().register_tcp_listen_port(port, backlog); | ||
| let me = self.self_ref.upgrade().unwrap(); | ||
| let mut registered_ifaces: alloc::vec::Vec<usize> = alloc::vec::Vec::new(); | ||
| for b in &listening.inners { | ||
| let nic_id = b.iface().nic_id(); | ||
| if !registered_ifaces.contains(&nic_id) { | ||
| b.iface().common().register_tcp_listen_port(port, backlog); | ||
| b.iface().common().bind_socket(me.clone()); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Registering the listening socket on each unique iface here is not matched by symmetric unregistration: close/shutdown paths only call Useful? React with 👍 / 👎. |
||
| registered_ifaces.push(nic_id); | ||
| } | ||
| } | ||
| (inner::Inner::Listening(listening), None) | ||
| } | ||
|
|
@@ -66,14 +76,21 @@ impl TcpSocket { | |
|
|
||
| pub fn try_accept(&self) -> Result<(Arc<TcpSocket>, smoltcp::wire::IpEndpoint), SystemError> { | ||
| // 主动推进协议栈:避免依赖后台 poll 线程,保证 accept 在无事件通知场景下也能前进。 | ||
| if let Some(iface) = self | ||
| .inner | ||
| .read() | ||
| .as_ref() | ||
| .and_then(|inner| inner.iface()) | ||
| .cloned() | ||
| // For INADDR_ANY listeners, poll all interfaces that have listen sockets. | ||
| { | ||
| iface.poll(); | ||
| let reader = self.inner.read(); | ||
| if let Some(inner::Inner::Listening(listening)) = reader.as_ref() { | ||
| let mut polled_nics: alloc::vec::Vec<usize> = alloc::vec::Vec::new(); | ||
| for b in &listening.inners { | ||
| let nic_id = b.iface().nic_id(); | ||
| if !polled_nics.contains(&nic_id) { | ||
| b.iface().poll(); | ||
| polled_nics.push(nic_id); | ||
| } | ||
| } | ||
| } else if let Some(iface) = reader.as_ref().and_then(|inner| inner.iface()).cloned() { | ||
| iface.poll(); | ||
| } | ||
| } | ||
|
|
||
| match self | ||
|
|
@@ -112,7 +129,6 @@ impl TcpSocket { | |
| &self, | ||
| remote_endpoint: smoltcp::wire::IpEndpoint, | ||
| ) -> Result<(), SystemError> { | ||
| // log::debug!("TcpSocket::start_connect: remote={:?}", remote_endpoint); | ||
| let mut writer = self.inner.write(); | ||
| let inner = writer.take().expect("Tcp inner::Inner is None"); | ||
| let (init, result) = match inner { | ||
|
|
@@ -316,8 +332,19 @@ impl TcpSocket { | |
| let local = listening.get_name(); | ||
| let port = local.port; | ||
|
|
||
| if let Some(b) = listening.inners.first() { | ||
| b.iface().common().unregister_tcp_listen_port(port); | ||
| // Unregister listen port and unbind socket from all unique interfaces. | ||
| // For INADDR_ANY listeners, listen sockets span multiple interfaces. | ||
| { | ||
| let me = self.self_ref.upgrade().unwrap(); | ||
| let mut unregistered: alloc::vec::Vec<usize> = alloc::vec::Vec::new(); | ||
| for b in &listening.inners { | ||
| let nic_id = b.iface().nic_id(); | ||
| if !unregistered.contains(&nic_id) { | ||
| b.iface().common().unregister_tcp_listen_port(port); | ||
| b.iface().common().unbind_socket(me.clone()); | ||
| unregistered.push(nic_id); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| for bound in &listening.inners { | ||
|
|
@@ -454,10 +481,16 @@ impl TcpSocket { | |
| // close(fd) must not break in-flight syscalls that already hold a | ||
| // reference to this socket object (gVisor ClosedWriteBlockingSocket). | ||
| // So we do NOT leave self.inner as None; we always reinsert it below. | ||
| if let Some(iface) = inner.iface() { | ||
| iface | ||
| .common() | ||
| .unbind_socket(self.self_ref.upgrade().unwrap()); | ||
| // | ||
| // For Listening sockets, unbind_socket must be done per-iface inside the | ||
| // Listening match arm (INADDR_ANY spans multiple interfaces). For all other | ||
| // states, inner.iface() returns the single owning iface. | ||
| if !matches!(inner, inner::Inner::Listening(_)) { | ||
| if let Some(iface) = inner.iface() { | ||
| iface | ||
| .common() | ||
| .unbind_socket(self.self_ref.upgrade().unwrap()); | ||
| } | ||
| } | ||
|
|
||
| match inner { | ||
|
|
@@ -526,8 +559,20 @@ impl TcpSocket { | |
| inner::Inner::Listening(mut ls) => { | ||
| // close(listen_fd) should stop listening on the port. | ||
| let port = ls.get_name().port; | ||
| if let Some(b) = ls.inners.first() { | ||
| b.iface().common().unregister_tcp_listen_port(port); | ||
| // Unregister listen port and unbind socket from all unique interfaces. | ||
| // For INADDR_ANY listeners, listen sockets span multiple interfaces, | ||
| // so we must clean up each one. | ||
| { | ||
| let me = self.self_ref.upgrade().unwrap(); | ||
| let mut cleaned: alloc::vec::Vec<usize> = alloc::vec::Vec::new(); | ||
| for b in &ls.inners { | ||
| let nic_id = b.iface().nic_id(); | ||
| if !cleaned.contains(&nic_id) { | ||
| b.iface().common().unregister_tcp_listen_port(port); | ||
| b.iface().common().unbind_socket(me.clone()); | ||
| cleaned.push(nic_id); | ||
| } | ||
| } | ||
| } | ||
| ls.close(); | ||
| // IMPORTANT: | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This branch inserts non-primary interface listeners into
innersbefore the original bound listener, butListening::close()still frees the bound TCP port viaself.inners[0].iface().port_manager().unbind_port(...), which assumes index 0 owns the reservation. On multi-interface INADDR_ANY listeners, close will unbind the wrong iface and leak the actual port binding on the primary iface, so subsequent bind/listen on the same port can fail withEADDRINUSE.Useful? React with 👍 / 👎.