Skip to content

networklayer: fix: the Neighbor Discovery defects that the standards tests found - #1392

Open
levy wants to merge 20 commits into
topic/standards-tests-igmp-mld-level2-fixesfrom
topic/standards-tests-nd-level2-fixes
Open

levy wants to merge 20 commits into
topic/standards-tests-igmp-mld-level2-fixesfrom
topic/standards-tests-nd-level2-fixes

Conversation

@levy

@levy levy commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

Stacked on #1388 (the IGMP and MLD repairs, topic/standards-tests-igmp-mld-level2-fixes). Merge #1388 first. This pull request targets that branch, so the diff shows only the ND commits.

The level 2 standards tests of IPv6 Neighbor Discovery and address autoconfiguration (tests/protocol/nd) found eleven defects in the model, against RFC 4861 and RFC 4862. doc/project/evidence/model/nd/results.md on master lists them as model gaps, and 15 protocol tests fail on master because of them. This pull request repairs ten of the defects, one defect in each commit. The eleventh, gap 9, is half repaired by #1388 and half open. After it, 36 of the 37 ND tests pass.

The defects have an effect on almost every IPv6 simulation:

  • Every IPv6 datagram without a hop limit from its transport left with the constant 30. RFC 4861 specifies the CurHopLimit of the interface: 64, or the value of the router.
  • A router advertised the hop limit 30 and always an MTU option of 1280. A host stored the MTU and the RetransTimer of its router, but did not use them, and Duplicate Address Detection read the Retrans Timer field in milliseconds as seconds.
  • A router answered only the first Router Solicitation, and its first periodic advertisement came 198 to 600 seconds after the start, not within 16 seconds.
  • A host dropped a whole Router Advertisement during Duplicate Address Detection, so on a link with two routers it could stay without a default router.
  • A Redirect carried no option: no target link-layer address and no part of the invoking packet.
  • An address from Ipv6NetworkConfigurator was valid at once and was never tested for a duplicate, and an address whose valid lifetime ended stayed in use.

The repairs are in Ipv6, Ipv6InterfaceData, Ipv6NeighbourDiscovery, the ICMPv6 serializer and Ipv6NetworkConfigurator. Each repair commit carries its source change, the test that it makes pass, its paragraph of the release note doc/whatsnew/incompatible/ipv6-neighbor-discovery-corrections.txt, and the fingerprint values that it moves. The statistical results are on the branch with the same name in the statistics repository, built on the statistics branch of #1388.

Why stacked. On master alone, these repairs make eight tests of other suites fail. The ND solicited-node change lets an MLDv2 host send Reports before the first MLDv1 Query, and an MLDv1 router stops on them in three MLD tests. The DAD of configured addresses keeps an address tentative for about 2 to 3 seconds: two MLD start-up tests fail, because the Mld Query timer starts before the router can send, and three IPv6 IPsec tests fail, because their flows start at 1 second. #1388 repairs both MLD causes and carries the solicited-node change, so this branch is built on it and does not repeat that change.

Gap 9 is half open. #1388 makes an interface join the solicited-node group of each address. But it also holds every MLD message until the link-local address is tested, so the Report for the group of a tentative address leaves after the solicitation of Duplicate Address Detection, and Rfc4862GroupsBeforeDad still fails. RFC 9777 section 5.2.14, and RFC 3590 for MLDv1, send that Report from the unspecified address. That repair needs the source in Ipv6 and in MLD, and routers that ignore such Reports; it is left for a later pull request. The test stays a plain FAIL with its new reason.

Risk: the trajectories of most IPv6 simulations change. 62 fingerprint configurations and 59 statistical results move, in examples/bgpv4, eigrp, inet, ipv6, manetrouting, ospfv3, pim and rip. Two defaults of Ipv6RoutingTable change: advCurHopLimit from 30 to 64, and advLinkMtu from 1280 to 0 (no MTU option). A simulation that needs the MTU option must set advLinkMtu. A configured global address is now usable about one to two seconds after the boot of its node, after Duplicate Address Detection. Set dupAddrDetectTransmits to 0 to keep the old behavior; the IPv6 IPsec tests now do this.

#1266 (icmpv6: keep one Router Discovery process per interface) changes Ipv6NeighbourDiscovery.cc too. The second of the two to merge must rebase on the first.

Commits, in the order to read them

