From fb2a38c3bcc75d856b507549533ab9a95c6b1e7a Mon Sep 17 00:00:00 2001 From: Gyorgy Szaszko Date: Thu, 15 Oct 2026 14:08:28 +0200 Subject: [PATCH] icmpv6: fix: send no RA while the link-local address is tentative A router sent Router Advertisements (RA) from its link-local address while Duplicate Address Detection (DAD) for that address was running, and before DAD started: start() creates the periodic RA timer whatever the state of the addresses, and createAndSendRaPacket() took the link-local address as the source without checking it. RFC 4861 Section 6.2.2 forbids RAs on an interface that has no unicast address assigned, Section 4.2 makes the assigned link-local address the source, and RFC 4862 Section 5.4 does not count a tentative address as assigned. createAndSendRaPacket() now sends nothing while the link-local address is tentative, which covers periodic RAs, solicited RAs and the unsolicited RA that Pmipv6 sends after a Proxy Binding Acknowledgement. An RA not sent is dropped, not deferred. When sendSolicitedRa() drops one, it points nextScheduledRATime back at the periodic RA, because processRsPacket() ignores every Router Solicitation (RS) that would be answered later than that time, and the dropped RA would otherwise have left the time in the past. sendPeriodicRa() keeps its timer running but does not count an RA it did not send, so the first periodic RA sent after DAD completes counts as the first of the initial advertisements of Section 6.2.4. With a 30-70 ms RA interval, a router sent 24 of its 58 RAs in 3 s before DAD of its link-local address completed; after this commit it sends none before that. tests/module/IPv6_ND_no_router_advertisement_from_tentative_address.test runs that case and checks that no RA comes before DAD completes; it fails before this commit. The fingerprints of examples/manetrouting/dymo IPv6, MultiIPv6 and DynamicIPv6 (RAs every 30-70 ms), examples/ipv6/pmipv6 General (RAs every 1-3 s, and the Proxy Mobile IPv6 RA) and examples/inet/hierarchical99 IPv6 (solicited RAs) change, and this commit records them; their statistical baselines change too. Change: src.networklayer.icmpv6.Ipv6NeighbourDiscovery | behavior.change.fix | test fingerprint statistical whatsnew --- WHATSNEW | 4 ++ .../icmpv6/Ipv6NeighbourDiscovery.cc | 24 ++++++- tests/fingerprint/examples.csv | 10 +-- tests/fingerprint/mipv6-refactoring.csv | 6 +- ..._advertisement_from_tentative_address.test | 63 +++++++++++++++++++ 5 files changed, 96 insertions(+), 11 deletions(-) create mode 100644 tests/module/IPv6_ND_no_router_advertisement_from_tentative_address.test diff --git a/WHATSNEW b/WHATSNEW index d0bbfb448c7..58fe4f3b861 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -471,6 +471,10 @@ 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. A router sends no Router Advertisement while its link-local address is tentative + + A router sent Router Advertisements from its link-local address while Duplicate Address Detection for that address was running, and before it started, although RFC 4861 Section 4.2 requires the source to be the assigned link-local address, Section 6.2.2 allows them only on an interface with an assigned unicast address, and RFC 4862 does not count a tentative address as assigned. Periodic, solicited and Proxy Mobile IPv6 advertisements are now not sent while the link-local address is tentative; a solicited advertisement that is due after the address is verified is sent as before. Simulations with short advertisement intervals, or with advertisements solicited or sent by Proxy Mobile IPv6 while a router is still verifying its address, produce different results, for example in examples/manetrouting/dymo, examples/ipv6/pmipv6 and examples/inet/hierarchical99. + INET-4.7 (July 2026) — feature release -------------------------------------- diff --git a/src/inet/networklayer/icmpv6/Ipv6NeighbourDiscovery.cc b/src/inet/networklayer/icmpv6/Ipv6NeighbourDiscovery.cc index 9785d3f5522..75c2e12730b 100644 --- a/src/inet/networklayer/icmpv6/Ipv6NeighbourDiscovery.cc +++ b/src/inet/networklayer/icmpv6/Ipv6NeighbourDiscovery.cc @@ -1258,9 +1258,15 @@ bool Ipv6NeighbourDiscovery::validateRsPacket(Packet *packet, const Ipv6RouterSo void Ipv6NeighbourDiscovery::createAndSendRaPacket(const Ipv6Address& destAddr, NetworkInterface *ie) { - EV_INFO << "Create and send RA invoked!\n"; // Must use link-local addr. See: RFC2461 Section 6.1.2 Ipv6Address sourceAddr = ie->getProtocolData()->getLinkLocalAddress(); + // RFC 4861 Section 4.2: the source is the link-local address assigned to the interface, + // and a tentative address is not assigned (RFC 4862 Section 5.4) + if (ie->getProtocolData()->isTentativeAddress(sourceAddr)) { + EV_INFO << "Link-local address " << sourceAddr << " is tentative, RA not sent\n"; + return; + } + EV_INFO << "Create and send RA invoked!\n"; // This operation includes all options, regardless whether it is solicited or unsolicited. if (ie->getProtocolData()->getAdvSendAdvertisements()) { // if this is an advertising interface @@ -1682,8 +1688,14 @@ void Ipv6NeighbourDiscovery::sendPeriodicRa(cMessage *msg) NetworkInterface *ie = (NetworkInterface *)msg->getContextPointer(); AdvIfEntry *advIfEntry = fetchAdvIfEntry(ie); Ipv6Address destAddr = Ipv6Address("FF02::1"); - createAndSendRaPacket(destAddr, ie); - advIfEntry->numRASent++; + // an RA that is not sent while the link-local address is tentative does not count + // as one of the initial advertisements + if (!ie->getProtocolData()->isTentativeAddress(ie->getProtocolData()->getLinkLocalAddress())) { + createAndSendRaPacket(destAddr, ie); + advIfEntry->numRASent++; + } + else + EV_INFO << "Link-local address is tentative, periodic RA not sent\n"; // RFC 2461, Section 6.2.4 /*Whenever a multicast advertisement is sent from an interface, the timer is @@ -1726,6 +1738,12 @@ void Ipv6NeighbourDiscovery::sendSolicitedRa(cMessage *msg) NetworkInterface *ie = (NetworkInterface *)msg->getContextPointer(); Ipv6Address destAddr = Ipv6Address("FF02::1"); EV_DETAIL << "Testing condition!\n"; + if (ie->getProtocolData()->isTentativeAddress(ie->getProtocolData()->getLinkLocalAddress())) { + // createAndSendRaPacket() sends nothing; processRsPacket() took this RA as the next + // scheduled one, so point back at the periodic RA, or later RSs would be ignored + if (AdvIfEntry *advIfEntry = fetchAdvIfEntry(ie)) + advIfEntry->nextScheduledRATime = advIfEntry->raTimeoutMsg->getArrivalTime(); + } createAndSendRaPacket(destAddr, ie); delete msg; } diff --git a/tests/fingerprint/examples.csv b/tests/fingerprint/examples.csv index 0180b3bb5a0..f8116f25a57 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, 2066-f2bb/tplx;a251-4e01/~tNl;0502-0a17/~tND;b45b-90f6/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 @@ -348,16 +348,16 @@ # /examples/manetrouting/dymo/, -f omnetpp.ini -c _IPv4 -r 0 # abstract-config /examples/manetrouting/dymo/, -f omnetpp.ini -c IPv4 -r 0, 10s, 1a85-cb5d/tplx;dfe8-11b9/~tNl;2b10-4dcb/tyf, PASS, wireless adhoc Ipv4 # /examples/manetrouting/dymo/, -f omnetpp.ini -c _IPv6 -r 0 # abstract-config -/examples/manetrouting/dymo/, -f omnetpp.ini -c IPv6 -r 0, 10s, d3ef-c8cf/tplx;fd53-3e15/~tNl;8301-9264/tyf, PASS, wireless adhoc +/examples/manetrouting/dymo/, -f omnetpp.ini -c IPv6 -r 0, 10s, e6da-b593/tplx;145b-4eb1/~tNl;b4b9-189a/tyf, PASS, wireless adhoc # /examples/manetrouting/dymo/, -f omnetpp.ini -c _Generic -r 0 # abstract-config /examples/manetrouting/dymo/, -f omnetpp.ini -c Generic -r 0, 10s, 5f8a-7174/tplx;9713-e7fe/~tNl;5716-c166/tyf, PASS, wireless adhoc # /examples/manetrouting/dymo/, -f omnetpp.ini -c _Multi -r 0 # abstract-config /examples/manetrouting/dymo/, -f omnetpp.ini -c MultiIPv4 -r 0, 10s, b346-3570/tplx;7bce-e34f/~tNl;4782-99e5/tyf, PASS, wireless adhoc Ipv4 -/examples/manetrouting/dymo/, -f omnetpp.ini -c MultiIPv6 -r 0, 10s, d3ef-c8cf/tplx;fd53-3e15/~tNl;3ba7-352e/tyf, PASS, wireless adhoc +/examples/manetrouting/dymo/, -f omnetpp.ini -c MultiIPv6 -r 0, 10s, e6da-b593/tplx;145b-4eb1/~tNl;0adc-8b6a/tyf, PASS, wireless adhoc /examples/manetrouting/dymo/, -f omnetpp.ini -c MultiGeneric -r 0, 10s, 0354-6dd4/tplx;735c-957d/~tNl;31f0-2788/tyf, PASS, wireless adhoc # /examples/manetrouting/dymo/, -f omnetpp.ini -c _Dynamic -r 0 # abstract-config /examples/manetrouting/dymo/, -f omnetpp.ini -c DynamicIPv4 -r 0, 22s, 2f57-3280/tplx;8f96-3d9c/~tNl;c33f-4f8c/tyf, PASS, wireless adhoc Ipv4 -/examples/manetrouting/dymo/, -f omnetpp.ini -c DynamicIPv6 -r 0, 22s, 1e32-4f69/tplx;c684-e25f/~tNl;3469-a611/tyf, PASS, wireless adhoc +/examples/manetrouting/dymo/, -f omnetpp.ini -c DynamicIPv6 -r 0, 22s, cc61-27bb/tplx;c396-a2df/~tNl;749c-1382/tyf, PASS, wireless adhoc /examples/manetrouting/dymo/, -f omnetpp.ini -c DynamicGeneric -r 0, 22s, b68e-dfe3/tplx;9c68-c2d4/~tNl;8c4d-6bc7/tyf, PASS, wireless adhoc # /examples/manetrouting/gpsr/, -f omnetpp.ini -c Random -r 0, # abstract-config @@ -383,7 +383,7 @@ /examples/ipv6/mipv6/, -f omnetpp.ini -c Handover -r 0, 70s, 68ea-9693/tplx;a44c-17bf/~tNl;96ff-ed7c/~tND;44ef-1a45/tyf, PASS, wireless EthernetMac /examples/ipv6/mipv6/, -f omnetpp.ini -c RouteOptimizationTwoCNs -r 0, 60s, bacf-986a/tplx;e6db-28c0/~tNl;805b-8a25/~tND;ed3e-17fa/tyf, PASS, wireless EthernetMac /examples/ipv6/mipv6roaming/, -f omnetpp.ini -c Roaming -r 0, 70s, b275-b584/tplx;28ce-974e/~tNl;dcec-ae59/~tND;afae-2b3c/tyf, PASS, wireless EthernetMac -/examples/ipv6/pmipv6/, -f omnetpp.ini -c General -r 0, 60s, e801-4d1a/tplx;1064-9bd4/~tNl;bd46-d85c/~tND;0277-d784/tyf, PASS, wireless EthernetMac +/examples/ipv6/pmipv6/, -f omnetpp.ini -c General -r 0, 60s, 1ace-6963/tplx;117c-4089/~tNl;ddb3-870d/~tND;62ee-3f4a/tyf, PASS, wireless EthernetMac /examples/mobility/, -f omnetpp.ini -c AnsimMobility -r 0, 10000s, 72f8-5c0b/tplx;0000-0000/~tNl;0000-0000/~tND;7dd1-18eb/tyf, PASS, /examples/mobility, -f omnetpp.ini -c AttachedMobility, 10s, 566c-6355/tplx;0000-0000/~tNl;0000-0000/~tND;fa92-f68c/tyf, PASS, diff --git a/tests/fingerprint/mipv6-refactoring.csv b/tests/fingerprint/mipv6-refactoring.csv index 4e8ebad4ab4..47cb4c95187 100644 --- a/tests/fingerprint/mipv6-refactoring.csv +++ b/tests/fingerprint/mipv6-refactoring.csv @@ -23,14 +23,14 @@ /examples/ipv6/nclients/, -f omnetpp.ini -c PPP_SCTP -r 0, 100s, 3a7c-924c/~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, 5836-f09b/~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, # DYMO with IPv6 -/examples/manetrouting/dymo/, -f omnetpp.ini -c IPv6 -r 0, 10s, d23a-ce35/~tNlb, PASS, wireless adhoc -/examples/manetrouting/dymo/, -f omnetpp.ini -c MultiIPv6 -r 0, 10s, d23a-ce35/~tNlb, PASS, wireless adhoc +/examples/manetrouting/dymo/, -f omnetpp.ini -c IPv6 -r 0, 10s, cbc4-92bf/~tNlb, PASS, wireless adhoc +/examples/manetrouting/dymo/, -f omnetpp.ini -c MultiIPv6 -r 0, 10s, cbc4-92bf/~tNlb, PASS, wireless adhoc # EIGRP with IPv6 /examples/eigrp/testing_scenario6/, -f omnetpp.ini -c EIGRP-IPv6_exp1_bandwidth-change -r 0, 3min, c645-565e/~tNlb, PASS, eigrp EthernetMac diff --git a/tests/module/IPv6_ND_no_router_advertisement_from_tentative_address.test b/tests/module/IPv6_ND_no_router_advertisement_from_tentative_address.test new file mode 100644 index 00000000000..c20da07b5ae --- /dev/null +++ b/tests/module/IPv6_ND_no_router_advertisement_from_tentative_address.test @@ -0,0 +1,63 @@ +%description: +A router sends no Router Advertisement while its link-local address is tentative: RFC 4861 +Section 6.2.2 forbids Router Advertisements on an interface that is not an advertising +interface, which needs an assigned unicast address, and RFC 4862 Section 5.4 does not count +a tentative address as assigned. + +Topology: router -- host + +The router's Router Advertisement interval is 30-70 ms, shorter than its Duplicate Address +Detection (DAD) for the link-local address. Only the router's Neighbour Discovery logs, so +no "Create and send RA invoked!" line may come before its "DAD completed for address fe80" +line, and at least one must come after it. + +%#-------------------------------------------------------------------------------------------------------------- +%file: test.ned + +import inet.networklayer.configurator.ipv6.Ipv6FlatNetworkConfigurator; +import inet.node.ipv6.Router6; +import inet.node.ipv6.StandardHost6; +import ned.DatarateChannel; + +network TentativeRouterAdvertisementTest +{ + types: + channel ethline extends DatarateChannel + { + delay = 0.1us; + datarate = 10Mbps; + } + submodules: + configurator: Ipv6FlatNetworkConfigurator; + router: Router6; + host: StandardHost6; + connections: + router.ethg++ <--> ethline <--> host.ethg++; +} + +%#-------------------------------------------------------------------------------------------------------------- +%inifile: omnetpp.ini + +[General] +network = TentativeRouterAdvertisementTest +ned-path = .;../../../../src +sim-time-limit = 3s +record-vector-results = false +cmdenv-express-mode = false + +**.router.ipv6.neighbourDiscovery.minIntervalBetweenRAs = 0.03s +**.router.ipv6.neighbourDiscovery.maxIntervalBetweenRAs = 0.07s + +**.router.ipv6.neighbourDiscovery.cmdenv-log-level = info +**.cmdenv-log-level = off + +%#-------------------------------------------------------------------------------------------------------------- +%contains-regex: stdout +DAD completed for address fe80::[0-9a-f:]+ on eth0, address is unique[\s\S]*Create and send RA invoked! +%#-------------------------------------------------------------------------------------------------------------- +%not-contains-regex: stdout +Create and send RA invoked![\s\S]*DAD completed for address fe80 +%#-------------------------------------------------------------------------------------------------------------- +%not-contains: stdout + Error +%#--------------------------------------------------------------------------------------------------------------