| // Copyright 2020 The gVisor Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package tcp |
| |
| import "gvisor.dev/gvisor/pkg/tcpip/seqnum" |
| |
| // sackRecovery stores the variables related to TCP SACK loss recovery |
| // algorithm. |
| // |
| // +stateify savable |
| type sackRecovery struct { |
| s *sender |
| } |
| |
| func newSACKRecovery(s *sender) *sackRecovery { |
| return &sackRecovery{s: s} |
| } |
| |
| // handleSACKRecovery implements the loss recovery phase as described in RFC6675 |
| // section 5, step C. |
| func (sr *sackRecovery) handleSACKRecovery(limit int, end seqnum.Value) (dataSent bool) { |
| snd := sr.s |
| snd.SetPipe() |
| |
| if smss := int(snd.ep.scoreboard.SMSS()); limit > smss { |
| // Cap segment size limit to s.smss as SACK recovery requires |
| // that all retransmissions or new segments send during recovery |
| // be of <= SMSS. |
| limit = smss |
| } |
| |
| nextSegHint := snd.writeList.Front() |
| for snd.outstanding < snd.sndCwnd { |
| var nextSeg *segment |
| var rescueRtx bool |
| nextSeg, nextSegHint, rescueRtx = snd.NextSeg(nextSegHint) |
| if nextSeg == nil { |
| return dataSent |
| } |
| if !snd.isAssignedSequenceNumber(nextSeg) || snd.sndNxt.LessThanEq(nextSeg.sequenceNumber) { |
| // New data being sent. |
| |
| // Step C.3 described below is handled by |
| // maybeSendSegment which increments sndNxt when |
| // a segment is transmitted. |
| // |
| // Step C.3 "If any of the data octets sent in |
| // (C.1) are above HighData, HighData must be |
| // updated to reflect the transmission of |
| // previously unsent data." |
| // |
| // We pass s.smss as the limit as the Step 2) requires that |
| // new data sent should be of size s.smss or less. |
| if sent := snd.maybeSendSegment(nextSeg, limit, end); !sent { |
| return dataSent |
| } |
| dataSent = true |
| snd.outstanding++ |
| snd.writeNext = nextSeg.Next() |
| continue |
| } |
| |
| // Now handle the retransmission case where we matched either step 1,3 or 4 |
| // of the NextSeg algorithm. |
| // RFC 6675, Step C.4. |
| // |
| // "The estimate of the amount of data outstanding in the network |
| // must be updated by incrementing pipe by the number of octets |
| // transmitted in (C.1)." |
| snd.outstanding++ |
| dataSent = true |
| snd.sendSegment(nextSeg) |
| |
| segEnd := nextSeg.sequenceNumber.Add(nextSeg.logicalLen()) |
| if rescueRtx { |
| // We do the last part of rule (4) of NextSeg here to update |
| // RescueRxt as until this point we don't know if we are going |
| // to use the rescue transmission. |
| snd.fr.rescueRxt = snd.fr.last |
| } else { |
| // RFC 6675, Step C.2 |
| // |
| // "If any of the data octets sent in (C.1) are below |
| // HighData, HighRxt MUST be set to the highest sequence |
| // number of the retransmitted segment unless NextSeg () |
| // rule (4) was invoked for this retransmission." |
| snd.fr.highRxt = segEnd - 1 |
| } |
| } |
| return dataSent |
| } |
| |
| func (sr *sackRecovery) DoRecovery(rcvdSeg *segment, fastRetransmit bool) { |
| snd := sr.s |
| if fastRetransmit { |
| snd.resendSegment() |
| } |
| |
| // We are in fast recovery mode. Ignore the ack if it's out of range. |
| if ack := rcvdSeg.ackNumber; !ack.InRange(snd.sndUna, snd.sndNxt+1) { |
| return |
| } |
| |
| // RFC 6675 recovery algorithm step C 1-5. |
| end := snd.sndUna.Add(snd.sndWnd) |
| dataSent := sr.handleSACKRecovery(snd.maxPayloadSize, end) |
| snd.postXmit(dataSent, true /* shouldScheduleProbe */) |
| } |