Each fix commit in src moves behavior. The tests commits change no model code: the first gives Rfc4862GroupsBeforeDad its new reason, and the others prepare the next repair, each for a test whose timing the repair changes. The plan and doc commits change no behavior.

  • c6d2dc2b48 plan: add: ND level 2 repairs, one gap for each commit
  • bf67a8a702 tests: change: Rfc4862GroupsBeforeDad names the MLD Report that waits for the link-local address (the new reason of the open half of gap 9)
  • 3cf34d9394 ipv6: fix: a datagram takes the CurHopLimit of its outgoing interface (gap 1)
  • 4f6c18213c networklayer: fix: a router advertises the hop limit 64 and no MTU option (gap 2, before gap 3: with the old MTU default, gap 3 alone would limit every host to 1280 octets)
  • 8d9f9a9e8d networklayer: fix: a host fragments to the MTU that its router advertises (gap 3)
  • 555cdd0678 networklayer: fix: address resolution waits the RetransTimer of its router (gap 4)
  • 2787442783 icmpv6: fix: a router answers every Router Solicitation (gap 5)
  • 59d5ef4270 tests: fix: the initial advertisements may come exactly 16 s apart (prepares gap 6)
  • 9931374d01 icmpv6: fix: the first advertisements of a router come within 16 s (gap 6, with the seed of two MLDv1 module tests)
  • f84db43f6a tests: fix: the answer of R1 comes during the address detection of A (prepares gap 7)
  • 9d94072e30 icmpv6: fix: a host takes its routers from advertisements during DAD (gap 7)
  • a554b72f20 tests: fix: R1 knows the link-layer address of R2 before the Redirect (prepares gap 8)
  • 55aa88fc4b icmpv6: fix: a Redirect carries the target address and the invoking packet (gap 8)
  • 95456c13dc tests: fix: the IPv6 IPsec scenarios run without Duplicate Address Detection (prepares gap 10)
  • e3ff43da45 networklayer: fix: a node tests the addresses that its configurator gives (gap 10)
  • ce926baec8 networklayer: fix: an address is removed when its valid lifetime ends (gap 11)
  • 8631810bdc plan: change: ND gaps 1 to 11 are repaired
  • eadff0948b doc: change: ND results, ledger and matrix after the repairs (a new run of the ND tests at 8631810bdc)
  • b43d0ca975 plan: change: ND level 2 repairs are done
  • cc4d7ec3c7 plan: location: move the ND repair plan to done

Architectural surface:

  • Packet content: the hop limit of IPv6 datagrams; the Cur Hop Limit, MTU option and Retrans Timer of Router Advertisements; the new options of a Redirect, with the new message class Ipv6NdRedirectedHeader in Ipv6NdMessage.msg and its serialization in Icmpv6HeaderSerializer.
  • Configuration surface: the defaults of advCurHopLimit (64) and advLinkMtu (0) of Ipv6RoutingTable, and the default of AdvRetransTimer in Ipv6InterfaceData (0, so that an advertisement leaves the field unspecified). No parameter is new or removed.
  • Contracts: Ipv6InterfaceData keeps the CurHopLimit, LinkMTU and RetransTimer of an interface, and the outgoing interface writes the hop limit of a datagram. An address of the configurator is tentative until Duplicate Address Detection ends. The ipv6AddressRemoved signal now also fires when a valid lifetime ends.
  • Tests of other suites: the six IPv6 IPsec tests set dupAddrDetectTransmits = 0, as the common mockups of the IPsec checks now say. The module tests MLD_host_basic and MLD_host_groupstates pin seed 0, because the new random delays of the first Router Advertisements swap their two hosts at seed 1. IPv6_RA_MTU_option sets advLinkMtu = 1280.
  • No feature descriptor changes, no sealed path, and no new AV-* or NV-* row.

Evidence

All builds are clean builds (make MODE=<mode> clean, then make MODE=<mode>), with OMNeT++ 6.x and clang on Linux. The commands are those of opp_repl, as on master. The results are at the tip of this branch, that is with #1388.

Command Result
opp_run_protocol_tests -m debug -w '^tests/protocol/nd$' at the tip 37 tests: 36 PASS, 1 FAIL, Rfc4862GroupsBeforeDad (gap 9). Master: 15 FAIL
opp_run_protocol_tests -m debug -w '^tests/protocol/mld$' at the tip, and the same for igmp and ipsec MLD 47 PASS, IGMP 43 PASS, IPsec 28 PASS and its 3 expected FAIL
opp_run_protocol_tests -m debug at the tip 825 tests: 454 PASS, 306 expected SKIP (the packetdrill tests need inet-gpl), 45 expected FAIL, 20 FAIL. The 20 are the 10 open RIP tests, Rfc4862GroupsBeforeDad, and 9 tests of tests/protocol/ipv6 (Rfc4443*, Rfc8200*) that fail on master too and that master repaired after the base of #1388. No other test fails.
opp_run_module_tests -m debug at the tip 759 PASS
opp_run_fingerprint_tests -m release at the tip 1840 tests: 1837 PASS, 2 expected ERROR, 1 FAIL: examples/inet/gsandcs SAT, which depends on the z3 version of the machine and does not move with this branch
inet_run_statistical_tests -m release, in a copy of the container of the GitHub job (clang-16), with the statistics branch (see the note below the table) 929 tests: 844 PASS, 10 expected SKIP, 1 expected ERROR, 73 FAIL and 1 ERROR. These 74 configurations fail on #1388 and on master too (802.11, 10BASE-T1S, DHCP and other areas). Three master failures pass with this statistics branch: ipv6/mipv6 Handover, ipv6/mipv6roaming and ipv6/pmipv6, whose results this branch regenerates. No other configuration fails.

