github.com/apernet/quic-go@v0.43.1-0.20240515053213-5e9e635fd9f0/internal/ackhandler/ecn_test.go (about)

     1  package ackhandler
     2  
     3  import (
     4  	mocklogging "github.com/apernet/quic-go/internal/mocks/logging"
     5  	"github.com/apernet/quic-go/internal/protocol"
     6  	"github.com/apernet/quic-go/internal/utils"
     7  	"github.com/apernet/quic-go/logging"
     8  
     9  	. "github.com/onsi/ginkgo/v2"
    10  	. "github.com/onsi/gomega"
    11  )
    12  
    13  var _ = Describe("ECN tracker", func() {
    14  	var ecnTracker *ecnTracker
    15  	var tracer *mocklogging.MockConnectionTracer
    16  
    17  	getAckedPackets := func(pns ...protocol.PacketNumber) []*packet {
    18  		var packets []*packet
    19  		for _, p := range pns {
    20  			packets = append(packets, &packet{PacketNumber: p})
    21  		}
    22  		return packets
    23  	}
    24  
    25  	BeforeEach(func() {
    26  		var tr *logging.ConnectionTracer
    27  		tr, tracer = mocklogging.NewMockConnectionTracer(mockCtrl)
    28  		ecnTracker = newECNTracker(utils.DefaultLogger, tr)
    29  	})
    30  
    31  	It("sends exactly 10 testing packets", func() {
    32  		tracer.EXPECT().ECNStateUpdated(logging.ECNStateTesting, logging.ECNTriggerNoTrigger)
    33  		for i := 0; i < 9; i++ {
    34  			Expect(ecnTracker.Mode()).To(Equal(protocol.ECT0))
    35  			// Do this twice to make sure only sent packets are counted
    36  			Expect(ecnTracker.Mode()).To(Equal(protocol.ECT0))
    37  			ecnTracker.SentPacket(protocol.PacketNumber(10+i), protocol.ECT0)
    38  		}
    39  		Expect(ecnTracker.Mode()).To(Equal(protocol.ECT0))
    40  		tracer.EXPECT().ECNStateUpdated(logging.ECNStateUnknown, logging.ECNTriggerNoTrigger)
    41  		ecnTracker.SentPacket(20, protocol.ECT0)
    42  		// In unknown state, packets shouldn't be ECN-marked.
    43  		Expect(ecnTracker.Mode()).To(Equal(protocol.ECNNon))
    44  	})
    45  
    46  	sendAllTestingPackets := func() {
    47  		tracer.EXPECT().ECNStateUpdated(logging.ECNStateTesting, logging.ECNTriggerNoTrigger)
    48  		tracer.EXPECT().ECNStateUpdated(logging.ECNStateUnknown, logging.ECNTriggerNoTrigger)
    49  		for i := 0; i < 10; i++ {
    50  			Expect(ecnTracker.Mode()).To(Equal(protocol.ECT0))
    51  			ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECT0)
    52  		}
    53  	}
    54  
    55  	It("fails ECN validation if all ECN testing packets are lost", func() {
    56  		sendAllTestingPackets()
    57  		for i := 10; i < 20; i++ {
    58  			Expect(ecnTracker.Mode()).To(Equal(protocol.ECNNon))
    59  			ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECNNon)
    60  		}
    61  		for i := 0; i < 9; i++ {
    62  			ecnTracker.LostPacket(protocol.PacketNumber(i))
    63  		}
    64  		// We don't care about the loss of non-testing packets
    65  		ecnTracker.LostPacket(15)
    66  		// Now lose the last testing packet.
    67  		tracer.EXPECT().ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedLostAllTestingPackets)
    68  		ecnTracker.LostPacket(9)
    69  		Expect(ecnTracker.Mode()).To(Equal(protocol.ECNNon))
    70  		// We still don't care about more non-testing packets being lost
    71  		ecnTracker.LostPacket(16)
    72  	})
    73  
    74  	It("only detects ECN mangling after sending all testing packets", func() {
    75  		tracer.EXPECT().ECNStateUpdated(logging.ECNStateTesting, logging.ECNTriggerNoTrigger)
    76  		for i := 0; i < 9; i++ {
    77  			Expect(ecnTracker.Mode()).To(Equal(protocol.ECT0))
    78  			ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECT0)
    79  			ecnTracker.LostPacket(protocol.PacketNumber(i))
    80  		}
    81  		// Send the last testing packet, and receive a
    82  		tracer.EXPECT().ECNStateUpdated(logging.ECNStateUnknown, logging.ECNTriggerNoTrigger)
    83  		Expect(ecnTracker.Mode()).To(Equal(protocol.ECT0))
    84  		ecnTracker.SentPacket(9, protocol.ECT0)
    85  		// Now lose the last testing packet.
    86  		tracer.EXPECT().ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedLostAllTestingPackets)
    87  		ecnTracker.LostPacket(9)
    88  	})
    89  
    90  	It("passes ECN validation when a testing packet is acknowledged, while still in testing state", func() {
    91  		tracer.EXPECT().ECNStateUpdated(logging.ECNStateTesting, logging.ECNTriggerNoTrigger)
    92  		for i := 0; i < 5; i++ {
    93  			Expect(ecnTracker.Mode()).To(Equal(protocol.ECT0))
    94  			ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECT0)
    95  		}
    96  		tracer.EXPECT().ECNStateUpdated(logging.ECNStateCapable, logging.ECNTriggerNoTrigger)
    97  		Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(3), 1, 0, 0)).To(BeFalse())
    98  		// make sure we continue sending ECT(0) packets
    99  		for i := 5; i < 100; i++ {
   100  			Expect(ecnTracker.Mode()).To(Equal(protocol.ECT0))
   101  			ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECT0)
   102  		}
   103  	})
   104  
   105  	It("passes ECN validation when a testing packet is acknowledged, while in unknown state", func() {
   106  		sendAllTestingPackets()
   107  		for i := 10; i < 20; i++ {
   108  			Expect(ecnTracker.Mode()).To(Equal(protocol.ECNNon))
   109  			ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECNNon)
   110  		}
   111  		// Lose some packets to make sure this doesn't influence the outcome.
   112  		for i := 0; i < 5; i++ {
   113  			ecnTracker.LostPacket(protocol.PacketNumber(i))
   114  		}
   115  		tracer.EXPECT().ECNStateUpdated(logging.ECNStateCapable, logging.ECNTriggerNoTrigger)
   116  		Expect(ecnTracker.HandleNewlyAcked([]*packet{{PacketNumber: 7}}, 1, 0, 0)).To(BeFalse())
   117  	})
   118  
   119  	It("fails ECN validation when the ACK contains more ECN counts than we sent packets", func() {
   120  		sendAllTestingPackets()
   121  		for i := 10; i < 20; i++ {
   122  			Expect(ecnTracker.Mode()).To(Equal(protocol.ECNNon))
   123  			ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECNNon)
   124  		}
   125  		// only 10 ECT(0) packets were sent, but the ACK claims to have received 12 of them
   126  		tracer.EXPECT().ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedMoreECNCountsThanSent)
   127  		Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12), 12, 0, 0)).To(BeFalse())
   128  	})
   129  
   130  	It("fails ECN validation when the ACK contains ECN counts for the wrong code point", func() {
   131  		sendAllTestingPackets()
   132  		for i := 10; i < 20; i++ {
   133  			Expect(ecnTracker.Mode()).To(Equal(protocol.ECNNon))
   134  			ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECNNon)
   135  		}
   136  		// We sent ECT(0), but this ACK acknowledges ECT(1).
   137  		tracer.EXPECT().ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedMoreECNCountsThanSent)
   138  		Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12), 0, 1, 0)).To(BeFalse())
   139  	})
   140  
   141  	It("fails ECN validation when the ACK doesn't contain ECN counts", func() {
   142  		sendAllTestingPackets()
   143  		for i := 10; i < 20; i++ {
   144  			Expect(ecnTracker.Mode()).To(Equal(protocol.ECNNon))
   145  			ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECNNon)
   146  		}
   147  		// First only acknowledge packets sent without ECN marks.
   148  		Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(12, 13, 14), 0, 0, 0)).To(BeFalse())
   149  		// Now acknowledge some packets sent with ECN marks.
   150  		tracer.EXPECT().ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedNoECNCounts)
   151  		Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(1, 2, 3, 15), 0, 0, 0)).To(BeFalse())
   152  	})
   153  
   154  	It("fails ECN validation when an ACK decreases ECN counts", func() {
   155  		sendAllTestingPackets()
   156  		for i := 10; i < 20; i++ {
   157  			Expect(ecnTracker.Mode()).To(Equal(protocol.ECNNon))
   158  			ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECNNon)
   159  		}
   160  		tracer.EXPECT().ECNStateUpdated(logging.ECNStateCapable, logging.ECNTriggerNoTrigger)
   161  		Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(1, 2, 3, 12), 3, 0, 0)).To(BeFalse())
   162  		// Now acknowledge some more packets, but decrease the ECN counts. Obviously, this doesn't make any sense.
   163  		tracer.EXPECT().ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedDecreasedECNCounts)
   164  		Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(4, 5, 6, 13), 2, 0, 0)).To(BeFalse())
   165  		// make sure that new ACKs are ignored
   166  		Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(7, 8, 9, 14), 5, 0, 0)).To(BeFalse())
   167  	})
   168  
   169  	// This can happen if ACK are lost / reordered.
   170  	It("doesn't fail validation if the ACK contains more ECN counts than it acknowledges packets", func() {
   171  		sendAllTestingPackets()
   172  		for i := 10; i < 20; i++ {
   173  			Expect(ecnTracker.Mode()).To(Equal(protocol.ECNNon))
   174  			ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECNNon)
   175  		}
   176  		tracer.EXPECT().ECNStateUpdated(logging.ECNStateCapable, logging.ECNTriggerNoTrigger)
   177  		Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(1, 2, 3, 12), 8, 0, 0)).To(BeFalse())
   178  	})
   179  
   180  	It("fails ECN validation when the ACK doesn't contain enough ECN counts", func() {
   181  		sendAllTestingPackets()
   182  		for i := 10; i < 20; i++ {
   183  			Expect(ecnTracker.Mode()).To(Equal(protocol.ECNNon))
   184  			ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECNNon)
   185  		}
   186  		// First only acknowledge some packets sent with ECN marks.
   187  		tracer.EXPECT().ECNStateUpdated(logging.ECNStateCapable, logging.ECNTriggerNoTrigger)
   188  		Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(1, 2, 3, 12), 2, 0, 1)).To(BeTrue())
   189  		// Now acknowledge some more packets sent with ECN marks, but don't increase the counters enough.
   190  		// This ACK acknowledges 3 more ECN-marked packets, but the counters only increase by 2.
   191  		tracer.EXPECT().ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedTooFewECNCounts)
   192  		Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(4, 5, 6, 15), 3, 0, 2)).To(BeFalse())
   193  	})
   194  
   195  	It("detects ECN mangling if all testing packets are marked CE", func() {
   196  		sendAllTestingPackets()
   197  		for i := 10; i < 20; i++ {
   198  			Expect(ecnTracker.Mode()).To(Equal(protocol.ECNNon))
   199  			ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECNNon)
   200  		}
   201  		// ECN capability not confirmed yet, therefore CE marks are not regarded as congestion events
   202  		Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(0, 1, 2, 3), 0, 0, 4)).To(BeFalse())
   203  		Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(4, 5, 6, 10, 11, 12), 0, 0, 7)).To(BeFalse())
   204  		// With the next ACK, all testing packets will now have been marked CE.
   205  		tracer.EXPECT().ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedManglingDetected)
   206  		Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(7, 8, 9, 13), 0, 0, 10)).To(BeFalse())
   207  	})
   208  
   209  	It("only detects ECN mangling after sending all testing packets", func() {
   210  		tracer.EXPECT().ECNStateUpdated(logging.ECNStateTesting, logging.ECNTriggerNoTrigger)
   211  		for i := 0; i < 9; i++ {
   212  			Expect(ecnTracker.Mode()).To(Equal(protocol.ECT0))
   213  			ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECT0)
   214  			Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(protocol.PacketNumber(i)), 0, 0, int64(i+1))).To(BeFalse())
   215  		}
   216  		// Send the last testing packet, and receive a
   217  		tracer.EXPECT().ECNStateUpdated(logging.ECNStateUnknown, logging.ECNTriggerNoTrigger)
   218  		Expect(ecnTracker.Mode()).To(Equal(protocol.ECT0))
   219  		ecnTracker.SentPacket(9, protocol.ECT0)
   220  		// This ACK now reports the last testing packets as CE as well.
   221  		tracer.EXPECT().ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedManglingDetected)
   222  		Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(9), 0, 0, 10)).To(BeFalse())
   223  	})
   224  
   225  	It("detects ECN mangling, if some testing packets are marked CE, and then others are lost", func() {
   226  		sendAllTestingPackets()
   227  		for i := 10; i < 20; i++ {
   228  			Expect(ecnTracker.Mode()).To(Equal(protocol.ECNNon))
   229  			ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECNNon)
   230  		}
   231  		// ECN capability not confirmed yet, therefore CE marks are not regarded as congestion events
   232  		Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(0, 1, 2, 3), 0, 0, 4)).To(BeFalse())
   233  		Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(6, 7, 8, 9), 0, 0, 8)).To(BeFalse())
   234  		// Lose one of the two unacknowledged packets.
   235  		ecnTracker.LostPacket(4)
   236  		// By losing the last unacknowledged testing packets, we should detect the mangling.
   237  		tracer.EXPECT().ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedManglingDetected)
   238  		ecnTracker.LostPacket(5)
   239  	})
   240  
   241  	It("detects ECN mangling, if some testing packets are lost, and then others are marked CE", func() {
   242  		sendAllTestingPackets()
   243  		for i := 10; i < 20; i++ {
   244  			Expect(ecnTracker.Mode()).To(Equal(protocol.ECNNon))
   245  			ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECNNon)
   246  		}
   247  		// Lose a few packets.
   248  		ecnTracker.LostPacket(0)
   249  		ecnTracker.LostPacket(1)
   250  		ecnTracker.LostPacket(2)
   251  		// ECN capability not confirmed yet, therefore CE marks are not regarded as congestion events
   252  		Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(3, 4, 5, 6, 7, 8), 0, 0, 6)).To(BeFalse())
   253  		// By CE-marking the last unacknowledged testing packets, we should detect the mangling.
   254  		tracer.EXPECT().ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedManglingDetected)
   255  		Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(9), 0, 0, 7)).To(BeFalse())
   256  	})
   257  
   258  	It("declares congestion", func() {
   259  		sendAllTestingPackets()
   260  		for i := 10; i < 20; i++ {
   261  			Expect(ecnTracker.Mode()).To(Equal(protocol.ECNNon))
   262  			ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECNNon)
   263  		}
   264  		// Receive one CE count.
   265  		tracer.EXPECT().ECNStateUpdated(logging.ECNStateCapable, logging.ECNTriggerNoTrigger)
   266  		Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(1, 2, 3, 12), 2, 0, 1)).To(BeTrue())
   267  		// No increase in CE. No congestion.
   268  		Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(4, 5, 6, 13), 5, 0, 1)).To(BeFalse())
   269  		// Increase in CE. More congestion.
   270  		Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(7, 8, 9, 14), 7, 0, 2)).To(BeTrue())
   271  	})
   272  })