@@ -7,7 +7,7 @@ use crate::{
77 tcp_flags:: { ACK , FIN , PSH , RST , SYN } ,
88 tcp_header_flags, tcp_header_fmt,
99 } ,
10- stream:: tcb:: { MAX_COUNT_FOR_DUP_ACK , MAX_RETRANSMIT_COUNT , MAX_UNACK , PacketType , READ_BUFFER_SIZE , RTO , Tcb , TcpState } ,
10+ stream:: tcb:: { MAX_COUNT_FOR_DUP_ACK , MAX_RETRANSMIT_COUNT , MAX_UNACK , PacketType , READ_BUFFER_SIZE , READ_CHUNK , RTO , Tcb , TcpState } ,
1111} ;
1212use etherparse:: { IpNumber , Ipv4Header , Ipv6FlowLabel , TcpHeader , TcpOptionElement } ;
1313use std:: {
@@ -165,9 +165,10 @@ pub struct IpStackTcpStream {
165165 write_notify : std:: sync:: Arc < std:: sync:: Mutex < Option < Waker > > > ,
166166 destroy_messenger : Option < :: tokio:: sync:: oneshot:: Sender < ( ) > > ,
167167 timeout : Pin < Box < tokio:: time:: Sleep > > ,
168- data_tx : tokio:: sync:: mpsc:: UnboundedSender < Vec < u8 > > ,
169- data_rx : tokio:: sync:: mpsc:: UnboundedReceiver < Vec < u8 > > ,
168+ data_tx : tokio:: sync:: mpsc:: Sender < Vec < u8 > > ,
169+ data_rx : tokio:: sync:: mpsc:: Receiver < Vec < u8 > > ,
170170 read_notify : std:: sync:: Arc < std:: sync:: Mutex < Option < Waker > > > ,
171+ drain_notify : Arc < tokio:: sync:: Notify > ,
171172 task_handle : Option < tokio:: task:: JoinHandle < std:: io:: Result < ( ) > > > ,
172173 exit_notifier : Option < tokio:: sync:: mpsc:: Sender < ( ) > > ,
173174 temp_read_buffer : Vec < u8 > ,
@@ -205,7 +206,8 @@ impl IpStackTcpStream {
205206 }
206207
207208 let ( stream_sender, stream_receiver) = tokio:: sync:: mpsc:: unbounded_channel :: < NetworkPacket > ( ) ;
208- let ( data_tx, data_rx) = tokio:: sync:: mpsc:: unbounded_channel :: < Vec < u8 > > ( ) ;
209+ let data_channel_len = config. read_buffer_size . div_ceil ( READ_CHUNK ) . max ( 1 ) ;
210+ let ( data_tx, data_rx) = tokio:: sync:: mpsc:: channel :: < Vec < u8 > > ( data_channel_len) ;
209211 let deadline = tokio:: time:: Instant :: now ( ) + config. timeout ;
210212
211213 let mut stream = IpStackTcpStream {
@@ -222,6 +224,7 @@ impl IpStackTcpStream {
222224 data_tx,
223225 data_rx,
224226 read_notify : std:: sync:: Arc :: new ( std:: sync:: Mutex :: new ( None ) ) ,
227+ drain_notify : Arc :: new ( tokio:: sync:: Notify :: new ( ) ) ,
225228 task_handle : None ,
226229 exit_notifier : None ,
227230 temp_read_buffer : Vec :: new ( ) ,
@@ -330,6 +333,8 @@ impl AsyncRead for IpStackTcpStream {
330333 buf. put_slice ( & data[ ..capacity] ) ;
331334 self . temp_read_buffer . extend_from_slice ( & data[ capacity..] ) ;
332335 }
336+ // A channel slot just freed, so wake the loop to flush more and reopen the window.
337+ self . drain_notify . notify_one ( ) ;
333338 Poll :: Ready ( Ok ( ( ) ) )
334339 }
335340 Poll :: Ready ( None ) => Poll :: Ready ( Ok ( ( ) ) ) ,
@@ -471,6 +476,7 @@ impl IpStackTcpStream {
471476 let write_notify = self . write_notify . clone ( ) ;
472477 let read_notify = self . read_notify . clone ( ) ;
473478 let data_tx = self . data_tx . clone ( ) ;
479+ let drain_notify = self . drain_notify . clone ( ) ;
474480 let destroy_messenger = self . destroy_messenger . take ( ) ;
475481
476482 let ( exit_task_notifier, exit_monitor) = tokio:: sync:: mpsc:: channel :: < ( ) > ( 10 ) ;
@@ -489,6 +495,7 @@ impl IpStackTcpStream {
489495 write_notify,
490496 read_notify,
491497 data_tx,
498+ drain_notify,
492499 exit_monitor,
493500 )
494501 . await ;
@@ -516,7 +523,8 @@ async fn tcp_main_logic_loop(
516523 network_tuple : NetworkTuple ,
517524 write_notify : std:: sync:: Arc < std:: sync:: Mutex < Option < Waker > > > ,
518525 read_notify : std:: sync:: Arc < std:: sync:: Mutex < Option < Waker > > > ,
519- data_tx : tokio:: sync:: mpsc:: UnboundedSender < Vec < u8 > > ,
526+ data_tx : tokio:: sync:: mpsc:: Sender < Vec < u8 > > ,
527+ drain_notify : Arc < tokio:: sync:: Notify > ,
520528 mut exit_monitor : tokio:: sync:: mpsc:: Receiver < ( ) > ,
521529) -> std:: io:: Result < ( ) > {
522530 {
@@ -642,6 +650,13 @@ async fn tcp_main_logic_loop(
642650 log:: debug!( "{network_tuple} task exited due to exit signal" ) ;
643651 break ;
644652 }
653+ _ = drain_notify. notified( ) => {
654+ // The upstream reader freed channel space, so flush whatever is buffered and
655+ // let the follow-up ACK carry the reopened window.
656+ let mut tcb = tcb. lock( ) . unwrap( ) ;
657+ extract_data_n_write_upstream( & up_packet_sender, & mut tcb, network_tuple, & data_tx, & read_notify) ?;
658+ continue ;
659+ }
645660 network_packet = stream_receiver. recv( ) => network_packet,
646661 } ;
647662
@@ -743,7 +758,7 @@ async fn tcp_main_logic_loop(
743758 }
744759 PacketType :: Invalid => { }
745760 }
746- } else if flags == ( ACK | FIN ) {
761+ } else if flags == ( ACK | FIN ) && tcb . get_ack ( ) == incoming_seq {
747762 // The other side is closing the connection, we need to send an ACK and change state to CloseWait
748763 tcb. increase_ack ( ) ;
749764 write_packet_to_device ( & up_packet_sender, network_tuple, & tcb, None , ACK , None , None ) ?;
@@ -838,7 +853,7 @@ async fn tcp_main_logic_loop(
838853 log:: trace!( "{network_tuple} {state:?}: Received final ACK, transitioned to {new_state:?}" ) ;
839854 }
840855 TcpState :: FinWait1 => {
841- if flags & ( ACK | FIN ) == ( ACK | FIN ) && len == 0 {
856+ if flags & ( ACK | FIN ) == ( ACK | FIN ) && len == 0 && tcb . get_ack ( ) == incoming_seq {
842857 // If the received packet is an ACK with FIN, we need to send an ACK and change state to TimeWait directly, not to FinWait2
843858 tcb. increase_ack ( ) ;
844859 write_packet_to_device ( & up_packet_sender, network_tuple, & tcb, None , ACK , None , None ) ?;
@@ -863,7 +878,7 @@ async fn tcp_main_logic_loop(
863878 }
864879 }
865880 TcpState :: FinWait2 => {
866- if flags & ( ACK | FIN ) == ( ACK | FIN ) && len == 0 {
881+ if flags & ( ACK | FIN ) == ( ACK | FIN ) && len == 0 && tcb . get_ack ( ) == incoming_seq {
867882 tcb. increase_ack ( ) ;
868883 write_packet_to_device ( & up_packet_sender, network_tuple, & tcb, None , ACK , None , None ) ?;
869884 tcb. change_state ( TcpState :: TimeWait ) ;
@@ -914,7 +929,7 @@ fn extract_data_n_write_upstream(
914929 up_packet_sender : & PacketSender ,
915930 tcb : & mut Tcb ,
916931 network_tuple : NetworkTuple ,
917- data_tx : & tokio:: sync:: mpsc:: UnboundedSender < Vec < u8 > > ,
932+ data_tx : & tokio:: sync:: mpsc:: Sender < Vec < u8 > > ,
918933 read_notify : & std:: sync:: Arc < std:: sync:: Mutex < Option < Waker > > > ,
919934) -> std:: io:: Result < ( ) > {
920935 let ( state, seq, ack) = ( tcb. get_state ( ) , tcb. get_seq ( ) , tcb. get_ack ( ) ) ;
@@ -924,10 +939,23 @@ fn extract_data_n_write_upstream(
924939 return Ok ( ( ) ) ;
925940 }
926941
927- if let Some ( data) = tcb. consume_unordered_packets ( 8192 ) {
942+ // Reserve the handoff slot before consuming, so buffered data is removed only once it has a
943+ // guaranteed home; the reserved permit shrinks the advertised window until the reader drains it.
944+ let permit = match data_tx. try_reserve ( ) {
945+ Ok ( permit) => permit,
946+ Err ( tokio:: sync:: mpsc:: error:: TrySendError :: Full ( ( ) ) ) => {
947+ write_packet_to_device ( up_packet_sender, network_tuple, tcb, None , ACK , None , None ) ?;
948+ return Ok ( ( ) ) ;
949+ }
950+ Err ( tokio:: sync:: mpsc:: error:: TrySendError :: Closed ( ( ) ) ) => {
951+ return Err ( std:: io:: Error :: new ( BrokenPipe , "data channel closed" ) ) ;
952+ }
953+ } ;
954+
955+ if let Some ( data) = tcb. consume_unordered_packets ( READ_CHUNK ) {
928956 let hint = if state == TcpState :: Established { "normally" } else { "still" } ;
929957 log:: trace!( "{network_tuple} {state:?}: {l_info} {hint} receiving data, len = {}" , data. len( ) ) ;
930- data_tx . send ( data) . map_err ( |e| std :: io :: Error :: new ( BrokenPipe , e ) ) ? ;
958+ permit . send ( data) ;
931959 read_notify. lock ( ) . unwrap ( ) . take ( ) . map ( |w| w. wake_by_ref ( ) ) . unwrap_or ( ( ) ) ;
932960 write_packet_to_device ( up_packet_sender, network_tuple, tcb, None , ACK , None , None ) ?;
933961 }
@@ -947,7 +975,11 @@ pub(crate) fn write_packet_to_device(
947975) -> std:: io:: Result < usize > {
948976 use std:: io:: Error ;
949977 let seq = seq. unwrap_or ( tcb. get_seq ( ) ) . 0 ;
950- let ( ack, window_size) = ( tcb. get_ack ( ) . 0 , tcb. get_recv_window ( ) . max ( tcb. get_mtu ( ) ) ) ;
978+ // Silly-window-syndrome avoidance: advertise a real window only when a full segment fits,
979+ // otherwise advertise zero so the peer enters persist mode until the reader frees space.
980+ let recv_window = tcb. get_recv_window ( ) ;
981+ let window_size = if recv_window >= tcb. get_mtu ( ) { recv_window } else { 0 } ;
982+ let ack = tcb. get_ack ( ) . 0 ;
951983 let ( src, dst) = ( tuple. dst , tuple. src ) ; // Note: The address is reversed here
952984 let calc = |ip_header_len : usize , tcp_header_len : usize | tcb. calculate_payload_max_len ( ip_header_len, tcp_header_len) ;
953985 let packet = create_raw_packet (
@@ -1048,3 +1080,47 @@ pub(crate) fn create_raw_packet(
10481080 payload : Some ( payload) ,
10491081 } )
10501082}
1083+
1084+ #[ cfg( test) ]
1085+ mod tests {
1086+ use super :: * ;
1087+ use crate :: stream:: tcb:: { MAX_COUNT_FOR_DUP_ACK , MAX_RETRANSMIT_COUNT , MAX_UNACK , READ_BUFFER_SIZE , RTO } ;
1088+
1089+ #[ tokio:: test]
1090+ async fn extract_reserves_before_consuming ( ) {
1091+ let ( up_tx, _up_rx) = tokio:: sync:: mpsc:: unbounded_channel :: < NetworkPacket > ( ) ;
1092+ let ( data_tx, mut data_rx) = tokio:: sync:: mpsc:: channel :: < Vec < u8 > > ( 1 ) ;
1093+ let read_notify = std:: sync:: Arc :: new ( std:: sync:: Mutex :: new ( None ) ) ;
1094+ let nt = NetworkTuple :: new ( "1.1.1.1:1" . parse ( ) . unwrap ( ) , "2.2.2.2:2" . parse ( ) . unwrap ( ) , true ) ;
1095+
1096+ let mut tcb = Tcb :: new (
1097+ SeqNum ( 1000 ) ,
1098+ 1500 ,
1099+ MAX_UNACK ,
1100+ READ_BUFFER_SIZE ,
1101+ MAX_COUNT_FOR_DUP_ACK ,
1102+ RTO ,
1103+ MAX_RETRANSMIT_COUNT ,
1104+ ) ;
1105+ tcb. change_state ( TcpState :: Established ) ;
1106+ tcb. add_unordered_packet ( SeqNum ( 1000 ) , vec ! [ 1 ; 500 ] ) ;
1107+ tcb. add_unordered_packet ( SeqNum ( 1500 ) , vec ! [ 2 ; 500 ] ) ;
1108+
1109+ // first extract fills the single channel slot and advances ack over the first chunk
1110+ extract_data_n_write_upstream ( & up_tx, & mut tcb, nt, & data_tx, & read_notify) . unwrap ( ) ;
1111+ assert_eq ! ( tcb. get_ack( ) , SeqNum ( 2000 ) ) ;
1112+
1113+ // channel is full: extract leaves the remaining data in the map and does not advance ack
1114+ tcb. add_unordered_packet ( SeqNum ( 2000 ) , vec ! [ 3 ; 500 ] ) ;
1115+ extract_data_n_write_upstream ( & up_tx, & mut tcb, nt, & data_tx, & read_notify) . unwrap ( ) ;
1116+ assert_eq ! ( tcb. get_ack( ) , SeqNum ( 2000 ) ) ;
1117+ assert_eq ! ( tcb. get_unordered_packets_total_len( ) , 500 ) ;
1118+
1119+ // draining the reader frees a slot, and the next extract flushes the tail
1120+ let first = data_rx. recv ( ) . await . unwrap ( ) ;
1121+ assert_eq ! ( first. len( ) , 1000 ) ;
1122+ extract_data_n_write_upstream ( & up_tx, & mut tcb, nt, & data_tx, & read_notify) . unwrap ( ) ;
1123+ assert_eq ! ( tcb. get_ack( ) , SeqNum ( 2500 ) ) ;
1124+ assert_eq ! ( tcb. get_unordered_packets_total_len( ) , 0 ) ;
1125+ }
1126+ }
0 commit comments