The container of the GitHub job has no opp_repl. So the statistical runs used INET's own copy of the test framework, with the commits of #1388 and of this branch replayed onto master 664b0f8940, before master removed that copy. The src trees are the same as on this branch. On GitHub, the job runs opp_run_statistical_tests.

Baseline updates. Each commit got its fingerprint values in tests/fingerprint/store.json from a clean release build at that commit, on the base of #1388 (opp_update_fingerprint_test_results -m release -w <dirs> -i <ini files> -c <configs>, for the configurations that the branch moves). Each statistics commit was made in the container of the GitHub job at the INET commit that it names. Each commit message names the configurations.

Commit Fingerprint configurations Statistical results
3cf34d9394 CurHopLimit 14 (~tND only) 0
4f6c18213c router defaults 36 33
555cdd0678 RetransTimer 32 (~tND only) 0
2787442783 answers to every solicitation 7 7
9931374d01 initial advertising interval 56 53
9d94072e30 advertisements during DAD 2 2
55aa88fc4b Redirect options 3 3
e3ff43da45 detection of configured addresses 39 39

The other commits move no fingerprint and no statistical result. The statistics branch is topic/standards-tests-nd-level2-fixes of inet-framework/statistics: 6 commits on topic/standards-tests-igmp-mld-level2-fixes of #1388.

Not verified here:

  • The feature tests, the validation tests and the chart tests. The CI of this pull request runs them.
  • AdvReachableTime has the same unit error as the Retrans Timer had. No test covers it, and this branch does not repair it.
  • The second half of gap 9 (the Report from the unspecified address, RFC 3590).

The plan, with the decisions of each gap and of the stack, is plan/done/nd-level2-fixes.md.

🤖 Generated with Claude Code

levy added 20 commits October 28, 2026 08:51
Before this commit, the level 2 pass measured the ND model and repaired nothing.

After this commit, a plan repairs the gaps of the pass, one gap for each commit.

Plan: plan/pending/nd-level2-fixes.md
Change: plan | behavior.add | - | nd-model-repairs
… for the link-local address

Before this commit, the gap paragraph of Rfc4862GroupsBeforeDad in tests/protocol/nd said that a node never joins its solicited-node groups. The IGMP and MLD repairs, on which this branch is built, repaired that half of ND gap 9: an interface now joins the solicited-node group of each of its addresses. The test still fails at observation 2, for another reason. MLD holds every message until the interface has a tested link-local address, so the Report for the group of the tentative address leaves after the Neighbor Solicitation of Duplicate Address Detection.

After this commit, the paragraph names that reason. RFC 9777 section 5.2.14, and RFC 3590 for MLDv1, send that Report from the unspecified address; the model does not. The test stays a plain FAIL, because the defect is open on this branch.

Plan: plan/pending/nd-level2-fixes.md
Change: tests.protocol | behavior.change | - | nd-model-repairs
Before this commit, the hop limit of a host was a constant (gap 1 of the ND results). Ipv6::encapsulate wrote IPv6_DEFAULT_ADVCURHOPLIMIT, 30, into every datagram whose transport gave no hop limit. The host copied the Cur Hop Limit of a Router Advertisement into its CurHopLimit variable, but no code read it. The default of that variable was 30 too.

After this commit, a datagram takes the CurHopLimit of its outgoing interface. The default of CurHopLimit is the new constant IPv6_DEFAULT_CURHOPLIMIT, 64. RFC 4861 section 6.3.2 sends IP packets with CurHopLimit, whose default is the value of the Assigned Numbers registry, 64. A hop limit from the transport is used as before.

The variable belongs to an interface, and routing chooses the interface after the encapsulation. So encapsulate() marks such a datagram with a HopLimitReq of the value -1, the default of the tag. fragmentPostRouting(), where every locally made datagram meets its outgoing interface, writes the CurHopLimit of that interface and removes the mark. A datagram that waits for Duplicate Address Detection passes fragmentPostRouting() twice. The mark makes the second pass change nothing.

To see the defect, run Rfc4861HopLimitFromRouter and Rfc4861HopLimitNoRouter of tests/protocol/nd on the parent commit: they see the hop limit 30, where the tests expect the value 50 of the router and 64.

Fingerprints: 14 configurations move, in ~tND only, the packet data: bgpv4 (BgpAndOspfv3 General and OspfOnly, BgpDiamondFailover6, BgpIpv6Basic, BgpWithdrawal6 General and Restart), inet/ipsec ipv6.ini Multicast6, the three IPv6 configurations of inet/udpclientserver, ipv6/mipv6 (Handover and RouteOptimizationTwoCNs), ipv6/mipv6roaming and ipv6/pmipv6.

No statistical result moves.

