github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/tcpip/transport/tcp/sack_recovery.go (about)

     1  // Copyright 2020 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package tcp
    16  
    17  import "github.com/nicocha30/gvisor-ligolo/pkg/tcpip/seqnum"
    18  
    19  // sackRecovery stores the variables related to TCP SACK loss recovery
    20  // algorithm.
    21  //
    22  // +stateify savable
    23  type sackRecovery struct {
    24  	s *sender
    25  }
    26  
    27  func newSACKRecovery(s *sender) *sackRecovery {
    28  	return &sackRecovery{s: s}
    29  }
    30  
    31  // handleSACKRecovery implements the loss recovery phase as described in RFC6675
    32  // section 5, step C.
    33  // +checklocks:sr.s.ep.mu
    34  func (sr *sackRecovery) handleSACKRecovery(limit int, end seqnum.Value) (dataSent bool) {
    35  	snd := sr.s
    36  	snd.SetPipe()
    37  
    38  	if smss := int(snd.ep.scoreboard.SMSS()); limit > smss {
    39  		// Cap segment size limit to s.smss as SACK recovery requires
    40  		// that all retransmissions or new segments send during recovery
    41  		// be of <= SMSS.
    42  		limit = smss
    43  	}
    44  
    45  	nextSegHint := snd.writeList.Front()
    46  	for snd.Outstanding < snd.SndCwnd {
    47  		var nextSeg *segment
    48  		var rescueRtx bool
    49  		nextSeg, nextSegHint, rescueRtx = snd.NextSeg(nextSegHint)
    50  		if nextSeg == nil {
    51  			return dataSent
    52  		}
    53  		if !snd.isAssignedSequenceNumber(nextSeg) || snd.SndNxt.LessThanEq(nextSeg.sequenceNumber) {
    54  			// New data being sent.
    55  
    56  			// Step C.3 described below is handled by
    57  			// maybeSendSegment which increments sndNxt when
    58  			// a segment is transmitted.
    59  			//
    60  			// Step C.3 "If any of the data octets sent in
    61  			// (C.1) are above HighData, HighData must be
    62  			// updated to reflect the transmission of
    63  			// previously unsent data."
    64  			//
    65  			// We pass s.smss as the limit as the Step 2) requires that
    66  			// new data sent should be of size s.smss or less.
    67  			if sent := snd.maybeSendSegment(nextSeg, limit, end); !sent {
    68  				return dataSent
    69  			}
    70  			dataSent = true
    71  			snd.Outstanding++
    72  			snd.updateWriteNext(nextSeg.Next())
    73  			continue
    74  		}
    75  
    76  		// Now handle the retransmission case where we matched either step 1,3 or 4
    77  		// of the NextSeg algorithm.
    78  		// RFC 6675, Step C.4.
    79  		//
    80  		// "The estimate of the amount of data outstanding in the network
    81  		// must be updated by incrementing pipe by the number of octets
    82  		// transmitted in (C.1)."
    83  		snd.Outstanding++
    84  		dataSent = true
    85  		snd.sendSegment(nextSeg)
    86  
    87  		segEnd := nextSeg.sequenceNumber.Add(nextSeg.logicalLen())
    88  		if rescueRtx {
    89  			// We do the last part of rule (4) of NextSeg here to update
    90  			// RescueRxt as until this point we don't know if we are going
    91  			// to use the rescue transmission.
    92  			snd.FastRecovery.RescueRxt = snd.FastRecovery.Last
    93  		} else {
    94  			// RFC 6675, Step C.2
    95  			//
    96  			// "If any of the data octets sent in (C.1) are below
    97  			// HighData, HighRxt MUST be set to the highest sequence
    98  			// number of the retransmitted segment unless NextSeg ()
    99  			// rule (4) was invoked for this retransmission."
   100  			snd.FastRecovery.HighRxt = segEnd - 1
   101  		}
   102  	}
   103  	return dataSent
   104  }
   105  
   106  // +checklocks:sr.s.ep.mu
   107  func (sr *sackRecovery) DoRecovery(rcvdSeg *segment, fastRetransmit bool) {
   108  	snd := sr.s
   109  	if fastRetransmit {
   110  		snd.resendSegment()
   111  	}
   112  
   113  	// We are in fast recovery mode. Ignore the ack if it's out of range.
   114  	if ack := rcvdSeg.ackNumber; !ack.InRange(snd.SndUna, snd.SndNxt+1) {
   115  		return
   116  	}
   117  
   118  	// RFC 6675 recovery algorithm step C 1-5.
   119  	end := snd.SndUna.Add(snd.SndWnd)
   120  	dataSent := sr.handleSACKRecovery(snd.MaxPayloadSize, end)
   121  	snd.postXmit(dataSent, true /* shouldScheduleProbe */)
   122  }