inet.af/netstack@v0.0.0-20220214151720-7585b01ddccf/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 "inet.af/netstack/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  func (sr *sackRecovery) handleSACKRecovery(limit int, end seqnum.Value) (dataSent bool) {
    34  	snd := sr.s
    35  	snd.SetPipe()
    36  
    37  	if smss := int(snd.ep.scoreboard.SMSS()); limit > smss {
    38  		// Cap segment size limit to s.smss as SACK recovery requires
    39  		// that all retransmissions or new segments send during recovery
    40  		// be of <= SMSS.
    41  		limit = smss
    42  	}
    43  
    44  	nextSegHint := snd.writeList.Front()
    45  	for snd.Outstanding < snd.SndCwnd {
    46  		var nextSeg *segment
    47  		var rescueRtx bool
    48  		nextSeg, nextSegHint, rescueRtx = snd.NextSeg(nextSegHint)
    49  		if nextSeg == nil {
    50  			return dataSent
    51  		}
    52  		if !snd.isAssignedSequenceNumber(nextSeg) || snd.SndNxt.LessThanEq(nextSeg.sequenceNumber) {
    53  			// New data being sent.
    54  
    55  			// Step C.3 described below is handled by
    56  			// maybeSendSegment which increments sndNxt when
    57  			// a segment is transmitted.
    58  			//
    59  			// Step C.3 "If any of the data octets sent in
    60  			// (C.1) are above HighData, HighData must be
    61  			// updated to reflect the transmission of
    62  			// previously unsent data."
    63  			//
    64  			// We pass s.smss as the limit as the Step 2) requires that
    65  			// new data sent should be of size s.smss or less.
    66  			if sent := snd.maybeSendSegment(nextSeg, limit, end); !sent {
    67  				return dataSent
    68  			}
    69  			dataSent = true
    70  			snd.Outstanding++
    71  			snd.writeNext = nextSeg.Next()
    72  			continue
    73  		}
    74  
    75  		// Now handle the retransmission case where we matched either step 1,3 or 4
    76  		// of the NextSeg algorithm.
    77  		// RFC 6675, Step C.4.
    78  		//
    79  		// "The estimate of the amount of data outstanding in the network
    80  		// must be updated by incrementing pipe by the number of octets
    81  		// transmitted in (C.1)."
    82  		snd.Outstanding++
    83  		dataSent = true
    84  		snd.sendSegment(nextSeg)
    85  
    86  		segEnd := nextSeg.sequenceNumber.Add(nextSeg.logicalLen())
    87  		if rescueRtx {
    88  			// We do the last part of rule (4) of NextSeg here to update
    89  			// RescueRxt as until this point we don't know if we are going
    90  			// to use the rescue transmission.
    91  			snd.FastRecovery.RescueRxt = snd.FastRecovery.Last
    92  		} else {
    93  			// RFC 6675, Step C.2
    94  			//
    95  			// "If any of the data octets sent in (C.1) are below
    96  			// HighData, HighRxt MUST be set to the highest sequence
    97  			// number of the retransmitted segment unless NextSeg ()
    98  			// rule (4) was invoked for this retransmission."
    99  			snd.FastRecovery.HighRxt = segEnd - 1
   100  		}
   101  	}
   102  	return dataSent
   103  }
   104  
   105  func (sr *sackRecovery) DoRecovery(rcvdSeg *segment, fastRetransmit bool) {
   106  	snd := sr.s
   107  	if fastRetransmit {
   108  		snd.resendSegment()
   109  	}
   110  
   111  	// We are in fast recovery mode. Ignore the ack if it's out of range.
   112  	if ack := rcvdSeg.ackNumber; !ack.InRange(snd.SndUna, snd.SndNxt+1) {
   113  		return
   114  	}
   115  
   116  	// RFC 6675 recovery algorithm step C 1-5.
   117  	end := snd.SndUna.Add(snd.SndWnd)
   118  	dataSent := sr.handleSACKRecovery(snd.MaxPayloadSize, end)
   119  	snd.postXmit(dataSent, true /* shouldScheduleProbe */)
   120  }