Plan: plan/pending/nd-level2-fixes.md
Change: src.networklayer.ipv6 | behavior.change.fix | fingerprint test whatsnew | nd-model-repairs
…tion

Before this commit, the router had two wrong defaults (gap 2 of the ND results). Ipv6RoutingTable had advCurHopLimit = default(30) and advLinkMtu = default(1280). So every Router Advertisement carried the Cur Hop Limit 30 and an MTU option of 1280, also on a link with the MTU 1500. The comment of the parameter said "0 = don't include", but the code always added the option.

After this commit, the defaults are 64 and 0, in the NED parameters and in the router variables of Ipv6InterfaceData. The advertisement carries an MTU option only for a nonzero AdvLinkMTU. RFC 4861 section 6.2.1 gives AdvCurHopLimit the value of the Assigned Numbers registry, 64, and AdvLinkMTU the default 0, which sends no MTU option.

The module test IPv6_RA_MTU_option used the old default to get an MTU option. It now sets advLinkMtu = 1280.

To see the defect, run Rfc4861RaCurHopLimit and Rfc4861RaMtuOption of tests/protocol/nd on the parent commit: they fail at observations 6 and 4.

Fingerprints: 36 configurations move, each a simulation with IPv6 routers. Every advertisement is 8 octets shorter, and the hosts take the hop limit 64. 32 move in tplx, ~tNl and ~tND, and 4 in tplx and ~tNl. They are bgpv4 (BgpAndOspfv3 General and OspfOnly, BgpDiamondFailover6, BgpIpv6Basic, BgpWithdrawal6 General and Restart), inet/hierarchical99 networklayer.ini IPv6, ipv6/ipv6configurator (CustomRA, Default, Hierarchical and ManualRouters), ipv6/mipv6 (Handover and RouteOptimizationTwoCNs), ipv6/mipv6roaming, ipv6/mld (General, MldDemo and MldV2Ssm), ipv6/nclients (ETH, PPP and PPP_SCTP), ipv6/pmipv6, manetrouting/dymo (DynamicIPv6, IPv6 and MultiIPv6), ospfv3 (multiple_areas_FINAL General, LinkDown and LinkDownLinkUp, small, v3_3_areas, v3_4_routers_2_areas and v3_mesh), pim (dm_ipv6, sm_ipv6 and ssm_ipv6) and rip/simpletest (IPv6 and MultiIPv4).

Statistical results: 33 move, the simulations with routers of IPv6 in the statistical set, for the same reason. The new results are in the statistics repository, branch topic/standards-tests-nd-level2-fixes, commit "ipv6: update statistics after the router defaults of ND".

Plan: plan/pending/nd-level2-fixes.md
Change: src.networklayer | behavior.change.fix | fingerprint statistical test whatsnew | nd-model-repairs
…ises

Before this commit, a host fragmented to the MTU of the interface, not to the advertised MTU (gap 3 of the ND results). The host copied the MTU option of a Router Advertisement into its LinkMTU variable, but Ipv6::fragmentAndSend fragmented to ie->getMtu(), and no code read LinkMTU.

After this commit, fragmentAndSend uses the LinkMTU of the interface when it is set and smaller than the MTU of the interface. RFC 4861 section 6.3.4 has the host copy the option into LinkMTU, and LinkMTU is the MTU of the link.

The default of LinkMTU was 1280, where RFC 4861 section 6.3.2 gives the value of the link type. It is now 0, which stands for the MTU of the interface, so that a host without an MTU option keeps the MTU of its link. A host takes an MTU option only between 1280 and the MTU of the interface, as the text of section 6.3.4 says. Before, it also took a larger value.

This repair comes after the one of the router defaults. With the old default of AdvLinkMTU, 1280, every host would have sent at most 1280 octets.

To see the defect, run Rfc4861MtuFromRouter of tests/protocol/nd on the parent commit: it fails at observation 2, where the echo request of 1500 octets leaves whole after an advertisement of 1400.

No fingerprint moves, because no router of the set sends an MTU option.

No statistical result moves.

Plan: plan/pending/nd-level2-fixes.md
Change: src.networklayer | behavior.change.fix | test whatsnew | nd-model-repairs
…outer

Before this commit, address resolution waited a constant, not RetransTimer (gap 4 of the ND results). Address resolution and the probes of Neighbor Unreachability Detection scheduled their retransmissions with _getRetransTimer(), the node constant RETRANS_TIMER. The host copied the Retrans Timer field of a Router Advertisement, in milliseconds, into its RetransTimer variable. That variable is an integer that Duplicate Address Detection read as seconds, and that the default and the retransTimer parameter filled in seconds.

After this commit, the RetransTimer variable is a simtime_t. The parameter sets it as it is, and an advertisement sets it from milliseconds. Address resolution, the probes and Duplicate Address Detection read it. RFC 4861 section 6.3.4 copies the field into RetransTimer, and sections 7.2.2 and 7.3.3 retransmit every RetransTimer milliseconds.

