@@ -178,17 +178,10 @@ export class TcpState {
178178 this . sendUnacknowledged = this . initialSendSeqNum ;
179179
180180 // Send a SYN
181- const flags = new Flags ( ) . withSyn ( ) ;
182- const segment = this . newSegment ( this . initialSendSeqNum , 0 ) . withFlags ( flags ) ;
183- if ( ! sendIpPacket ( this . srcHost , this . dstHost , segment ) ) {
184- console . warn (
185- `Device ${ this . srcHost . id } couldn't send SYN to device ${ this . dstHost . id } .` ,
186- ) ;
181+ if ( ! this . sendSynSegment ( ) ) {
187182 return false ;
188183 }
189184
190- this . rttEstimator . startMeasurement ( this . initialSendSeqNum ) ;
191-
192185 // Move to SYN_SENT state
193186 this . state = TcpStateEnum . SYN_SENT ;
194187 return await this . connectionQueue . pop ( ) ;
@@ -308,12 +301,11 @@ export class TcpState {
308301 this . initialRecvSeqNum = segment . sequenceNumber ;
309302 if ( flags . ack ) {
310303 this . sendUnacknowledged = segment . acknowledgementNumber ;
311- }
312- if ( flags . ack ) {
313304 // It's a valid SYN-ACK
314305 // Process the segment normally
315306 this . state = TcpStateEnum . ESTABLISHED ;
316307 this . connectionQueue . push ( true ) ;
308+ this . retransmissionQueue . ack ( segment . acknowledgementNumber ) ;
317309 if ( this . handleSegmentData ( segment ) !== ProcessingResult . SUCCESS ) {
318310 console . debug ( "Segment data processing failed" ) ;
319311 return ProcessingResult . DISCARD ;
@@ -678,6 +670,13 @@ export class TcpState {
678670 segment . withFlags ( new Flags ( ) . withRst ( ) ) ;
679671 sendIpPacket ( this . srcHost , this . dstHost , segment ) ;
680672 break ;
673+ } else if ( result === "SYN" ) {
674+ // Retransmit SYN packet
675+ console . debug ( "[" + this . srcHost . id + "] [TCP] Processing SYN timeout" ) ;
676+ retransmitPromise = this . retransmissionQueue . pop ( ) ;
677+ this . sendSynSegment ( ) ;
678+ this . showTimeoutIcon ( ) ;
679+ continue ;
681680 } else if ( "segment" in result ) {
682681 console . debug ( "[" + this . srcHost . id + "] [TCP] Processing segments" ) ;
683682 receivedSegmentPromise = this . tcpQueue . pop ( ) ;
@@ -715,14 +714,8 @@ export class TcpState {
715714 console . debug ( "[" + this . srcHost . id + "] [TCP] Processing timeout" ) ;
716715 retransmitPromise = this . retransmissionQueue . pop ( ) ;
717716 // Retransmit the segment
718- this . srcHost . showDeviceIconFor (
719- "tcp_timeout" ,
720- "⏰" ,
721- "TCP Timeout" ,
722- 2000 ,
723- Layer . Transport ,
724- ) ;
725717 this . resendPacket ( result . seqNum , result . size ) ;
718+ this . showTimeoutIcon ( ) ;
726719 this . congestionControl . notifyTimeout ( ) ;
727720 continue ;
728721 }
@@ -782,7 +775,28 @@ export class TcpState {
782775 this . tcpQueue . close ( ) ;
783776 }
784777
778+ private sendSynSegment ( ) {
779+ const flags = new Flags ( ) . withSyn ( ) ;
780+ const segment = this . newSegment ( this . initialSendSeqNum , 0 ) . withFlags ( flags ) ;
781+ if ( ! sendIpPacket ( this . srcHost , this . dstHost , segment ) ) {
782+ console . warn (
783+ `Device ${ this . srcHost . id } couldn't send SYN to device ${ this . dstHost . id } .` ,
784+ ) ;
785+ return false ;
786+ }
787+
788+ // Add the SYN segment to the retransmission queue
789+ this . retransmissionQueue . pushSyn ( ) ;
790+
791+ // Reset measurement
792+ this . rttEstimator . restartMeasurement ( this . initialSendSeqNum ) ;
793+ return true ;
794+ }
795+
785796 private resendPacket ( seqNum : number , size : number ) {
797+ if ( seqNum === this . initialSendSeqNum && size === 0 ) {
798+ // This is the initial SYN segment
799+ }
786800 const segment = this . newSegment ( seqNum , this . recvNext ) . withFlags (
787801 new Flags ( ) . withAck ( ) ,
788802 ) ;
@@ -813,6 +827,10 @@ export class TcpState {
813827 if ( ! item ) {
814828 return ;
815829 }
830+ if ( item === "SYN" ) {
831+ console . error ( "SYN segment retransmitted with an established connection" ) ;
832+ return ;
833+ }
816834 // Resend packet
817835 this . resendPacket ( item . seqNum , item . size ) ;
818836 }
@@ -825,9 +843,20 @@ export class TcpState {
825843 const bytesInFlight = this . sendNext - this . sendUnacknowledged ;
826844 return ( windowSize - bytesInFlight ) % u32_MODULUS ;
827845 }
846+
847+ private showTimeoutIcon ( ) {
848+ this . srcHost . showDeviceIconFor (
849+ "tcp_timeout" ,
850+ "⏰" ,
851+ "TCP Timeout" ,
852+ 2000 ,
853+ Layer . Transport ,
854+ ) ;
855+ }
828856}
829857
830- interface RetransmissionQueueItem {
858+ type RetransmissionQueueItem = DataSegment | "SYN" ;
859+ interface DataSegment {
831860 seqNum : number ;
832861 size : number ;
833862}
@@ -845,14 +874,26 @@ class RetransmissionQueue {
845874 this . rttEstimator = rttEstimator ;
846875 }
847876
877+ pushSyn ( ) {
878+ this . itemQueue . unshift ( "SYN" ) ;
879+ this . startTimer ( ) ;
880+ }
881+
848882 push ( seqNum : number , size : number ) {
849883 const item = { seqNum, size } ;
850884 this . itemQueue . push ( item ) ;
851- this . itemQueue . sort ( ( a , b ) => a . seqNum - b . seqNum ) ;
885+ this . itemQueue . sort ( ( a , b ) => {
886+ // SYN segments should always be at the front
887+ if ( a === "SYN" ) {
888+ return - 1 ;
889+ }
890+ if ( b === "SYN" ) {
891+ return 1 ;
892+ }
893+ return a . seqNum - b . seqNum ;
894+ } ) ;
852895
853- if ( ! this . timeoutTick ) {
854- this . startTimer ( ) ;
855- }
896+ this . startTimer ( ) ;
856897 }
857898
858899 /**
@@ -870,6 +911,10 @@ class RetransmissionQueue {
870911
871912 ack ( ackNum : number ) {
872913 this . itemQueue = this . itemQueue . filter ( ( item ) => {
914+ // We treat any valid ACK as a SYN ACK
915+ if ( item === "SYN" ) {
916+ return false ;
917+ }
873918 return ! (
874919 item . seqNum < ackNum ||
875920 ( item . seqNum + item . size ) % u32_MODULUS <= ackNum
@@ -884,9 +929,10 @@ class RetransmissionQueue {
884929 if ( this . itemQueue . length === 0 ) {
885930 return ;
886931 }
887- const firstSegmentItem = this . itemQueue [ 0 ] ;
888- // Remove the segment from the queue
889- this . ack ( firstSegmentItem . seqNum + 1 ) ;
932+ const firstSegmentItem = this . itemQueue . shift ( ) ;
933+ if ( this . itemQueue . length === 0 ) {
934+ this . stopTimer ( ) ;
935+ }
890936 return firstSegmentItem ;
891937 }
892938
@@ -1162,6 +1208,12 @@ class RTTEstimator {
11621208 Ticker . shared . remove ( this . measureTick , this ) ;
11631209 }
11641210
1211+ restartMeasurement ( seqNum : number ) {
1212+ // Restart the measurement for the segment
1213+ this . discardMeasurement ( seqNum ) ;
1214+ this . startMeasurement ( seqNum ) ;
1215+ }
1216+
11651217 private measureTick ( ticker : Ticker ) {
11661218 // Update the current sample's RTT
11671219 // NOTE: we do this to account for the simulation's speed
0 commit comments