Skip to content

ipv6: advertise soon after an interface starts advertising - #1181

Open
adamgeorge309 wants to merge 1 commit into
masterfrom
topic/gy/ipv6-initial-ra
Open

ipv6: advertise soon after an interface starts advertising#1181
adamgeorge309 wants to merge 1 commit into
masterfrom
topic/gy/ipv6-initial-ra

Conversation

@adamgeorge309

@adamgeorge309 adamgeorge309 commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

A router's first unsolicited Router Advertisement was due 200 s to 600 s after
startup, so hosts next to it had to solicit and no simulation shorter than
MinRtrAdvInterval saw an unsolicited advertisement at all. RFC 4861 Section
6.2.4 asks for the first few to be at most MAX_INITIAL_RTR_ADVERT_INTERVAL --
16 s -- apart. The clamp that does this is implemented in INET, but in a place
the first advertisement never reaches.

Closes #1178

The problem

sendPeriodicRa() holds the Section 6.2.4 clamp, and it runs only after an
advertisement has already gone out. The timer that produces the first
advertisement is set by createRaTimer(), which has no clamp. So the mechanism
could never affect the advertisement it exists for.

In examples/ipv6/nclients, config ETH, over the first 30 s on master:

Router Advertisements sent 5
of those, unsolicited 0
first unsolicited advertisement scheduled for 208 s to 583 s, per advertising interface

Nine advertising interfaces exist in that network and not one of them advertises
on its own within the run.

The fix

The clamp does not belong in createRaTimer() either. That runs at startup,
where Section 6.2.2 does not yet call the interface an advertising interface --
it has no unicast address -- and where Section 4.2 has no link-local address for
it to source an advertisement from. Clamping there also gives every router on a
link the same deadline measured from t=0, which is the synchronization between
routers that Section 6.2.4's randomized interval exists to prevent; with that
variant, MIPv6_movement_detection stops on the RadioMedium check for two
transmissions starting at precisely the same simulation time.

The clamp is applied where the interface actually becomes an advertising
interface: in makeTentativeAddressPermanent(), once link-local Duplicate
Address Detection has verified the address the advertisement will be sourced
from. That is the counterpart of the Router Solicitation the same function
already starts for a host. Routers reach it at different times, because the
bootup delay and the Duplicate Address Detection delay are both random, so the
advertisements stay spread out.

The timer is only ever brought forward, never pushed back, so an interface
already advertising more often than every MAX_INITIAL_RTR_ADVERT_INTERVAL keeps
its schedule untouched -- examples/ipv6/mipv6, which advertises every 3 s to
7 s, does not move.

After the fix, in the same 30 s: the nine advertising interfaces become
advertising interfaces between 1.31 s and 1.97 s and each advertises 16 s later.
The run carries 14 Router Advertisements instead of 5, the nine new ones
unsolicited.

Verification

cd tests/fingerprint && ./fingerprinttest -s -F tyf
cd tests/module      && inet_run_module_tests   -m release -f 'IPv6|MIPv6|Ipv6'
cd tests/protocol/ipv6 && inet_run_protocol_tests -m release
bash doc/project/enforcement/check-commits.sh origin/master..HEAD

The fingerprint suite is run whole -- all 1774 rows of every .csv, not an
IPv6-filtered subset -- because this change moves rows whose configuration names
do not contain the string ipv6: examples/bgpv4, examples/manetrouting/gpsr,
examples/rip/simpletest and examples/inet/hierarchical99.

Unmodified master gives 1774 rows with 3 failures and 62 errors, and so does
this branch. The 62 errors are disabled optional features -- VoIPStream,
TcpLwip, VoipStreamSender -- and have nothing to do with IPv6. The 3 failures
are examples/ipv6/mipv6 Handover and RouteOptimizationTwoCNs and
examples/ipv6/mipv6roaming Roaming, all ~tNlb rows of
mipv6-refactoring.csv. Their recorded values are stale on master already, so
they keep them here; re-recording them would absorb that staleness rather than
fix it.

Baselines re-recorded: 25 rows, 19 in examples.csv and 6 in
mipv6-refactoring.csv. The affected examples are
examples/ipv6/nclients, examples/ipv6/mld, examples/pim over IPv6,
examples/bgpv4 over IPv6, examples/inet/hierarchical99 IPv6,
examples/manetrouting/gpsr and examples/rip/simpletest -- every example whose
routers advertise less often than every MAX_INITIAL_RTR_ADVERT_INTERVAL and which
runs long enough to reach the first advertisement.

The moved ingredients follow from those advertisements. 't' moves because they
and everything they trigger happen at new times; 'p' and 'N' because events now
occur in modules and nodes that had none at those points; 'l' and 'b' because
advertisements that were not in the hash before contribute their length and their
contents; 'x' with the event sequence. Graphical ('tyf') fingerprints were
excluded from every run and are unchanged.

Landing order against #1183

#1183, the pull request for #1179, re-records 20 of the same fingerprint rows as this branch -- 16 in
examples.csv and 4 in mipv6-refactoring.csv, in examples/ipv6/nclients,
examples/bgpv4 over IPv6, examples/inet/hierarchical99 IPv6,
examples/manetrouting/gpsr and examples/rip/simpletest. Both branches record
those values against plain master, because each has to stand on its own, so the
two conflict textually in tests/fingerprint/examples.csv and
tests/fingerprint/mipv6-refactoring.csv, and whichever lands second needs
those 20 rows re-recorded on top of the first
. Ping me and I will do it for
whichever order you prefer.