The default of AdvRetransTimer changes from 1, meant as seconds in a field of milliseconds, to 0, the default of RFC 4861 section 6.2.1. With 1, a host would now retransmit every millisecond. The same unit error of AdvReachableTime stays, and the ND results list it among the other findings.

To see the defect, run Rfc4861RetransTimerFromRouter of tests/protocol/nd on the parent commit: it fails at observation 3, with solicitations 1 s apart after an advertisement of 2000.

Fingerprints: 32 configurations move, in ~tND only. The Retrans Timer field of every advertisement is 0, not 1, and a host keeps its RetransTimer of 1 s either way. They are bgpv4 (BgpAndOspfv3 General and OspfOnly, BgpDiamondFailover6, BgpIpv6Basic, BgpWithdrawal6 General and Restart), inet/hierarchical99 networklayer.ini IPv6, ipv6/ipv6configurator (CustomRA, Default, Hierarchical and ManualRouters), ipv6/mipv6 (Handover and RouteOptimizationTwoCNs), ipv6/mipv6roaming, ipv6/mld (General, MldDemo and MldV2Ssm), ipv6/nclients (ETH, PPP and PPP_SCTP), ipv6/pmipv6, ospfv3 (multiple_areas_FINAL General, LinkDown and LinkDownLinkUp, small, v3_3_areas, v3_4_routers_2_areas and v3_mesh), pim (dm_ipv6, sm_ipv6 and ssm_ipv6) and rip/simpletest MultiIPv4.

No statistical result moves.

Plan: plan/pending/nd-level2-fixes.md
Change: src.networklayer | behavior.change.fix | fingerprint test whatsnew | nd-model-repairs
Before this commit, the router answered only its first solicitation (gap 5 of the ND results). processRsPacket schedules an answer only when it comes before nextScheduledRATime, the time of the next advertisement, and then sets that time to the time of the answer. sendSolicitedRa did not set it back. After the first answer, the time stayed in the past, and the router dropped every later answer until its first periodic advertisement, 198 to 600 s after the start.

After this commit, nextScheduledRATime is again the time of the periodic timer after an answer. RFC 4861 section 6.2.6 answers each solicitation within MAX_RA_DELAY_TIME, rate limited to one multicast advertisement every MIN_DELAY_BETWEEN_RAS.

RFC 4861 also asks a multicast answer to restart the periodic timer. That is the repair of gap 6, not of this commit.

To see the defect, run Rfc4861RaSolicited of tests/protocol/nd on the parent commit: it fails at observation 2, where R answers none of the solicitations of host B at 12.7, 16.7 and 20.7 s.

Fingerprints: 7 configurations move, in tplx, ~tNl and ~tND, where more than one solicitation reaches a router in the first minutes: inet/hierarchical99 networklayer.ini IPv6, ipv6/mld (General and MldDemo), ospfv3/multiple_areas_FINAL (General, LinkDown and LinkDownLinkUp) and ospfv3/small.

Statistical results: 7 move, for the same reason: hierarchical99 IPv6, ipv6/mld (General and MldDemo), ospfv3/multiple_areas_FINAL (3) and ospfv3/small. The new results are in the statistics repository, branch topic/standards-tests-nd-level2-fixes, commit "ipv6: update statistics after the answers to every solicitation".

Plan: plan/pending/nd-level2-fixes.md
Change: src.networklayer.icmpv6 | behavior.change.fix | fingerprint statistical test whatsnew | nd-model-repairs
Before this commit, Rfc4861RaUnsolicited was wrong at the bound. The repair of gap 6 of the ND results caps the timer before each of the first three Router Advertisements at MAX_INITIAL_RTR_ADVERT_INTERVAL, 16 s. The interval that the router draws, 198 to 600 s, is always larger, so each capped interval is exactly 16 s. The test waited for the second and the third advertisement with within(16.0). The deadline of a step fires before an event of the same instant, so the test failed at 21.770573 s, exactly 16 s after the second advertisement. Its check says "at most 16 seconds" (doc/project/evidence/protocol/nd/checks/router-discovery.md).

After this commit, the two steps wait 17 s and assert that the interval is at most 16 s. They still fail for a longer interval. Without the repair of gap 6, the test fails as before, at observation 2.

Plan: plan/pending/nd-level2-fixes.md
Change: tests | behavior.change.fix | - | nd-model-repairs
Before this commit, the first periodic Router Advertisement came 198 to 600 s after the start (gap 6 of the ND results). createRaTimer set the first timer to a random value between MinRtrAdvInterval and MaxRtrAdvInterval. sendPeriodicRa applied the cap of MAX_INITIAL_RTR_ADVERT_INTERVAL only to the timers after the first advertisement, and with numRASent <= 3 to the timer before the fourth one too. An answer to a solicitation did not restart the timer.

