mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
RACK: Detect DSACK
Detect if the ACK is a duplicate and update in RACK. PiperOrigin-RevId: 342332569
This commit is contained in:
committed by
gVisor bot
parent
6c0f53002a
commit
839dd97008
@@ -82,6 +82,7 @@ type TCPRACKState struct {
|
||||
FACK seqnum.Value
|
||||
RTT time.Duration
|
||||
Reord bool
|
||||
DSACKSeen bool
|
||||
}
|
||||
|
||||
// TCPEndpointID is the unique 4 tuple that identifies a given endpoint.
|
||||
|
||||
@@ -3051,6 +3051,7 @@ func (e *endpoint) completeState() stack.TCPEndpointState {
|
||||
FACK: rc.fack,
|
||||
RTT: rc.rtt,
|
||||
Reord: rc.reorderSeen,
|
||||
DSACKSeen: rc.dsackSeen,
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -29,12 +29,12 @@ import (
|
||||
//
|
||||
// +stateify savable
|
||||
type rackControl struct {
|
||||
// dsackSeen indicates if the connection has seen a DSACK.
|
||||
dsackSeen bool
|
||||
|
||||
// endSequence is the ending TCP sequence number of rackControl.seg.
|
||||
endSequence seqnum.Value
|
||||
|
||||
// dsack indicates if the connection has seen a DSACK.
|
||||
dsack bool
|
||||
|
||||
// fack is the highest selectively or cumulatively acknowledged
|
||||
// sequence.
|
||||
fack seqnum.Value
|
||||
@@ -122,3 +122,8 @@ func (rc *rackControl) detectReorder(seg *segment) {
|
||||
rc.reorderSeen = true
|
||||
}
|
||||
}
|
||||
|
||||
// setDSACKSeen updates rack control if duplicate SACK is seen by the connection.
|
||||
func (rc *rackControl) setDSACKSeen() {
|
||||
rc.dsackSeen = true
|
||||
}
|
||||
|
||||
@@ -1182,25 +1182,29 @@ func (s *sender) detectLoss(seg *segment) (fastRetransmit bool) {
|
||||
// See: https://tools.ietf.org/html/draft-ietf-tcpm-rack-08#section-7.2
|
||||
// steps 2 and 3.
|
||||
func (s *sender) walkSACK(rcvdSeg *segment) {
|
||||
if len(rcvdSeg.parsedOptions.SACKBlocks) == 0 {
|
||||
// Look for DSACK block.
|
||||
idx := 0
|
||||
n := len(rcvdSeg.parsedOptions.SACKBlocks)
|
||||
if s.checkDSACK(rcvdSeg) {
|
||||
s.rc.setDSACKSeen()
|
||||
idx = 1
|
||||
n--
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Sort the SACK blocks. The first block is the most recent unacked
|
||||
// block. The following blocks can be in arbitrary order.
|
||||
sackBlocks := make([]header.SACKBlock, len(rcvdSeg.parsedOptions.SACKBlocks))
|
||||
copy(sackBlocks, rcvdSeg.parsedOptions.SACKBlocks)
|
||||
sackBlocks := make([]header.SACKBlock, n)
|
||||
copy(sackBlocks, rcvdSeg.parsedOptions.SACKBlocks[idx:])
|
||||
sort.Slice(sackBlocks, func(i, j int) bool {
|
||||
return sackBlocks[j].Start.LessThan(sackBlocks[i].Start)
|
||||
})
|
||||
|
||||
seg := s.writeList.Front()
|
||||
for _, sb := range sackBlocks {
|
||||
// This check excludes DSACK blocks.
|
||||
if sb.Start.LessThanEq(rcvdSeg.ackNumber) || sb.Start.LessThanEq(s.sndUna) || s.sndNxt.LessThan(sb.End) {
|
||||
continue
|
||||
}
|
||||
|
||||
for seg != nil && seg.sequenceNumber.LessThan(sb.End) && seg.xmitCount != 0 {
|
||||
if sb.Start.LessThanEq(seg.sequenceNumber) && !seg.acked {
|
||||
s.rc.update(seg, rcvdSeg, s.ep.tsOffset)
|
||||
@@ -1212,6 +1216,50 @@ func (s *sender) walkSACK(rcvdSeg *segment) {
|
||||
}
|
||||
}
|
||||
|
||||
// checkDSACK checks if a DSACK is reported and updates it in RACK.
|
||||
func (s *sender) checkDSACK(rcvdSeg *segment) bool {
|
||||
n := len(rcvdSeg.parsedOptions.SACKBlocks)
|
||||
if n == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
sb := rcvdSeg.parsedOptions.SACKBlocks[0]
|
||||
// Check if SACK block is invalid.
|
||||
if sb.End.LessThan(sb.Start) {
|
||||
return false
|
||||
}
|
||||
|
||||
// See: https://tools.ietf.org/html/rfc2883#section-5 DSACK is sent in
|
||||
// at most one SACK block. DSACK is detected in the below two cases:
|
||||
// * If the SACK sequence space is less than this cumulative ACK, it is
|
||||
// an indication that the segment identified by the SACK block has
|
||||
// been received more than once by the receiver.
|
||||
// * If the sequence space in the first SACK block is greater than the
|
||||
// cumulative ACK, then the sender next compares the sequence space
|
||||
// in the first SACK block with the sequence space in the second SACK
|
||||
// block, if there is one. This comparison can determine if the first
|
||||
// SACK block is reporting duplicate data that lies above the
|
||||
// cumulative ACK.
|
||||
if sb.Start.LessThan(rcvdSeg.ackNumber) {
|
||||
return true
|
||||
}
|
||||
|
||||
if n > 1 {
|
||||
sb1 := rcvdSeg.parsedOptions.SACKBlocks[1]
|
||||
if sb1.End.LessThan(sb1.Start) {
|
||||
return false
|
||||
}
|
||||
|
||||
// If the first SACK block is fully covered by second SACK
|
||||
// block, then the first block is a DSACK block.
|
||||
if sb.End.LessThanEq(sb1.End) && sb1.Start.LessThanEq(sb.Start) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// handleRcvdSegment is called when a segment is received; it is responsible for
|
||||
// updating the send-related state.
|
||||
func (s *sender) handleRcvdSegment(rcvdSeg *segment) {
|
||||
|
||||
@@ -30,15 +30,17 @@ const (
|
||||
maxPayload = 10
|
||||
tsOptionSize = 12
|
||||
maxTCPOptionSize = 40
|
||||
mtu = header.TCPMinimumSize + header.IPv4MinimumSize + maxTCPOptionSize + maxPayload
|
||||
)
|
||||
|
||||
// TestRACKUpdate tests the RACK related fields are updated when an ACK is
|
||||
// received on a SACK enabled connection.
|
||||
func TestRACKUpdate(t *testing.T) {
|
||||
c := context.New(t, uint32(header.TCPMinimumSize+header.IPv4MinimumSize+maxTCPOptionSize+maxPayload))
|
||||
c := context.New(t, uint32(mtu))
|
||||
defer c.Cleanup()
|
||||
|
||||
var xmitTime time.Time
|
||||
probeDone := make(chan struct{})
|
||||
c.Stack().AddTCPProbe(func(state stack.TCPEndpointState) {
|
||||
// Validate that the endpoint Sender.RACKState is what we expect.
|
||||
if state.Sender.RACKState.XmitTime.Before(xmitTime) {
|
||||
@@ -54,6 +56,7 @@ func TestRACKUpdate(t *testing.T) {
|
||||
if state.Sender.RACKState.RTT == 0 {
|
||||
t.Fatalf("RACK RTT failed to update when an ACK is received, got RACKState.RTT == 0 want != 0")
|
||||
}
|
||||
close(probeDone)
|
||||
})
|
||||
setStackSACKPermitted(t, c, true)
|
||||
createConnectedWithSACKAndTS(c)
|
||||
@@ -73,18 +76,20 @@ func TestRACKUpdate(t *testing.T) {
|
||||
c.ReceiveAndCheckPacketWithOptions(data, bytesRead, maxPayload, tsOptionSize)
|
||||
bytesRead += maxPayload
|
||||
c.SendAck(seqnum.Value(context.TestInitialSequenceNumber).Add(1), bytesRead)
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
// Wait for the probe function to finish processing the ACK before the
|
||||
// test completes.
|
||||
<-probeDone
|
||||
}
|
||||
|
||||
// TestRACKDetectReorder tests that RACK detects packet reordering.
|
||||
func TestRACKDetectReorder(t *testing.T) {
|
||||
c := context.New(t, uint32(header.TCPMinimumSize+header.IPv4MinimumSize+maxTCPOptionSize+maxPayload))
|
||||
c := context.New(t, uint32(mtu))
|
||||
defer c.Cleanup()
|
||||
|
||||
const ackNum = 2
|
||||
|
||||
var n int
|
||||
ch := make(chan struct{})
|
||||
const ackNumToVerify = 2
|
||||
probeDone := make(chan struct{})
|
||||
c.Stack().AddTCPProbe(func(state stack.TCPEndpointState) {
|
||||
gotSeq := state.Sender.RACKState.FACK
|
||||
wantSeq := state.Sender.SndNxt
|
||||
@@ -95,7 +100,7 @@ func TestRACKDetectReorder(t *testing.T) {
|
||||
}
|
||||
|
||||
n++
|
||||
if n < ackNum {
|
||||
if n < ackNumToVerify {
|
||||
if state.Sender.RACKState.Reord {
|
||||
t.Fatalf("RACK reorder detected when there is no reordering")
|
||||
}
|
||||
@@ -105,11 +110,11 @@ func TestRACKDetectReorder(t *testing.T) {
|
||||
if state.Sender.RACKState.Reord == false {
|
||||
t.Fatalf("RACK reorder detection failed")
|
||||
}
|
||||
close(ch)
|
||||
close(probeDone)
|
||||
})
|
||||
setStackSACKPermitted(t, c, true)
|
||||
createConnectedWithSACKAndTS(c)
|
||||
data := buffer.NewView(ackNum * maxPayload)
|
||||
data := buffer.NewView(ackNumToVerify * maxPayload)
|
||||
for i := range data {
|
||||
data[i] = byte(i)
|
||||
}
|
||||
@@ -120,7 +125,7 @@ func TestRACKDetectReorder(t *testing.T) {
|
||||
}
|
||||
|
||||
bytesRead := 0
|
||||
for i := 0; i < ackNum; i++ {
|
||||
for i := 0; i < ackNumToVerify; i++ {
|
||||
c.ReceiveAndCheckPacketWithOptions(data, bytesRead, maxPayload, tsOptionSize)
|
||||
bytesRead += maxPayload
|
||||
}
|
||||
@@ -133,5 +138,393 @@ func TestRACKDetectReorder(t *testing.T) {
|
||||
|
||||
// Wait for the probe function to finish processing the ACK before the
|
||||
// test completes.
|
||||
<-ch
|
||||
<-probeDone
|
||||
}
|
||||
|
||||
func sendAndReceive(t *testing.T, c *context.Context, numPackets int) buffer.View {
|
||||
setStackSACKPermitted(t, c, true)
|
||||
createConnectedWithSACKAndTS(c)
|
||||
|
||||
data := buffer.NewView(numPackets * maxPayload)
|
||||
for i := range data {
|
||||
data[i] = byte(i)
|
||||
}
|
||||
|
||||
// Write the data.
|
||||
if _, _, err := c.EP.Write(tcpip.SlicePayload(data), tcpip.WriteOptions{}); err != nil {
|
||||
t.Fatalf("Write failed: %s", err)
|
||||
}
|
||||
|
||||
bytesRead := 0
|
||||
for i := 0; i < numPackets; i++ {
|
||||
c.ReceiveAndCheckPacketWithOptions(data, bytesRead, maxPayload, tsOptionSize)
|
||||
bytesRead += maxPayload
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
const (
|
||||
validDSACKDetected = 1
|
||||
failedToDetectDSACK = 2
|
||||
invalidDSACKDetected = 3
|
||||
)
|
||||
|
||||
func addDSACKSeenCheckerProbe(t *testing.T, c *context.Context, numACK int, probeDone chan int) {
|
||||
var n int
|
||||
c.Stack().AddTCPProbe(func(state stack.TCPEndpointState) {
|
||||
// Validate that RACK detects DSACK.
|
||||
n++
|
||||
if n < numACK {
|
||||
if state.Sender.RACKState.DSACKSeen {
|
||||
probeDone <- invalidDSACKDetected
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if !state.Sender.RACKState.DSACKSeen {
|
||||
probeDone <- failedToDetectDSACK
|
||||
return
|
||||
}
|
||||
probeDone <- validDSACKDetected
|
||||
})
|
||||
}
|
||||
|
||||
// TestRACKDetectDSACK tests that RACK detects DSACK with duplicate segments.
|
||||
// See: https://tools.ietf.org/html/rfc2883#section-4.1.1.
|
||||
func TestRACKDetectDSACK(t *testing.T) {
|
||||
c := context.New(t, uint32(mtu))
|
||||
defer c.Cleanup()
|
||||
|
||||
probeDone := make(chan int)
|
||||
const ackNumToVerify = 2
|
||||
addDSACKSeenCheckerProbe(t, c, ackNumToVerify, probeDone)
|
||||
|
||||
numPackets := 8
|
||||
data := sendAndReceive(t, c, numPackets)
|
||||
|
||||
// Cumulative ACK for [1-5] packets.
|
||||
seq := seqnum.Value(context.TestInitialSequenceNumber).Add(1)
|
||||
bytesRead := 5 * maxPayload
|
||||
c.SendAck(seq, bytesRead)
|
||||
|
||||
// Expect retransmission of #6 packet.
|
||||
c.ReceiveAndCheckPacketWithOptions(data, bytesRead, maxPayload, tsOptionSize)
|
||||
|
||||
// Send DSACK block for #6 packet indicating both
|
||||
// initial and retransmitted packet are received and
|
||||
// packets [1-7] are received.
|
||||
start := c.IRS.Add(seqnum.Size(bytesRead))
|
||||
end := start.Add(maxPayload)
|
||||
bytesRead += 2 * maxPayload
|
||||
c.SendAckWithSACK(seq, bytesRead, []header.SACKBlock{{start, end}})
|
||||
|
||||
// Wait for the probe function to finish processing the
|
||||
// ACK before the test completes.
|
||||
err := <-probeDone
|
||||
switch err {
|
||||
case failedToDetectDSACK:
|
||||
t.Fatalf("RACK DSACK detection failed")
|
||||
case invalidDSACKDetected:
|
||||
t.Fatalf("RACK DSACK detected when there is no duplicate SACK")
|
||||
}
|
||||
}
|
||||
|
||||
// TestRACKDetectDSACKWithOutOfOrder tests that RACK detects DSACK with out of
|
||||
// order segments.
|
||||
// See: https://tools.ietf.org/html/rfc2883#section-4.1.2.
|
||||
func TestRACKDetectDSACKWithOutOfOrder(t *testing.T) {
|
||||
c := context.New(t, uint32(mtu))
|
||||
defer c.Cleanup()
|
||||
|
||||
probeDone := make(chan int)
|
||||
const ackNumToVerify = 2
|
||||
addDSACKSeenCheckerProbe(t, c, ackNumToVerify, probeDone)
|
||||
|
||||
numPackets := 10
|
||||
data := sendAndReceive(t, c, numPackets)
|
||||
|
||||
// Cumulative ACK for [1-5] packets.
|
||||
seq := seqnum.Value(context.TestInitialSequenceNumber).Add(1)
|
||||
bytesRead := 5 * maxPayload
|
||||
c.SendAck(seq, bytesRead)
|
||||
|
||||
// Expect retransmission of #6 packet.
|
||||
c.ReceiveAndCheckPacketWithOptions(data, bytesRead, maxPayload, tsOptionSize)
|
||||
|
||||
// Send DSACK block for #6 packet indicating both
|
||||
// initial and retransmitted packet are received and
|
||||
// packets [1-7] are received.
|
||||
start := c.IRS.Add(seqnum.Size(bytesRead))
|
||||
end := start.Add(maxPayload)
|
||||
bytesRead += 2 * maxPayload
|
||||
// Send DSACK block for #6 along with out of
|
||||
// order #9 packet is received.
|
||||
start1 := c.IRS.Add(seqnum.Size(bytesRead) + maxPayload)
|
||||
end1 := start1.Add(maxPayload)
|
||||
c.SendAckWithSACK(seq, bytesRead, []header.SACKBlock{{start, end}, {start1, end1}})
|
||||
|
||||
// Wait for the probe function to finish processing the
|
||||
// ACK before the test completes.
|
||||
err := <-probeDone
|
||||
switch err {
|
||||
case failedToDetectDSACK:
|
||||
t.Fatalf("RACK DSACK detection failed")
|
||||
case invalidDSACKDetected:
|
||||
t.Fatalf("RACK DSACK detected when there is no duplicate SACK")
|
||||
}
|
||||
}
|
||||
|
||||
// TestRACKDetectDSACKWithOutOfOrderDup tests that DSACK is detected on a
|
||||
// duplicate of out of order packet.
|
||||
// See: https://tools.ietf.org/html/rfc2883#section-4.1.3
|
||||
func TestRACKDetectDSACKWithOutOfOrderDup(t *testing.T) {
|
||||
c := context.New(t, uint32(mtu))
|
||||
defer c.Cleanup()
|
||||
|
||||
probeDone := make(chan int)
|
||||
const ackNumToVerify = 4
|
||||
addDSACKSeenCheckerProbe(t, c, ackNumToVerify, probeDone)
|
||||
|
||||
numPackets := 10
|
||||
sendAndReceive(t, c, numPackets)
|
||||
|
||||
// ACK [1-5] packets.
|
||||
seq := seqnum.Value(context.TestInitialSequenceNumber).Add(1)
|
||||
bytesRead := 5 * maxPayload
|
||||
c.SendAck(seq, bytesRead)
|
||||
|
||||
// Send SACK indicating #6 packet is missing and received #7 packet.
|
||||
offset := seqnum.Size(bytesRead + maxPayload)
|
||||
start := c.IRS.Add(1 + offset)
|
||||
end := start.Add(maxPayload)
|
||||
c.SendAckWithSACK(seq, bytesRead, []header.SACKBlock{{start, end}})
|
||||
|
||||
// Send SACK with #6 packet is missing and received [7-8] packets.
|
||||
end = start.Add(2 * maxPayload)
|
||||
c.SendAckWithSACK(seq, bytesRead, []header.SACKBlock{{start, end}})
|
||||
|
||||
// Consider #8 packet is duplicated on the network and send DSACK.
|
||||
dsackStart := c.IRS.Add(1 + offset + maxPayload)
|
||||
dsackEnd := dsackStart.Add(maxPayload)
|
||||
c.SendAckWithSACK(seq, bytesRead, []header.SACKBlock{{dsackStart, dsackEnd}, {start, end}})
|
||||
|
||||
// Wait for the probe function to finish processing the ACK before the
|
||||
// test completes.
|
||||
err := <-probeDone
|
||||
switch err {
|
||||
case failedToDetectDSACK:
|
||||
t.Fatalf("RACK DSACK detection failed")
|
||||
case invalidDSACKDetected:
|
||||
t.Fatalf("RACK DSACK detected when there is no duplicate SACK")
|
||||
}
|
||||
}
|
||||
|
||||
// TestRACKDetectDSACKSingleDup tests DSACK for a single duplicate subsegment.
|
||||
// See: https://tools.ietf.org/html/rfc2883#section-4.2.1.
|
||||
func TestRACKDetectDSACKSingleDup(t *testing.T) {
|
||||
c := context.New(t, uint32(mtu))
|
||||
defer c.Cleanup()
|
||||
|
||||
probeDone := make(chan int)
|
||||
const ackNumToVerify = 4
|
||||
addDSACKSeenCheckerProbe(t, c, ackNumToVerify, probeDone)
|
||||
|
||||
numPackets := 4
|
||||
data := sendAndReceive(t, c, numPackets)
|
||||
|
||||
// Send ACK for #1 packet.
|
||||
bytesRead := maxPayload
|
||||
seq := seqnum.Value(context.TestInitialSequenceNumber).Add(1)
|
||||
c.SendAck(seq, bytesRead)
|
||||
|
||||
// Missing [2-3] packets and received #4 packet.
|
||||
seq = seqnum.Value(context.TestInitialSequenceNumber).Add(1)
|
||||
start := c.IRS.Add(1 + seqnum.Size(3*maxPayload))
|
||||
end := start.Add(seqnum.Size(maxPayload))
|
||||
c.SendAckWithSACK(seq, bytesRead, []header.SACKBlock{{start, end}})
|
||||
|
||||
// Expect retransmission of #2 packet.
|
||||
c.ReceiveAndCheckPacketWithOptions(data, bytesRead, maxPayload, tsOptionSize)
|
||||
|
||||
// ACK for retransmitted #2 packet.
|
||||
bytesRead += maxPayload
|
||||
c.SendAckWithSACK(seq, bytesRead, []header.SACKBlock{{start, end}})
|
||||
|
||||
// Simulate receving delayed subsegment of #2 packet and delayed #3 packet by
|
||||
// sending DSACK block for the subsegment.
|
||||
dsackStart := c.IRS.Add(1 + seqnum.Size(bytesRead))
|
||||
dsackEnd := dsackStart.Add(seqnum.Size(maxPayload / 2))
|
||||
c.SendAckWithSACK(seq, numPackets*maxPayload, []header.SACKBlock{{dsackStart, dsackEnd}})
|
||||
|
||||
// Wait for the probe function to finish processing the ACK before the
|
||||
// test completes.
|
||||
err := <-probeDone
|
||||
switch err {
|
||||
case failedToDetectDSACK:
|
||||
t.Fatalf("RACK DSACK detection failed")
|
||||
case invalidDSACKDetected:
|
||||
t.Fatalf("RACK DSACK detected when there is no duplicate SACK")
|
||||
}
|
||||
}
|
||||
|
||||
// TestRACKDetectDSACKDupWithCumulativeACK tests DSACK for two non-contiguous
|
||||
// duplicate subsegments covered by the cumulative acknowledgement.
|
||||
// See: https://tools.ietf.org/html/rfc2883#section-4.2.2.
|
||||
func TestRACKDetectDSACKDupWithCumulativeACK(t *testing.T) {
|
||||
c := context.New(t, uint32(mtu))
|
||||
defer c.Cleanup()
|
||||
|
||||
probeDone := make(chan int)
|
||||
const ackNumToVerify = 5
|
||||
addDSACKSeenCheckerProbe(t, c, ackNumToVerify, probeDone)
|
||||
|
||||
numPackets := 6
|
||||
data := sendAndReceive(t, c, numPackets)
|
||||
|
||||
// Send ACK for #1 packet.
|
||||
bytesRead := maxPayload
|
||||
seq := seqnum.Value(context.TestInitialSequenceNumber).Add(1)
|
||||
c.SendAck(seq, bytesRead)
|
||||
|
||||
// Missing [2-5] packets and received #6 packet.
|
||||
seq = seqnum.Value(context.TestInitialSequenceNumber).Add(1)
|
||||
start := c.IRS.Add(1 + seqnum.Size(5*maxPayload))
|
||||
end := start.Add(seqnum.Size(maxPayload))
|
||||
c.SendAckWithSACK(seq, bytesRead, []header.SACKBlock{{start, end}})
|
||||
|
||||
// Expect retransmission of #2 packet.
|
||||
c.ReceiveAndCheckPacketWithOptions(data, bytesRead, maxPayload, tsOptionSize)
|
||||
|
||||
// Received delayed #2 packet.
|
||||
bytesRead += maxPayload
|
||||
c.SendAckWithSACK(seq, bytesRead, []header.SACKBlock{{start, end}})
|
||||
|
||||
// Received delayed #4 packet.
|
||||
start1 := c.IRS.Add(1 + seqnum.Size(3*maxPayload))
|
||||
end1 := start1.Add(seqnum.Size(maxPayload))
|
||||
c.SendAckWithSACK(seq, bytesRead, []header.SACKBlock{{start1, end1}, {start, end}})
|
||||
|
||||
// Simulate receiving retransmitted subsegment for #2 packet and delayed #3
|
||||
// packet by sending DSACK block for #2 packet.
|
||||
dsackStart := c.IRS.Add(1 + seqnum.Size(maxPayload))
|
||||
dsackEnd := dsackStart.Add(seqnum.Size(maxPayload / 2))
|
||||
c.SendAckWithSACK(seq, 4*maxPayload, []header.SACKBlock{{dsackStart, dsackEnd}, {start, end}})
|
||||
|
||||
// Wait for the probe function to finish processing the ACK before the
|
||||
// test completes.
|
||||
err := <-probeDone
|
||||
switch err {
|
||||
case failedToDetectDSACK:
|
||||
t.Fatalf("RACK DSACK detection failed")
|
||||
case invalidDSACKDetected:
|
||||
t.Fatalf("RACK DSACK detected when there is no duplicate SACK")
|
||||
}
|
||||
}
|
||||
|
||||
// TestRACKDetectDSACKDup tests two non-contiguous duplicate subsegments not
|
||||
// covered by the cumulative acknowledgement.
|
||||
// See: https://tools.ietf.org/html/rfc2883#section-4.2.3.
|
||||
func TestRACKDetectDSACKDup(t *testing.T) {
|
||||
c := context.New(t, uint32(mtu))
|
||||
defer c.Cleanup()
|
||||
|
||||
probeDone := make(chan int)
|
||||
const ackNumToVerify = 5
|
||||
addDSACKSeenCheckerProbe(t, c, ackNumToVerify, probeDone)
|
||||
|
||||
numPackets := 7
|
||||
data := sendAndReceive(t, c, numPackets)
|
||||
|
||||
// Send ACK for #1 packet.
|
||||
bytesRead := maxPayload
|
||||
seq := seqnum.Value(context.TestInitialSequenceNumber).Add(1)
|
||||
c.SendAck(seq, bytesRead)
|
||||
|
||||
// Missing [2-6] packets and SACK #7 packet.
|
||||
seq = seqnum.Value(context.TestInitialSequenceNumber).Add(1)
|
||||
start := c.IRS.Add(1 + seqnum.Size(6*maxPayload))
|
||||
end := start.Add(seqnum.Size(maxPayload))
|
||||
c.SendAckWithSACK(seq, bytesRead, []header.SACKBlock{{start, end}})
|
||||
|
||||
// Received delayed #3 packet.
|
||||
start1 := c.IRS.Add(1 + seqnum.Size(2*maxPayload))
|
||||
end1 := start1.Add(seqnum.Size(maxPayload))
|
||||
c.SendAckWithSACK(seq, bytesRead, []header.SACKBlock{{start1, end1}, {start, end}})
|
||||
|
||||
// Expect retransmission of #2 packet.
|
||||
c.ReceiveAndCheckPacketWithOptions(data, bytesRead, maxPayload, tsOptionSize)
|
||||
|
||||
// Consider #2 packet has been dropped and SACK #4 packet.
|
||||
start2 := c.IRS.Add(1 + seqnum.Size(3*maxPayload))
|
||||
end2 := start2.Add(seqnum.Size(maxPayload))
|
||||
c.SendAckWithSACK(seq, bytesRead, []header.SACKBlock{{start2, end2}, {start1, end1}, {start, end}})
|
||||
|
||||
// Simulate receiving retransmitted subsegment for #3 packet and delayed #5
|
||||
// packet by sending DSACK block for the subsegment.
|
||||
dsackStart := c.IRS.Add(1 + seqnum.Size(2*maxPayload))
|
||||
dsackEnd := dsackStart.Add(seqnum.Size(maxPayload / 2))
|
||||
end1 = end1.Add(seqnum.Size(2 * maxPayload))
|
||||
c.SendAckWithSACK(seq, bytesRead, []header.SACKBlock{{dsackStart, dsackEnd}, {start1, end1}})
|
||||
|
||||
// Wait for the probe function to finish processing the ACK before the
|
||||
// test completes.
|
||||
err := <-probeDone
|
||||
switch err {
|
||||
case failedToDetectDSACK:
|
||||
t.Fatalf("RACK DSACK detection failed")
|
||||
case invalidDSACKDetected:
|
||||
t.Fatalf("RACK DSACK detected when there is no duplicate SACK")
|
||||
}
|
||||
}
|
||||
|
||||
// TestRACKWithInvalidDSACKBlock tests that DSACK is not detected when DSACK
|
||||
// is not the first SACK block.
|
||||
func TestRACKWithInvalidDSACKBlock(t *testing.T) {
|
||||
c := context.New(t, uint32(mtu))
|
||||
defer c.Cleanup()
|
||||
|
||||
probeDone := make(chan struct{})
|
||||
const ackNumToVerify = 2
|
||||
var n int
|
||||
c.Stack().AddTCPProbe(func(state stack.TCPEndpointState) {
|
||||
// Validate that RACK does not detect DSACK when DSACK block is
|
||||
// not the first SACK block.
|
||||
n++
|
||||
t.Helper()
|
||||
if state.Sender.RACKState.DSACKSeen {
|
||||
t.Fatalf("RACK DSACK detected when there is no duplicate SACK")
|
||||
}
|
||||
|
||||
if n == ackNumToVerify {
|
||||
close(probeDone)
|
||||
}
|
||||
})
|
||||
|
||||
numPackets := 10
|
||||
data := sendAndReceive(t, c, numPackets)
|
||||
|
||||
// Cumulative ACK for [1-5] packets.
|
||||
seq := seqnum.Value(context.TestInitialSequenceNumber).Add(1)
|
||||
bytesRead := 5 * maxPayload
|
||||
c.SendAck(seq, bytesRead)
|
||||
|
||||
// Expect retransmission of #6 packet.
|
||||
c.ReceiveAndCheckPacketWithOptions(data, bytesRead, maxPayload, tsOptionSize)
|
||||
|
||||
// Send DSACK block for #6 packet indicating both
|
||||
// initial and retransmitted packet are received and
|
||||
// packets [1-7] are received.
|
||||
start := c.IRS.Add(seqnum.Size(bytesRead))
|
||||
end := start.Add(maxPayload)
|
||||
bytesRead += 2 * maxPayload
|
||||
|
||||
// Send DSACK block as second block.
|
||||
start1 := c.IRS.Add(seqnum.Size(bytesRead) + maxPayload)
|
||||
end1 := start1.Add(maxPayload)
|
||||
c.SendAckWithSACK(seq, bytesRead, []header.SACKBlock{{start1, end1}, {start, end}})
|
||||
|
||||
// Wait for the probe function to finish processing the
|
||||
// ACK before the test completes.
|
||||
<-probeDone
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user