Skip to content

ipv6: answer every Router Solicitation, not only the first one - #1139

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

ipv6: answer every Router Solicitation, not only the first one#1139
adamgeorge309 wants to merge 1 commit into
masterfrom
topic/gy/ipv6-solicited-ra-ignored

Conversation

@adamgeorge309

@adamgeorge309 adamgeorge309 commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

An advertising IPv6 interface answered the first Router Solicitation (RS) it received and then
silently discarded every later one until its next periodic Router Advertisement (RA) was due. With
INET's own NED defaults for the periodic interval — 200 s / 600 s, which are also RFC 4861's
MinRtrAdvInterval / MaxRtrAdvInterval — that window is far longer than a host's Router
Discovery budget, so a host that solicited second never autoconfigured a global address and stayed
unreachable for the rest of the run, with no error and a zero exit code.

Closes #1138

The problem

processRsPacket() compares the computed send time of a solicited advertisement against
advIfEntry->nextScheduledRATime, which per RFC 4861 Section 6.2.6 is the time the next
multicast Router Advertisement is scheduled to be sent. Scheduling a solicited advertisement
correctly sets the field to its own send time, because it then is the next multicast advertisement
to go out — but sendSolicitedRa() never moved the field on once that advertisement had been
sent. It kept pointing at an advertisement already in the past, so every later solicitation
computed a send time in the future, found it "later than" a past timestamp, and fell into the
branch that discards the solicitation.

Measured on the added module test, with minIntervalBetweenRAs = 200 s and
maxIntervalBetweenRAs = 600 s. hostB is shut down at t = 1 s and restarted at t = 20 s, so it
loses its addresses and has to redo Router Discovery at a time when no periodic advertisement is
due:

t (s) event on master with this change
23.635015 hostB's solicitation 1 reaches R discarded answered
23.705191 R sends the solicited Router Advertisement sent
23.705399 hostB assigns its new address on eth0 assigned
27.635015 solicitation 2 reaches R discarded not sent — already configured
31.635015 solicitation 3 reaches R discarded not sent
32.634884 hostB: No RA messages were received logged
40–60 hostB sends 20 UDP packets to srv srv receives 0 srv receives 20

The defect

sendSolicitedRa() now points nextScheduledRATime back at the pending periodic advertisement
once the solicited one has been sent. createRaTimer() is the only place an AdvIfEntry is
created and it always schedules raTimeoutMsg, and sendPeriodicRa() reschedules it before
returning, so the timer is always pending at a future time whenever the entry exists.

The else branch is also given a log line. It is now taken only when a future advertisement
really will serve the solicitation, which is the case RFC 4861 Section 6.2.6 describes.

Verification

tests/module/IPv6_RA_repeated_solicitations.test is new. It shuts a host down and restarts it so
that it has to redo Router Discovery while no periodic advertisement is due. It fails on
unmodified master (No RA messages were received, srv receives 0 packets) and passes here.

All 39 IPv6* and MIPv6* module tests pass.

Fingerprints re-recorded for the IPv6 examples that have two or more soliciting hosts on one link:
ipv6/mipv6 (Handover), ipv6/mld (MldV2Ssm), ospfv3/small, ospfv3/multiple_areas_FINAL (all
three configurations) and inet/hierarchical99 (IPv6). Every ingredient of those fingerprints
moves for one reason: the router now emits Router Advertisements it used to discard. That adds
events (t), in modules that previously saw none at that point (p, N), carrying Router
Advertisement payloads that were not in the hashed stream before (l, D, b), and everything
downstream on those nodes shifts (x). The graphical (tyf) ingredients were excluded from the
run and are left untouched. Verified stable across two independent runs.

IPv6 examples with only one soliciting host per router interface are unaffected and were not
re-recorded: ipv6/nclients (each client has its own point-to-point link) and the
inet/udpclientserver IPv6 configurations all still pass.