After this commit, computeRaInterval() draws the interval and caps it while fewer than MAX_INITIAL_RTR_ADVERTISEMENTS advertisements went out. createRaTimer, sendPeriodicRa and sendSolicitedRa use it. sendSolicitedRa counts the answer, restarts the timer and sets the time of the next advertisement. This replaces the smaller repair of gap 5. RFC 4861 section 6.2.4 caps the timer at 16 s for the first MAX_INITIAL_RTR_ADVERTISEMENTS advertisements of an interface. Section 6.2.6 resets the timer after a multicast answer, as if an unsolicited advertisement had been sent.

The capped interval is exactly 16 s, so routers that become advertising interfaces at the same instant would advertise in step. createRaTimer ran for every router in start(), at the same instant. In examples/manetrouting/gpsr, twenty routers then sent their first advertisement at exactly 16 s, and the radio medium stops such a run with "Another transmission is already started at precisely the same simulation time". A router sends its advertisements from its link-local address. So its interfaces now become advertising interfaces when the router has booted, at the random routerBootupTime, and createRaTimer runs there. A solicitation that comes before the boot is discarded.

To see the defect, run Rfc4861RaUnsolicited of tests/protocol/nd on the parent commit, with the test repair of the commit before: it fails at observation 2. Rfc4861RouterLifetimeZero, the test of gap 7, also passes after this commit. The advertisement of R1 no longer comes during the Duplicate Address Detection of host A, although the model still drops such an advertisement.

The module tests MLD_host_basic and MLD_host_groupstates pin a seed at which host1 is the reporter. The new random delays of the first advertisements swap the two hosts at seed 1, so both tests now pin seed 0, as their notes ask.

Fingerprints: 56 configurations move, the simulations with IPv6 routers, which now advertise in the first seconds, with the phases of their boot times. 36 move in tplx, ~tNl and ~tND, and 20 in tplx and ~tNl. They are bgpv4 (BgpAndOspfv3 General and OspfOnly, BgpDiamondFailover6, BgpIpv6Basic, BgpWithdrawal6 General and Restart), the nine IPv6 configurations of eigrp (testing_scenario6, testing_scenario_ds and two_node6), inet/hierarchical99 networklayer.ini IPv6, ipv6/ipv6configurator (CustomRA, Default, Hierarchical and ManualRouters), ipv6/mipv6 (Handover and RouteOptimizationTwoCNs), ipv6/mipv6roaming, ipv6/mld (General, MldDemo and MldV2Ssm), ipv6/nclients (ETH, PPP and PPP_SCTP), ipv6/pmipv6, manetrouting (dsdv MultiIPv4; dymo and gpsr DynamicIPv6, IPv6, MultiGeneric, MultiIPv4 and MultiIPv6), the ten configurations of ospfv3, pim (dm_ipv6, sm_ipv6 and ssm_ipv6) and rip/simpletest (IPv6 and MultiIPv4).

Statistical results: 53 move, the simulations with routers of IPv6 in the statistical set, for the same reason. The new results are in the statistics repository, branch topic/standards-tests-nd-level2-fixes, commit "ipv6: update statistics after the initial advertising intervals".

Plan: plan/pending/nd-level2-fixes.md
Change: src.networklayer.icmpv6 | behavior.change.fix | fingerprint statistical test whatsnew | nd-model-repairs
Before this commit, Rfc4861RouterLifetimeZero, which shows gap 7 of the ND results, passed with gap 7 still in the code. Host A drops the Router Advertisement of R1 because it comes during the Duplicate Address Detection that the advertisement of R2 starts. That order came from two random answer delays, 0 to 0.5 s each. In the level 2 run, R2 answered first and R1 45 ms later. The repair of gap 6 changed the random draws, and R1 answered first.

After this commit, the link of R1 has a delay of 250 ms in each direction. The answer of R2 arrives 0 to 0.5 s after the solicitation of A. The answer of R1 arrives 0.5 to 1 s after it, so always after the answer of R2 and during the detection, which lasts 1 to 2 s. Without the repair of gap 7, the test fails at observation 2 again: A sends no echo request.

Plan: plan/pending/nd-level2-fixes.md
Change: tests | behavior.change.fix | - | nd-model-repairs
Before this commit, a host dropped every Router Advertisement while it ran Duplicate Address Detection (gap 7 of the ND results). processRaPacket returned when isDadInProgress() held. On a link with two routers, the advertisement of the first one starts the detection of the global address. The advertisement of the second one, which comes some milliseconds later, was lost. The host could stay without a default router.

After this commit, processRaPacket does the router part, processRaForRouterUpdates, for every valid advertisement. It skips only the Prefix Information options during the detection, because a new address would start a second detection on the interface. The TODO about the restart of the detection stays. RFC 4861 section 6.3.4 updates the Default Router List and the parameters of the link on every valid advertisement.

To see the defect, run Rfc4861RouterLifetimeZero of tests/protocol/nd, with the test repair of the commit before, on the parent commit: it fails at observation 2, because host A sends no echo request.