The source changes do not conflict: they touch disjoint parts of
Ipv6NeighbourDiscovery.cc.

tests/fingerprint/store.json, the separate expectation store opp_repl reads,
is deliberately left alone. It is already out of step with the .csv baselines
for these rows on master: for examples/ipv6/nclients ETH at the same
directory, ini file, configuration, run and time limit it holds tplx
c5d4-8871 where examples.csv holds 5ceb-d724. That drift predates this
branch and, as 3a9cc27 records, no single source commit causes it; it belongs
in a baseline-only commit of its own.

Module tests: 42 tests, 40 pass. MIPv6_tcp_handover and IPv6_packet_too_big
fail identically on unmodified master.

Protocol tests, tests/protocol/ipv6: 29 tests, 21 pass and 8 expected failures
-- the same result as unmodified master.

check-commits.sh passes. check-architecture.sh and check-naming.sh report
only candidates already present on master, in src/inet/applications/sctpapp,
src/inet/applications/rtpapp and images/misc.

Architectural surface

No contract, packet content, configuration surface or feature descriptor
changes, and no sealed path is touched. No new AV-* or NV-* row is needed.

Not addressed here

sendPeriodicRa() clamps the timer while
numRASent <= MAX_INITIAL_RTR_ADVERTISEMENTS. Whether that should be <
depends on whether Section 6.2.4 counts the timer that produces an advertisement
or the timer set after it; the two readings differ by one advertisement and this
branch leaves the existing one alone.

Two further Neighbour Discovery conformance defects are fixed separately, in
#1179 (the Duplicate Address Detection delay) and #1180 (address lifetimes on a
repeated prefix).

A router waited a uniform draw between MinRtrAdvInterval and
MaxRtrAdvInterval -- 200 s and 600 s by default -- for its first
unsolicited Router Advertisement. Hosts next to it therefore had to
solicit, and no simulation shorter than MinRtrAdvInterval saw an
unsolicited advertisement at all. In examples/ipv6/nclients, config ETH,
all five Router Advertisements of the first 30 s answered a Router
Solicitation.

RFC 4861 Section 6.2.4 asks for the opposite:

    For the first few advertisements (up to MAX_INITIAL_RTR_ADVERTISEMENTS)
    sent from an interface when it becomes an advertising interface, if the
    randomly chosen interval is greater than MAX_INITIAL_RTR_ADVERT_INTERVAL,
    the timer SHOULD be set to MAX_INITIAL_RTR_ADVERT_INTERVAL instead.
    Using a smaller interval for the initial advertisements increases the
    likelihood of a router being discovered quickly when it first becomes
    available, in the presence of possible packet loss.

The clamp was implemented, but only in sendPeriodicRa(), which runs after
an advertisement has already been sent. The timer that produces the first
advertisement is set by createRaTimer(), which had no clamp, so the
mechanism could never affect the advertisement it exists for.

The clamp does not belong in createRaTimer() either. That runs at startup,
where Section 6.2.2 does not yet call the interface an advertising
interface -- it has no unicast address -- and where Section 4.2 has no
link-local address for it to source an advertisement from. Clamping there
also sets every router on a link to the same MAX_INITIAL_RTR_ADVERT_INTERVAL
from t=0, which is the synchronization between routers that the randomized
interval of Section 6.2.4 exists to prevent: with that variant,
MIPv6_movement_detection stopped on the RadioMedium check for two
transmissions starting at precisely the same time.

Apply the clamp where the interface actually becomes an advertising
interface: in makeTentativeAddressPermanent(), once link-local Duplicate
Address Detection has verified the address the advertisement will be sourced
from. That is the counterpart of the Router Solicitation that the same
function already starts for a host. Routers reach it at different times,
because the bootup delay and the Duplicate Address Detection delay are both
random, so the advertisements stay spread out.

The timer is only brought forward, never pushed back, so an interface
configured to advertise more often than every MAX_INITIAL_RTR_ADVERT_INTERVAL
keeps its schedule untouched; examples/ipv6/mipv6, which advertises every
3 s to 7 s, is unaffected.

In examples/ipv6/nclients, config ETH, the nine advertising interfaces now
become advertising interfaces between 1.31 s and 1.97 s and each advertises
16 s later. The first 30 s carry 14 Router Advertisements instead of 5.

The re-recorded fingerprints follow from those advertisements, in every
example whose routers advertise less often than every
MAX_INITIAL_RTR_ADVERT_INTERVAL and which runs long enough to reach the
first one: examples/ipv6/nclients, examples/ipv6/mld, examples/pim over
IPv6, examples/bgpv4 over IPv6, examples/inet/hierarchical99 IPv6,
examples/manetrouting/gpsr and examples/rip/simpletest. 't' moves because
the advertisements and everything they trigger happen at new times; 'p' and
'N' because events now occur in modules and nodes that had none at those
points; 'l' and 'b' because advertisements that were not in the hash before
contribute their length and their contents; 'x' with the event sequence.
Graphical ('tyf') fingerprints were excluded from the run and are unchanged.

The three rows that already fail on master -- examples/ipv6/mipv6 Handover
and RouteOptimizationTwoCNs, and examples/ipv6/mipv6roaming Roaming, all in
mipv6-refactoring.csv -- produce the same values as before this change and
keep their recorded ones.
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.

ipv6: the MAX_INITIAL_RTR_ADVERT_INTERVAL clamp never reaches the first Router Advertisement

1 participant