diff --git a/src/inet/networklayer/icmpv6/Ipv6NeighbourDiscovery.cc b/src/inet/networklayer/icmpv6/Ipv6NeighbourDiscovery.cc index 9785d3f5522..f74501f9ac6 100644 --- a/src/inet/networklayer/icmpv6/Ipv6NeighbourDiscovery.cc +++ b/src/inet/networklayer/icmpv6/Ipv6NeighbourDiscovery.cc @@ -68,6 +68,7 @@ Ipv6NeighbourDiscovery::~Ipv6NeighbourDiscovery() for (auto *entry : advIfList) { cancelAndDelete(entry->raTimeoutMsg); + cancelAndDelete(entry->pendingSolicitedRaMsg); delete entry; } @@ -1208,7 +1209,18 @@ void Ipv6NeighbourDiscovery::processRsPacket(Packet *packet, const Ipv6RouterSol // computed time, ignore the random delay and let that advertisement serve // this solicitation (RFC 4861 Section 6.2.6). if (scheduledTime < advIfEntry->nextScheduledRATime) { + /*This advertisement becomes the next multicast RA on the interface, so it + supersedes the solicited one that was pending until now: that one was + scheduled to serve the earlier solicitations, and this one, being earlier, + serves them too. Sending both would put two multicast RAs on the link less + than MIN_DELAY_BETWEEN_RAS apart, which RFC 4861 Section 6.2.6 forbids.*/ + if (advIfEntry->pendingSolicitedRaMsg) { + EV_DETAIL << "Cancelling the solicited RA scheduled at " << advIfEntry->nextScheduledRATime + << "; this earlier one supersedes it\n"; + cancelAndDelete(advIfEntry->pendingSolicitedRaMsg); + } scheduleAt(scheduledTime, msg); + advIfEntry->pendingSolicitedRaMsg = msg; advIfEntry->nextScheduledRATime = scheduledTime; } else @@ -1727,6 +1739,8 @@ void Ipv6NeighbourDiscovery::sendSolicitedRa(cMessage *msg) Ipv6Address destAddr = Ipv6Address("FF02::1"); EV_DETAIL << "Testing condition!\n"; createAndSendRaPacket(destAddr, ie); + if (AdvIfEntry *advIfEntry = fetchAdvIfEntry(ie)) + advIfEntry->pendingSolicitedRaMsg = nullptr; delete msg; } @@ -2693,6 +2707,7 @@ void Ipv6NeighbourDiscovery::stop() // cancel and delete all advertising interface entries for (auto *entry : advIfList) { cancelAndDelete(entry->raTimeoutMsg); + cancelAndDelete(entry->pendingSolicitedRaMsg); delete entry; } advIfList.clear(); diff --git a/src/inet/networklayer/icmpv6/Ipv6NeighbourDiscovery.h b/src/inet/networklayer/icmpv6/Ipv6NeighbourDiscovery.h index f122b6e9399..75ea45da082 100644 --- a/src/inet/networklayer/icmpv6/Ipv6NeighbourDiscovery.h +++ b/src/inet/networklayer/icmpv6/Ipv6NeighbourDiscovery.h @@ -127,6 +127,7 @@ class INET_API Ipv6NeighbourDiscovery : public OperationalBase, protected cListe simtime_t nextScheduledRATime; // stores time when next RA will be sent. simtime_t lastMulticastRATime = -1; // time the last multicast RA was sent (-1 = none yet); used to rate-limit RAs to MIN_DELAY_BETWEEN_RAS (RFC 4861 Section 6.2.6) cMessage *raTimeoutMsg; // the message to cancel when resetting RA timer + cMessage *pendingSolicitedRaMsg = nullptr; // solicited RA scheduled on this interface but not yet sent (nullptr = none); cancelled when a solicitation schedules an earlier one }; typedef std::vector AdvIfList; diff --git a/tests/fingerprint/examples.csv b/tests/fingerprint/examples.csv index 283915ab865..1e06681d865 100644 --- a/tests/fingerprint/examples.csv +++ b/tests/fingerprint/examples.csv @@ -165,7 +165,7 @@ # /examples/inet/hierarchical99/, -f .qtenv.ini -c General -r 0 /examples/inet/hierarchical99/, -f networklayer.ini -c IPv4 -r 0, 10000s, a94d-edae/tplx;1f32-e325/~tNl;b138-ee8b/~tND;66c9-cd73/tyf, PASS, EthernetMac Ipv4 -/examples/inet/hierarchical99/, -f networklayer.ini -c IPv6 -r 0, 10000s, 0001-0108/tplx;3960-479e/~tNl;add1-e848/~tND;2c03-abef/tyf, PASS, EthernetMac +/examples/inet/hierarchical99/, -f networklayer.ini -c IPv6 -r 0, 10000s, 6164-8ced/tplx;6b03-6322/~tNl;e7c1-b997/~tND;2c03-abef/tyf, PASS, EthernetMac /examples/inet/hierarchical99/, -f networklayer.ini -c Generic -r 0, 10000s, 6ae4-6e75/tplx;2209-80ac/~tNl;93bf-6657/tyf, PASS, EthernetMac /examples/inet/hierarchical99/, -f networklayer.ini -c Flooding -r 0, 10000s, 7ba7-5b91/tplx;b0f0-411e/~tNl;2b94-8906/tyf, PASS, EthernetMac /examples/inet/hierarchical99/, -f networklayer.ini -c ProbabilisticBroadcast -r 0, 10000s, 5a54-9779/tplx;1165-a70b/~tNl;760b-abfc/tyf, PASS, EthernetMac @@ -381,7 +381,7 @@ /examples/manetrouting/multiradio/, -f omnetpp.ini -c SingleRadio -r 0, 20s, 85a0-51b8/tplx;c07a-44e1/~tNl;3aa0-49ed/tyf, PASS, wireless adhoc Ipv4 /examples/ipv6/mipv6/, -f omnetpp.ini -c Handover -r 0, 70s, 19b8-20a4/tplx;b378-071d/~tNl;d358-e3bb/~tND;44ef-1a45/tyf, PASS, wireless EthernetMac -/examples/ipv6/mipv6/, -f omnetpp.ini -c RouteOptimizationTwoCNs -r 0, 60s, 3f0c-078d/tplx;04e1-1f03/~tNl;4199-2b15/~tND;ed3e-17fa/tyf, PASS, wireless EthernetMac +/examples/ipv6/mipv6/, -f omnetpp.ini -c RouteOptimizationTwoCNs -r 0, 60s, 5115-7e10/tplx;a522-3d29/~tNl;5319-39f4/~tND;ed3e-17fa/tyf, PASS, wireless EthernetMac /examples/ipv6/mipv6roaming/, -f omnetpp.ini -c Roaming -r 0, 70s, 0798-40c2/tplx;a682-8d0e/~tNl;cdb7-1b34/~tND;afae-2b3c/tyf, PASS, wireless EthernetMac /examples/ipv6/pmipv6/, -f omnetpp.ini -c General -r 0, 60s, f614-da0d/tplx;8b55-7191/~tNl;b490-dc09/~tND;0277-d784/tyf, PASS, wireless EthernetMac diff --git a/tests/fingerprint/mipv6-refactoring.csv b/tests/fingerprint/mipv6-refactoring.csv index 4c2175bce5a..334523e2a0d 100644 --- a/tests/fingerprint/mipv6-refactoring.csv +++ b/tests/fingerprint/mipv6-refactoring.csv @@ -9,7 +9,7 @@ # MIPv6 example — the primary test /examples/ipv6/mipv6/, -f omnetpp.ini -c Handover -r 0, 70s, aa29-a8c5/~tNlb, PASS, wireless EthernetMac -/examples/ipv6/mipv6/, -f omnetpp.ini -c RouteOptimizationTwoCNs -r 0, 60s, 068b-23aa/~tNlb, PASS, wireless EthernetMac +/examples/ipv6/mipv6/, -f omnetpp.ini -c RouteOptimizationTwoCNs -r 0, 60s, b108-5896/~tNlb, PASS, wireless EthernetMac /examples/ipv6/mipv6roaming/, -f omnetpp.ini -c Roaming -r 0, 70s, 7ed8-bee3/~tNlb, PASS, wireless EthernetMac # IPv6 examples @@ -23,7 +23,7 @@ /examples/ipv6/nclients/, -f omnetpp.ini -c PPP_SCTP -r 0, 100s, 06b9-ff17/~tNlb, PASS, # IPv6 networking examples -/examples/inet/hierarchical99/, -f networklayer.ini -c IPv6 -r 0, 10000s, f206-a0e2/~tNlb, PASS, EthernetMac +/examples/inet/hierarchical99/, -f networklayer.ini -c IPv6 -r 0, 10000s, 1cbb-1534/~tNlb, PASS, EthernetMac /examples/inet/udpclientserver/, -f omnetpp.ini -c udp_OK_ipv6 -r 0, 10s, 8feb-3936/~tNlb, PASS, /examples/inet/udpclientserver/, -f omnetpp.ini -c udp_Port_Unav_ipv6 -r 0, 10s, 9661-c1f5/~tNlb, PASS, /examples/inet/udpclientserver/, -f omnetpp.ini -c udp_Host_Unav_ipv6 -r 0, 10s, 6360-9ed1/~tNlb, PASS, diff --git a/tests/module/IPv6_RA_min_delay_between_RAs.test b/tests/module/IPv6_RA_min_delay_between_RAs.test new file mode 100644 index 00000000000..089705ffde6 --- /dev/null +++ b/tests/module/IPv6_RA_min_delay_between_RAs.test @@ -0,0 +1,85 @@ +%description: +Tests that consecutive multicast Router Advertisements (RA) sent by an IPv6 router to +the all-nodes address are never closer than MIN_DELAY_BETWEEN_RAS (3 s), even when +several Router Solicitations (RS) arrive while a solicited advertisement is already +pending (RFC 4861 Section 6.2.6). + +Six hosts boot at the same time on one link, so their solicitations reach router R +within a fraction of a second of each other, while the advertisement scheduled for the +first of them has not been sent yet. Each solicitation computes its own send time from +a random delay in [0, MAX_RA_DELAY_TIME], so a later solicitation can land earlier than +the pending advertisement. When it does, the router must let that one advertisement +serve them all, not send both. + +The scenario depends on the random delays the run draws, so the test also asserts that +the superseding case really occurred. Without that, a change elsewhere that shifts the +random number stream could leave the run with no overlapping solicitations at all, and +the rate-limit assertion would then pass without exercising anything. + +%#-------------------------------------------------------------------------------------------------------------- +%file: test.ned +import inet.networklayer.configurator.ipv6.Ipv6FlatNetworkConfigurator; +import inet.node.ethernet.EthernetSwitch; +import inet.node.ipv6.Router6; +import inet.node.ipv6.StandardHost6; +import ned.DatarateChannel; + +network RaRateLimit +{ + types: + channel ethline extends DatarateChannel + { + delay = 0.1us; + datarate = 10Mbps; + } + submodules: + configurator: Ipv6FlatNetworkConfigurator; + switch: EthernetSwitch; + R: Router6; + host[6]: StandardHost6; + connections: + R.ethg++ <--> ethline <--> switch.ethg++; + for i=0..5 { + host[i].ethg++ <--> ethline <--> switch.ethg++; + } +} +%#-------------------------------------------------------------------------------------------------------------- +%inifile: omnetpp.ini +[General] +record-vector-results = false +ned-path = ../../../../src +network = RaRateLimit +sim-time-limit = 30s +cmdenv-express-mode = false +**.cmdenv-log-level = detail +cmdenv-log-prefix = "%t %C: " + +# RFC 4861 defaults, and also INET's own NED defaults: no periodic advertisement is due +# within the run, so every advertisement seen here is a solicited one +**.R.ipv6.neighbourDiscovery.minIntervalBetweenRAs = 200s +**.R.ipv6.neighbourDiscovery.maxIntervalBetweenRAs = 600s + +# all hosts boot together, as after a power failure +**.host[*].ipv6.neighbourDiscovery.hostBootupTime = 0.4s + +# Ethernet NIC configuration +**.eth[*].queue.typename = "EthernetQosQueue" +**.eth[*].queue.dataQueue.typename = "DropTailQueue" +**.eth[*].queue.dataQueue.packetCapacity = 10 + +%#-------------------------------------------------------------------------------------------------------------- +%subst: /omnetpp::// +%#-------------------------------------------------------------------------------------------------------------- +%postrun-command: awk '/R\.ipv6\.neighbourDiscovery: Create and send RA invoked/ { if (prev != "" && $1 - prev < 3) printf "VIOLATION: two multicast RAs %.6f s apart, at %s and %s\n", $1 - prev, prev, $1; prev = $1 }' test.out > ra_gaps.out +%not-contains: ra_gaps.out +VIOLATION +%#-------------------------------------------------------------------------------------------------------------- +%contains: stdout +RaRateLimit.host[5].ipv6.neighbourDiscovery: Assigning new address to: eth0 +%#-------------------------------------------------------------------------------------------------------------- +%contains: stdout +R.ipv6.neighbourDiscovery: Cancelling the solicited RA scheduled at +%#-------------------------------------------------------------------------------------------------------------- +%postrun-command: grep "undisposed object:" test.out > test_undisposed.out || true +%not-contains: test_undisposed.out +undisposed object: (