Fingerprints: 2 configurations move, in tplx, ~tNl and ~tND, where a node now takes a router from an advertisement during its detection: ipv6/mld General and ipv6/pmipv6.

Statistical results: 2 move, ipv6/mld (General) and ipv6/pmipv6, where an advertisement comes during a detection. The new results are in the statistics repository, branch topic/standards-tests-nd-level2-fixes, commit "ipv6: update statistics after the advertisements during DAD".

Plan: plan/pending/nd-level2-fixes.md
Change: src.networklayer.icmpv6 | behavior.change.fix | fingerprint statistical test whatsnew | nd-model-repairs
Before this commit, Rfc4861RedirectFields had a wrong premise. It checks the Target Link-Layer Address option of the Redirect, which RFC 4861 section 4.5 includes "if known". The test said that R1 knows the address of R2 because it has just forwarded the echo request to R2. That is not so. A router builds the Redirect before it forwards the packet (Ipv6::routePacket, as Linux in ip6_forward), and R1 resolves R2 only for that first forwarded packet. With the repair of gap 8, the Redirect carried the Redirected Header option but no Target Link-Layer Address option, and the test still failed.

After this commit, R1 pings the link-local address of R2, the target of the Redirect, at 8 s. This is after R2 started at 5 s and before the echo request of A at 10 s, so the premise of the check holds. Without the repair of gap 8, the test fails at observation 2 as before, because the Redirect has no option.

Plan: plan/pending/nd-level2-fixes.md
Change: tests | behavior.change.fix | - | nd-model-repairs
…acket

Before this commit, the Redirect carried no option (gap 8 of the ND results). sendRedirect built the fixed part only, setChunkLength(B(40)), so every Redirect was 80 octets. The model had no class for the Redirected Header option, and the serializer had no case for it.

After this commit, sendRedirect adds the Target Link-Layer Address option from the neighbour cache entry of the target. It also adds a Redirected Header option, the new class Ipv6NdRedirectedHeader, with the octets of the invoking packet, cut so that the Redirect is at most 1280 octets and padded to 8 octets. RFC 4861 section 4.5 includes the Target Link-Layer Address option if the router knows the address. It also includes the Redirected Header option with as much of the invoking packet as fits into a Redirect of at most 1280 octets (sections 4.6.3 and 8.2).

The ICMPv6 serializer writes and reads the option. On reading, the padding counts as data, because it cannot be told apart. If the invoking packet cannot be serialized, the Redirect has no Redirected Header option, and the router logs a warning.

To see the defect, run Rfc4861RedirectFields, with the test repair of the commit before, Rfc4861RedirectedHeader and Rfc6980RedirectLargePacket of tests/protocol/nd on the parent commit: they fail at observations 2, 3 and 4.

Fingerprints: 3 configurations move, in tplx, ~tNl and ~tND, the scenarios with Redirects: ipv6/mipv6 (Handover and RouteOptimizationTwoCNs) and ipv6/mipv6roaming.

Statistical results: 3 move, the scenarios with Redirects: ipv6/mipv6 (Handover and RouteOptimizationTwoCNs) and ipv6/mipv6roaming. The new results are in the statistics repository, branch topic/standards-tests-nd-level2-fixes, commit "ipv6: update statistics after the options of the Redirect".

Plan: plan/pending/nd-level2-fixes.md
Change: src.networklayer.icmpv6 | behavior.change.fix | fingerprint statistical test whatsnew | nd-model-repairs
…tection

Before this commit, the IPv6 variants of the IPsec checks assumed that a host can use its configured address at the start: the flows of A start at 1 second ("The flows" in doc/project/evidence/protocol/ipsec/checks.md). The next change of this branch, ND gap 10, makes a configured address tentative until Duplicate Address Detection tests it, at about 3.3 seconds. Rfc4302AhFormatIpv6, Rfc4302AhIcvPositionIpv6 and Rfc4303EspFormatIpv6 in tests/protocol/ipsec then miss their first AH or ESP packet: B ignores the solicitations of A for its tentative address, and the datagrams of 1 and 2 seconds are lost.

After this commit, the common mockups of the IPsec checks say that an IPv6 node does no Duplicate Address Detection, with DupAddrDetectTransmits 0, which RFC 4862 section 5.1 allows. The six IPv6 tests set dupAddrDetectTransmits = 0. The addresses are usable at the start, as on IPv4, and the checks keep their times. The tests pass before and after this commit, and after ND gap 10.

Plan: plan/pending/nd-level2-fixes.md
Change: tests.protocol | behavior.change.fix | - | nd-model-repairs
…ives

Before this commit, a router did not test its configured address (gap 10 of the ND results). Ipv6NetworkConfigurator assigned a global address as not tentative, assignAddress(globalAddress, false, ...), and nothing started Duplicate Address Detection for it. The router tested its link-local address only.

