github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/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/SagerNet/gvisor/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 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 }