github.com/MerlinKodo/gvisor@v0.0.0-20231110090155-957f62ecf90e/pkg/tcpip/transport/tcp/sack.go (about) 1 // Copyright 2018 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 ( 18 "github.com/MerlinKodo/gvisor/pkg/tcpip/header" 19 "github.com/MerlinKodo/gvisor/pkg/tcpip/seqnum" 20 ) 21 22 const ( 23 // MaxSACKBlocks is the maximum number of SACK blocks stored 24 // at receiver side. 25 MaxSACKBlocks = 6 26 ) 27 28 // UpdateSACKBlocks updates the list of SACK blocks to include the segment 29 // specified by segStart->segEnd. If the segment happens to be an out of order 30 // delivery then the first block in the sack.blocks always includes the 31 // segment identified by segStart->segEnd. 32 func UpdateSACKBlocks(sack *SACKInfo, segStart seqnum.Value, segEnd seqnum.Value, rcvNxt seqnum.Value) { 33 newSB := header.SACKBlock{Start: segStart, End: segEnd} 34 35 // Ignore any invalid SACK blocks or blocks that are before rcvNxt as 36 // those bytes have already been acked. 37 if newSB.End.LessThanEq(newSB.Start) || newSB.End.LessThan(rcvNxt) { 38 return 39 } 40 41 if sack.NumBlocks == 0 { 42 sack.Blocks[0] = newSB 43 sack.NumBlocks = 1 44 return 45 } 46 var n = 0 47 for i := 0; i < sack.NumBlocks; i++ { 48 start, end := sack.Blocks[i].Start, sack.Blocks[i].End 49 if end.LessThanEq(rcvNxt) { 50 // Discard any sack blocks that are before rcvNxt as 51 // those have already been acked. 52 continue 53 } 54 if newSB.Start.LessThanEq(end) && start.LessThanEq(newSB.End) { 55 // Merge this SACK block into newSB and discard this SACK 56 // block. 57 if start.LessThan(newSB.Start) { 58 newSB.Start = start 59 } 60 if newSB.End.LessThan(end) { 61 newSB.End = end 62 } 63 } else { 64 // Save this block. 65 sack.Blocks[n] = sack.Blocks[i] 66 n++ 67 } 68 } 69 if rcvNxt.LessThan(newSB.Start) { 70 // If this was an out of order segment then make sure that the 71 // first SACK block is the one that includes the segment. 72 // 73 // See the first bullet point in 74 // https://tools.ietf.org/html/rfc2018#section-4 75 if n == MaxSACKBlocks { 76 // If the number of SACK blocks is equal to 77 // MaxSACKBlocks then discard the last SACK block. 78 n-- 79 } 80 for i := n - 1; i >= 0; i-- { 81 sack.Blocks[i+1] = sack.Blocks[i] 82 } 83 sack.Blocks[0] = newSB 84 n++ 85 } 86 sack.NumBlocks = n 87 } 88 89 // TrimSACKBlockList updates the sack block list by removing/modifying any block 90 // where start is < rcvNxt. 91 func TrimSACKBlockList(sack *SACKInfo, rcvNxt seqnum.Value) { 92 n := 0 93 for i := 0; i < sack.NumBlocks; i++ { 94 if sack.Blocks[i].End.LessThanEq(rcvNxt) { 95 continue 96 } 97 if sack.Blocks[i].Start.LessThan(rcvNxt) { 98 // Shrink this SACK block. 99 sack.Blocks[i].Start = rcvNxt 100 } 101 sack.Blocks[n] = sack.Blocks[i] 102 n++ 103 } 104 sack.NumBlocks = n 105 }