After this commit, the configurator assigns the address as tentative when the interface does the detection. It applies the per-interface dupAddrDetectTransmits of its XML before the address, so that the decision uses it. When the node has booted, Neighbor Discovery starts the detection for every tentative address of an interface, not only for the link-local one. RFC 4862 section 5.4 tests every unicast address before it is assigned, also a manually configured one, unless DupAddrDetectTransmits is zero.

Two detections can now run on one interface. So the dadInProgress flag of the interface stays set while any of them runs.

To see the defect, run Rfc4862RouterDad of tests/protocol/nd on the parent commit: it fails at observation 2, where R tests its link-local address but not 2001:db8:1::1.

Fingerprints: 39 configurations move, the IPv6 simulations whose configurator assigns the addresses. 25 move in tplx, ~tNl and ~tND, and 14 in tplx and ~tNl. They are bgpv4 (BgpAndOspfv3 General and OspfOnly, BgpDiamondFailover6, BgpIpv6Basic, BgpWithdrawal6 General and Restart), the nine IPv6 configurations of eigrp, inet/ipsec ipv6.ini (Multicast6, TCPTraffic6 and UDPTraffic6), ipv6/ipv6configurator (CustomRA, Default, Hierarchical and ManualRouters), ipv6/mipv6 (Handover and RouteOptimizationTwoCNs), ipv6/mipv6roaming, ipv6/pmipv6, manetrouting/gpsr (DynamicIPv6, IPv6 and MultiIPv6) and the ten configurations of ospfv3.

Statistical results: 39 move, the IPv6 scenarios whose configurator assigns the addresses, for the same reason. The new results are in the statistics repository, branch topic/standards-tests-nd-level2-fixes, commit "ipv6: update statistics after the detection of configured addresses".

Plan: plan/pending/nd-level2-fixes.md
Change: src.networklayer | behavior.change.fix | fingerprint statistical test whatsnew | nd-model-repairs
Before this commit, an expired address stayed in use (gap 11 of the ND results). Host A sent its solicitations from an address whose valid lifetime had ended 12 s before. choosePreferredAddress removes expired addresses, but runs only when an address is added or removed. getPreferredAddress returned the cached address, with the comment "FIXME TODO check expiry time!".

After this commit, Neighbor Discovery has a timer at the earliest end of a valid lifetime among the addresses of all interfaces. The router advertisements and the end of a detection, which set the lifetimes, reschedule it. When the timer fires, the expired addresses are removed with removeAddress, so the ipv6AddressRemoved signal is emitted and the interface leaves the solicited-node group of the address. RFC 4862 section 5.5.4 says that an address whose valid lifetime ends becomes invalid, and an invalid address is neither a source nor a destination.

The new getter Ipv6InterfaceData::getAddressExpiryTime reads the lifetime. The removal of expired addresses in choosePreferredAddress now leaves their groups too.

To see the defect, run Rfc4862AddressLifetime of tests/protocol/nd on the parent commit: it fails at observation 2.

No fingerprint moves.

No statistical result moves.

Plan: plan/pending/nd-level2-fixes.md
Change: src.networklayer | behavior.change.fix | test whatsnew | nd-model-repairs
Before this commit, the plan did not show that gaps 1 to 11 of the ND results are repaired.

After this commit, the plan marks ND gaps 1 to 11 as repaired.

Plan: plan/pending/nd-level2-fixes.md
Change: plan | behavior.change | - | nd-model-repairs
Before this commit, the ND results, the ledger and the matrix did not show the repairs.

After this commit, they come from a fresh run at 8631810 after the eleven repairs: 37 tests, 37 PASS. results.md marks gaps 1 to 11 repaired with the commits of each, and keeps the description of the old code. The three test errors that the repairs made visible are named at their gaps.

Other findings adds the advertising timers that ran in step and the Redirect that is built before the next hop is resolved. It keeps the unit error of the Reachable Time, which stays.

The ledger has no FAIL, with 5 features supported, 13 partial and 5 untested. The matrix has 5 features confirmed and no defect or declined verdict. notes.md gets a "Fixed on" entry for each repaired quirk, the lessons of the repairs about random draws, deadlines at the bound and premises of a check, and new follow-ups. The ledger script is a copy of the level 2 one in audit/nd-level2-fixes/ of inet-master, outside git.

Plan: plan/pending/nd-level2-fixes.md
Change: doc | behavior.change | - | nd-model-repairs
Before this commit, the plan was pending and did not show the end state of the ND level 2 repairs.

After this commit, the plan is done. The eleven gaps are repaired, each in its own commit with its tests. Three test errors that the repairs made visible have commits of their own. The fingerprints and the statistical results of the IPv6 scenarios move with gaps 1, 2, 4 to 10. The new results are in seven commits of the statistics branch of the same name.

Plan: plan/pending/nd-level2-fixes.md
Change: plan | behavior.change | - | nd-model-repairs
Change: plan | location | - | nd-model-repairs

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant