diff --git a/WHATSNEW b/WHATSNEW index ef7ba478bc5..97d107800e0 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -532,19 +532,23 @@ Notable bug fixes are the following: The HT mode factory now includes band and preamble format in its cache key. A 5 GHz request after an equivalent 2.4 GHz request therefore receives the requested band and timing. Previously, that request could receive the earlier mode. Identical complete requests still reuse one object. -2. TCP Limited Transmit sends only new data +2. Random Early Detection (RED) reports its early drops as congestion + + RedDropper emitted every drop with reason OTHER_PACKET_DROP, so its packetDropCongestion statistic always read 0. An early drop is now reported with reason CONGESTION and a drop at the packet capacity with QUEUE_OVERFLOW. Which packets are dropped does not change; the packetDropCongestion scalars and vectors of RED queues that make early drops, including those in EthernetQosRedQueue and AFxyQueue, become non-zero. + +3. TCP Limited Transmit sends only new data With SACK and lossDetectionMode = "dupthresh", the first duplicate ACK of a flight with no more new data retransmitted the first hole: Limited Transmit took its segment from NextSeg(), whose rule 3 returns an old segment. The fast retransmit then sent the hole again, and the receiver reported a D-SACK. Limited Transmit now sends only previously unsent data, as RFC 3042 and step (3.3) of RFC 6675 say, in both loss detection modes. No fingerprint or statistical baseline of INET moves; simulations that select the dupthresh mode with SACK can change. -3. DumbTcp no longer sends an acknowledged FIN again +4. DumbTcp no longer sends an acknowledged FIN again The retransmission timer of DumbTcp restarted at each expiry, also when nothing was outstanding, so DumbTcp sent an acknowledged FIN again every 2 seconds until the connection closed; with a peer that does not close, it went on to the end of the simulation. The timer now stops when all data, the FIN included, is acknowledged, as RFC 6298 section 5.2 says. Simulations that select DumbTcp can change. -4. Nagle no longer holds a TCP retransmission +5. Nagle no longer holds a TCP retransmission Nagle's algorithm counted data as unacknowledged up to the highest sequence number sent. After a go-back, after a retransmission timeout or after a SYN-ACK that did not acknowledge the data of a Fast Open SYN, the partial segment at the end is a retransmission, and Nagle held it until the next retransmission timeout. Nagle now holds only new data, as RFC 9293 section 3.7.4 says and as Linux does. Simulations with losses and Nagle on (the default of Tcp and LegacyTcp) can change; 27 fingerprints and 11 statistical results of the INET examples moved. LinuxTcp, with the Nagle variant of Minshall, was not affected in the packetdrill suite. -5. TCP repairs that reach the algorithms of INET 4.7 +6. TCP repairs that reach the algorithms of INET 4.7 A review of all TCP parameters found defects. Most of their repairs concern features that are new in this release (entry 34), but the repairs below also reach the algorithms and the parameters of INET 4.7. Simulations that use them can change; 30 fingerprints and 20 statistical results of the INET examples moved. diff --git a/src/inet/queueing/filter/RedDropper.cc b/src/inet/queueing/filter/RedDropper.cc index 3c997c40166..f461c681661 100644 --- a/src/inet/queueing/filter/RedDropper.cc +++ b/src/inet/queueing/filter/RedDropper.cc @@ -133,6 +133,7 @@ void RedDropper::handlePacketFiltered(Packet *packet, bool matches) avg = decision.avg; count = decision.count; q_time = decision.q_time; + lastResult = decision.result; switch (decision.result) { case QUEUE_FULL: EV_DEBUG << "Queue length >= capacity" << EV_FIELD(packetCapacity) << EV_ENDL; @@ -167,6 +168,18 @@ void RedDropper::handlePacketFiltered(Packet *packet, bool matches) } } +void RedDropper::dropPacket(Packet *packet) +{ + // an early drop is the congestion signal RED exists to give, a drop at the packet + // capacity is a buffer overflow, as in PacketQueue + if (lastResult == QUEUE_FULL) + PacketFilterBase::dropPacket(packet, QUEUE_OVERFLOW); + else if (lastResult == RANDOMLY_ABOVE_LIMIT || lastResult == ABOVE_MAX_LIMIT) + PacketFilterBase::dropPacket(packet, CONGESTION); + else + PacketFilterBase::dropPacket(packet, OTHER_PACKET_DROP); +} + void RedDropper::receiveSignal(cComponent *source, simsignal_t signalID, cObject *obj, cObject *details) { Enter_Method("%s", cComponent::getSignalName(signalID)); diff --git a/src/inet/queueing/filter/RedDropper.h b/src/inet/queueing/filter/RedDropper.h index ec9691f9850..ee527de3160 100644 --- a/src/inet/queueing/filter/RedDropper.h +++ b/src/inet/queueing/filter/RedDropper.h @@ -48,6 +48,7 @@ class INET_API RedDropper : public PacketFilterBase, public cListener bool useEcn = false; bool markNext = false; double randomValue = NaN; // drawn before the decision that uses it, so that a query draws nothing + RedResult lastResult = BELOW_MIN_LIMIT; // of the last decision, for the reason of a drop ModuleRef collection; @@ -58,6 +59,7 @@ class INET_API RedDropper : public PacketFilterBase, public cListener virtual bool matchesResult(RedResult result, const Packet *packet) const; virtual void handlePacketFiltered(Packet *packet, bool matches) override; + virtual void dropPacket(Packet *packet) override; // cListener: virtual void receiveSignal(cComponent *source, simsignal_t signalID, cObject *obj, cObject *details) override; diff --git a/tests/module/RedDropper_congestion_drop_reason.test b/tests/module/RedDropper_congestion_drop_reason.test new file mode 100644 index 00000000000..782bef24fc7 --- /dev/null +++ b/tests/module/RedDropper_congestion_drop_reason.test @@ -0,0 +1,67 @@ +%description: +A Random Early Detection (RED) early drop is reported with reason CONGESTION, so the +packetDropCongestion statistic of RedDropper counts it. + +Topology: client --100Mbps-- router --10Mbps-- server + +client sends 20 Mb/s of UDP into the router's 10 Mb/s link. The router's queue on that +link is a RedDropperQueue with maxth = 4 and a packet capacity of 24, so RED drops +packets once its average queue length reaches 4, long before the queue is full. Such +drops must be emitted with reason CONGESTION, so packetDropCongestion must count at +least one. + +%#-------------------------------------------------------------------------------------------------------------- +%file: test.ned + +import inet.networklayer.configurator.ipv4.Ipv4NetworkConfigurator; +import inet.node.inet.Router; +import inet.node.inet.StandardHost; +import ned.DatarateChannel; + +network RedDropperCongestionTest +{ + submodules: + configurator: Ipv4NetworkConfigurator; + client: StandardHost; + router: Router; + server: StandardHost; + connections: + client.ethg++ <--> DatarateChannel { datarate = 100Mbps; delay = 1us; } <--> router.ethg++; + router.ethg++ <--> DatarateChannel { datarate = 10Mbps; delay = 1us; } <--> server.ethg++; +} + +%#-------------------------------------------------------------------------------------------------------------- +%inifile: omnetpp.ini + +[General] +network = RedDropperCongestionTest +ned-path = .;../../../../src +sim-time-limit = 1.5s +record-vector-results = false +cmdenv-express-mode = true + +**.router.eth[1].queue.typename = "RedDropperQueue" +**.router.eth[1].queue.red.minth = 3 +**.router.eth[1].queue.red.maxth = 4 +**.router.eth[1].queue.red.maxp = 1 +**.router.eth[1].queue.red.wq = 1 +**.router.eth[1].queue.red.packetCapacity = 24 + +**.client.numApps = 1 +**.client.app[0].typename = "UdpBasicApp" +**.client.app[0].destAddresses = "server" +**.client.app[0].destPort = 1000 +**.client.app[0].messageLength = 1000B +**.client.app[0].sendInterval = 0.4ms +**.client.app[0].startTime = 1s +**.server.numApps = 1 +**.server.app[0].typename = "UdpSink" +**.server.app[0].localPort = 1000 + +%#-------------------------------------------------------------------------------------------------------------- +%contains-regex: results/General-#0.sca +scalar RedDropperCongestionTest\.router\.eth\[1\]\.queue\.red packetDropCongestion:count [1-9][0-9]* +%#-------------------------------------------------------------------------------------------------------------- +%not-contains: stdout + Error +%#--------------------------------------------------------------------------------------------------------------