ipv6/mipv6roaming (Roaming, ~tNlb) fails both before and after this change with the same
actual value 7ed8-bee3, so it is pre-existing and deliberately not re-recorded here. Confirmed
by reverting only the Ipv6NeighbourDiscovery.cc hunk, rebuilding, and rerunning that row.

Not addressed here

The host protocol constants IPv6_MAX_RTR_SOLICITATIONS, IPv6_RTR_SOLICITATION_INTERVAL and
IPv6_MAX_RTR_SOLICITATION_DELAY remain unreachable from an ini file — their setters have no
callers in src/. Recorded in the issue.


Open in Devin Review

An advertising interface answered the first Router Solicitation it received
and then silently discarded every later one until its next periodic Router
Advertisement was due. With the RFC 4861 default MinRtrAdvInterval and
MaxRtrAdvInterval (200s and 600s, which are also INET's NED defaults) that
window is far longer than a host's Router Discovery budget of
MAX_RTR_SOLICITATIONS * RTR_SOLICITATION_INTERVAL, about 9s, so a host that
solicited second gave up with "No RA messages were received. Assume no routers
are on-link", never autoconfigured a global address, and stayed unreachable for
the rest of the run, with no error and a zero exit code.

processRsPacket() decides whether to schedule a solicited advertisement by
comparing the computed send time against advIfEntry->nextScheduledRATime,
which per RFC 4861 Section 6.2.6 must be the time the *next* multicast Router
Advertisement is scheduled to be sent:

    - Upon receipt of a Router Solicitation, compute a random delay within
      the range 0 through MAX_RA_DELAY_TIME.  If the computed value
      corresponds to a time later than the time the next multicast Router
      Advertisement is scheduled to be sent, ignore the random delay and
      send the advertisement at the already-scheduled time.

Scheduling a solicited advertisement correctly sets nextScheduledRATime to its
send time, because it then is the next multicast advertisement to go out. But
sendSolicitedRa() never moved the field on once that advertisement had been
sent, so it kept pointing at an advertisement already in the past. Every later
solicitation computed a send time in the future, found it "later than" a past
timestamp, and fell into the ignore branch -- where nothing then served the
solicitation, although Section 6.2.6 also states that "a router sends
advertisements in response to valid solicitations received on an advertising
interface".

Point nextScheduledRATime back at the pending periodic advertisement once the
solicited one has been sent. Also log the ignore path, which is now only taken
when a future advertisement really will serve the solicitation.

The comparison itself was made dimensionally correct only recently, in
632e460 ("IPv6: three Neighbour Discovery conformance fixes"); before that
it compared a duration against an absolute time, which was wrong but almost
always true, so the ignore branch was effectively unreachable and the stale
field did no harm.

Adds tests/module/IPv6_RA_repeated_solicitations.test, which shuts a host down
and restarts it so that it has to redo Router Discovery while no periodic
advertisement is due. The test fails on unmodified master.

Fingerprints re-recorded for the IPv6 examples that have two or more soliciting
hosts on one link: ipv6/mipv6 (Handover), ipv6/mld (MldV2Ssm), ospfv3/small,
ospfv3/multiple_areas_FINAL (all three configurations) and inet/hierarchical99
(IPv6). Every ingredient of those fingerprints moves for one reason: the router
now emits Router Advertisements it used to discard. That adds events (t), in
modules that previously saw none at that point (p, N), carrying Router
Advertisement payloads that were not in the hashed stream before (l, D, b), and
everything downstream on those nodes shifts (x). The graphical (tyf)
ingredients were excluded from the run and are left untouched. Verified stable
across two independent runs.

IPv6 examples with only one soliciting host per router interface are unaffected
and were not re-recorded: ipv6/nclients (each client has its own point-to-point
link) and inet/udpclientserver IPv6 configurations all still pass.

ipv6/mipv6roaming (Roaming, ~tNlb) fails both before and after this change with
the same actual value 7ed8-bee3, so it is pre-existing and deliberately not
re-recorded here.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 1 additional finding.

Open in Devin Review

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: sendSolicitedRa() leaves nextScheduledRATime in the past, so every later Router Solicitation is discarded

1 participant