github.com/danielpfeifer02/quic-go-prio-packs@v0.41.0-28/internal/ackhandler/ecn_test.go (about) 1 package ackhandler 2 3 import ( 4 mocklogging "github.com/danielpfeifer02/quic-go-prio-packs/internal/mocks/logging" 5 "github.com/danielpfeifer02/quic-go-prio-packs/internal/protocol" 6 "github.com/danielpfeifer02/quic-go-prio-packs/internal/utils" 7 "github.com/danielpfeifer02/quic-go-prio-packs/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 })