mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-21 08:26:30 +00:00
Receive RST packets as ECONNRESET
errors
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
aa29640ed7
commit
d40d452e9d
@ -10,8 +10,6 @@ pub enum BindError {
|
||||
}
|
||||
|
||||
pub mod tcp {
|
||||
pub use smoltcp::socket::tcp::{RecvError, SendError};
|
||||
|
||||
/// An error returned by [`TcpListener::new_listen`].
|
||||
///
|
||||
/// [`TcpListener::new_listen`]: crate::socket::TcpListener::new_listen
|
||||
@ -51,6 +49,44 @@ pub mod tcp {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An error returned by [`TcpConnection::send`].
|
||||
///
|
||||
/// [`TcpConnection::send`]: crate::socket::TcpConnection::send
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum SendError {
|
||||
InvalidState,
|
||||
/// The connection is reset.
|
||||
ConnReset,
|
||||
}
|
||||
|
||||
impl From<smoltcp::socket::tcp::SendError> for SendError {
|
||||
fn from(value: smoltcp::socket::tcp::SendError) -> Self {
|
||||
match value {
|
||||
smoltcp::socket::tcp::SendError::InvalidState => Self::InvalidState,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An error returned by [`TcpConnection::recv`].
|
||||
///
|
||||
/// [`TcpConnection::recv`]: crate::socket::TcpConnection::recv
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum RecvError {
|
||||
InvalidState,
|
||||
Finished,
|
||||
/// The connection is reset.
|
||||
ConnReset,
|
||||
}
|
||||
|
||||
impl From<smoltcp::socket::tcp::RecvError> for RecvError {
|
||||
fn from(value: smoltcp::socket::tcp::RecvError) -> Self {
|
||||
match value {
|
||||
smoltcp::socket::tcp::RecvError::InvalidState => Self::InvalidState,
|
||||
smoltcp::socket::tcp::RecvError::Finished => Self::Finished,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod udp {
|
||||
|
@ -17,7 +17,7 @@ use super::{
|
||||
};
|
||||
use crate::{
|
||||
define_boolean_value,
|
||||
errors::tcp::ConnectError,
|
||||
errors::tcp::{ConnectError, RecvError, SendError},
|
||||
ext::Ext,
|
||||
iface::BoundPort,
|
||||
socket::{
|
||||
@ -42,6 +42,8 @@ pub struct RawTcpSocketExt<E: Ext> {
|
||||
has_connected: bool,
|
||||
/// Indicates if the receiving side of this socket is shut down by the user.
|
||||
is_recv_shut: bool,
|
||||
/// Indicates if the socket is closed by a RST packet.
|
||||
is_rst_closed: bool,
|
||||
}
|
||||
|
||||
impl<E: Ext> Deref for RawTcpSocketExt<E> {
|
||||
@ -92,6 +94,14 @@ impl<E: Ext> RawTcpSocketExt<E> {
|
||||
pub fn is_recv_shut(&self) -> bool {
|
||||
self.is_recv_shut
|
||||
}
|
||||
|
||||
/// Checks if the socket is closed by a RST packet.
|
||||
///
|
||||
/// Note that the flag is automatically cleared when it is read by
|
||||
/// [`TcpConnection::clear_rst_closed`], [`TcpConnection::send`], or [`TcpConnection::recv`].
|
||||
pub fn is_rst_closed(&self) -> bool {
|
||||
self.is_rst_closed
|
||||
}
|
||||
}
|
||||
|
||||
define_boolean_value!(
|
||||
@ -106,6 +116,7 @@ impl<E: Ext> RawTcpSocketExt<E> {
|
||||
this: &Arc<TcpConnectionBg<E>>,
|
||||
old_state: State,
|
||||
old_recv_queue: usize,
|
||||
is_rst: bool,
|
||||
) -> (SocketEvents, TcpConnBecameDead) {
|
||||
let became_dead = if self.state() != State::Established {
|
||||
// After the connection is closed by the user, no new data can be read, and such unread
|
||||
@ -117,6 +128,10 @@ impl<E: Ext> RawTcpSocketExt<E> {
|
||||
&& matches!(old_state, State::FinWait1 | State::FinWait2)
|
||||
&& self.recv_queue() > old_recv_queue
|
||||
{
|
||||
// Strictly speaking, the socket isn't closed by an incoming RST packet in this
|
||||
// situation. Instead, we reset the connection and _send_ an outgoing RST packet.
|
||||
// However, Linux reports `ECONNRESET`, so we have to follow Linux.
|
||||
self.is_rst_closed = true;
|
||||
self.abort();
|
||||
}
|
||||
self.check_dead(this)
|
||||
@ -125,6 +140,9 @@ impl<E: Ext> RawTcpSocketExt<E> {
|
||||
};
|
||||
|
||||
let events = if self.state() != old_state {
|
||||
if self.state() == State::Closed && is_rst {
|
||||
self.is_rst_closed = true;
|
||||
}
|
||||
self.on_new_state(this)
|
||||
} else {
|
||||
SocketEvents::empty()
|
||||
@ -211,6 +229,7 @@ impl<E: Ext> TcpConnectionInner<E> {
|
||||
listener,
|
||||
has_connected: false,
|
||||
is_recv_shut: false,
|
||||
is_rst_closed: false,
|
||||
};
|
||||
|
||||
TcpConnectionInner {
|
||||
@ -344,7 +363,7 @@ impl<E: Ext> TcpConnection<E> {
|
||||
/// Sends some data.
|
||||
///
|
||||
/// Polling the iface _may_ be required after this method succeeds.
|
||||
pub fn send<F, R>(&self, f: F) -> Result<(R, NeedIfacePoll), smoltcp::socket::tcp::SendError>
|
||||
pub fn send<F, R>(&self, f: F) -> Result<(R, NeedIfacePoll), SendError>
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> (usize, R),
|
||||
{
|
||||
@ -353,7 +372,12 @@ impl<E: Ext> TcpConnection<E> {
|
||||
|
||||
let mut socket = self.0.inner.lock();
|
||||
|
||||
if socket.is_rst_closed {
|
||||
socket.is_rst_closed = false;
|
||||
return Err(SendError::ConnReset);
|
||||
}
|
||||
let result = socket.send(f)?;
|
||||
|
||||
let need_poll = self
|
||||
.0
|
||||
.update_next_poll_at_ms(socket.poll_at(iface.context()));
|
||||
@ -364,7 +388,7 @@ impl<E: Ext> TcpConnection<E> {
|
||||
/// Receives some data.
|
||||
///
|
||||
/// Polling the iface _may_ be required after this method succeeds.
|
||||
pub fn recv<F, R>(&self, f: F) -> Result<(R, NeedIfacePoll), smoltcp::socket::tcp::RecvError>
|
||||
pub fn recv<F, R>(&self, f: F) -> Result<(R, NeedIfacePoll), RecvError>
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> (usize, R),
|
||||
{
|
||||
@ -374,10 +398,16 @@ impl<E: Ext> TcpConnection<E> {
|
||||
let mut socket = self.0.inner.lock();
|
||||
|
||||
if socket.is_recv_shut && socket.recv_queue() == 0 {
|
||||
return Err(smoltcp::socket::tcp::RecvError::Finished);
|
||||
return Err(RecvError::Finished);
|
||||
}
|
||||
let result = match socket.recv(f) {
|
||||
Err(_) if socket.is_rst_closed => {
|
||||
socket.is_rst_closed = false;
|
||||
return Err(RecvError::ConnReset);
|
||||
}
|
||||
res => res,
|
||||
}?;
|
||||
|
||||
let result = socket.recv(f)?;
|
||||
let need_poll = self
|
||||
.0
|
||||
.update_next_poll_at_ms(socket.poll_at(iface.context()));
|
||||
@ -385,6 +415,18 @@ impl<E: Ext> TcpConnection<E> {
|
||||
Ok((result, need_poll))
|
||||
}
|
||||
|
||||
/// Checks if the socket is closed by a RST packet and clears the flag.
|
||||
///
|
||||
/// This flag is set when the socket is closed by a RST packet, and cleared when the connection
|
||||
/// reset error is reported via one of the [`Self::send`], [`Self::recv`], or this method.
|
||||
pub fn clear_rst_closed(&self) -> bool {
|
||||
let mut socket = self.0.inner.lock();
|
||||
|
||||
let is_rst = socket.is_rst_closed;
|
||||
socket.is_rst_closed = false;
|
||||
is_rst
|
||||
}
|
||||
|
||||
/// Shuts down the sending half of the connection.
|
||||
///
|
||||
/// This method will return `false` if the socket is in the CLOSED or TIME_WAIT state.
|
||||
@ -534,6 +576,7 @@ impl<E: Ext> TcpConnectionBg<E> {
|
||||
|
||||
let old_state = socket.state();
|
||||
let old_recv_queue = socket.recv_queue();
|
||||
let is_rst = tcp_repr.control == TcpControl::Rst;
|
||||
// For TCP, receiving an ACK packet can free up space in the queue, allowing more packets
|
||||
// to be queued.
|
||||
let mut events = SocketEvents::CAN_RECV | SocketEvents::CAN_SEND;
|
||||
@ -543,7 +586,8 @@ impl<E: Ext> TcpConnectionBg<E> {
|
||||
Some((ip_repr, tcp_repr)) => TcpProcessResult::ProcessedWithReply(ip_repr, tcp_repr),
|
||||
};
|
||||
|
||||
let (state_events, became_dead) = socket.check_state(self, old_state, old_recv_queue);
|
||||
let (state_events, became_dead) =
|
||||
socket.check_state(self, old_state, old_recv_queue, is_rst);
|
||||
events |= state_events;
|
||||
|
||||
self.add_events(events);
|
||||
@ -565,6 +609,7 @@ impl<E: Ext> TcpConnectionBg<E> {
|
||||
|
||||
let old_state = socket.state();
|
||||
let old_recv_queue = socket.recv_queue();
|
||||
let mut is_rst = false;
|
||||
let mut events = SocketEvents::empty();
|
||||
|
||||
let mut reply = None;
|
||||
@ -581,11 +626,13 @@ impl<E: Ext> TcpConnectionBg<E> {
|
||||
if !socket.accepts(cx, ip_repr, tcp_repr) {
|
||||
break;
|
||||
}
|
||||
reply = socket.process(cx, ip_repr, tcp_repr);
|
||||
is_rst |= tcp_repr.control == TcpControl::Rst;
|
||||
events |= SocketEvents::CAN_RECV | SocketEvents::CAN_SEND;
|
||||
reply = socket.process(cx, ip_repr, tcp_repr);
|
||||
}
|
||||
|
||||
let (state_events, became_dead) = socket.check_state(self, old_state, old_recv_queue);
|
||||
let (state_events, became_dead) =
|
||||
socket.check_state(self, old_state, old_recv_queue, is_rst);
|
||||
events |= state_events;
|
||||
|
||||
self.add_events(events);
|
||||
|
Reference in New Issue
Block a user