IPv6: three Neighbour Discovery conformance fixes - #1119
Conversation
| if (scheduledTime < advIfEntry->nextScheduledRATime) { | ||
| scheduleAt(scheduledTime, msg); | ||
| advIfEntry->nextScheduledRATime = scheduledTime; | ||
| } | ||
| else | ||
| delete msg; |
There was a problem hiding this comment.
🔴 Solicited router advertisements silently dropped after the first one fires until the next periodic advertisement
The next-scheduled-RA timestamp is overwritten when a solicited advertisement is queued (advIfEntry->nextScheduledRATime = scheduledTime at src/inet/networklayer/icmpv6/Ipv6NeighbourDiscovery.cc:1212) but never restored after that advertisement fires, so subsequent router solicitations are silently ignored.
Impact: A router stops responding to host Router Solicitations for up to 600 seconds (the default periodic RA interval), delaying address configuration for newly-joining hosts.
Mechanism: stale nextScheduledRATime after solicited RA fires
- A periodic RA is scheduled at absolute time T1;
nextScheduledRATime = T1. - An RS arrives. The code computes
scheduledTime = simTime() + delay(< T1), schedules a solicited RA, and setsnextScheduledRATime = scheduledTime(line 1212). - The solicited RA fires via
sendSolicitedRa(src/inet/networklayer/icmpv6/Ipv6NeighbourDiscovery.cc:1723-1731), which sends the RA and deletes the message but does NOT restorenextScheduledRATimeback to T1. - Now
nextScheduledRATimeholds a value in the past (the time the solicited RA was sent). - Another RS arrives.
scheduledTime = simTime() + delayis in the future, soscheduledTime < nextScheduledRATime(future < past) is FALSE → the new solicited RA message is deleted (line 1215) and no response is sent. - This persists until the periodic RA timer fires at T1 and
sendPeriodicRaresetsnextScheduledRATime(line 1718).
The old code accidentally avoided this because it compared a duration (interval) against the absolute nextScheduledRATime, which was almost always true. The new code uses the correct absolute-time comparison but exposes the stale-timestamp problem.
Prompt for agents
The problem is that nextScheduledRATime becomes stale after a solicited RA fires. The periodic RA timer is still running at its original time T1, but nextScheduledRATime was overwritten to the solicited RA's time. After the solicited RA fires, nextScheduledRATime should reflect the periodic RA's scheduled time (T1).
Possible approaches:
1. In sendSolicitedRa(), after sending the RA, look up the advIfEntry and restore nextScheduledRATime to the time the periodic RA timer is scheduled to fire. This requires knowing when the periodic timer will fire (could store it separately or compute from the raTimeoutMsg's arrival time).
2. Don't update nextScheduledRATime when scheduling a solicited RA. Instead, track the solicited RA's scheduled time separately (e.g., in a separate field or by checking if a solicited RA msg is already scheduled).
3. In sendSolicitedRa(), after sending, set nextScheduledRATime to the periodic RA timer's scheduled arrival time using advIfEntry->raTimeoutMsg->getArrivalTime().
The key insight is that nextScheduledRATime must always reflect the actual next time a multicast RA will be sent, which after a solicited RA fires is the periodic RA's scheduled time.
Was this helpful? React with 👍 or 👎 to provide feedback.
4f8d684 to
26f0c2d
Compare
Fix three RFC-conformance issues in Ipv6NeighbourDiscovery:
- DAD disabled still probed: initiateDad() sent one Neighbor Solicitation
before checking DupAddrDetectTransmits, so an interface with DAD disabled
(dupAddrDetectTransmits = 0) still emitted a spurious probe for global
addresses (the link-local path guarded the call, the global/SLAAC paths
did not). Short-circuit initiateDad() to make the address permanent
immediately when DAD is disabled (RFC 4862 Section 5.4), which fixes all
call sites; the now-redundant link-local guard is simplified.
- No gratuitous Neighbor Advertisement: add a sendGratuitousNa parameter
(default false) that, when set, announces each newly configured address
with an unsolicited NA to the all-nodes multicast group once DAD
completes (RFC 4861 Section 7.2.6), so neighbors refresh their caches
without waiting for Neighbor Unreachability Detection. sendUnsolicitedNa()
gains an optional target-address argument.
- Router Advertisements were not rate-limited: the MIN_DELAY_BETWEEN_RAS
enforcement was an unimplemented stub, and the solicited-RA scheduling
compared a delay (duration) against nextScheduledRATime (absolute time).
Track the time of the last multicast RA per advertising interface and
defer a solicited RA to at least MIN_DELAY_BETWEEN_RAS after it, and fix
the scheduling comparison (RFC 4861 Section 6.2.6). Also plugs a small
leak of the solicited-RA timer message on the ignore path.
Fingerprints re-recorded for the affected IPv6 examples (DAD-disabled:
pim/{dm,sm,ssm}_ipv6, ipv6/mld; RA timing: ipv6/mipv6, ipv6/mipv6roaming,
ipv6/pmipv6, ospfv3/small, ospfv3/multiple_areas_FINAL, inet/hierarchical99
[IPv6 config]). Verified stable across two independent runs; graphical
(tyf) fingerprints left as-is.
26f0c2d to
632e460
Compare
Three RFC-conformance fixes in
Ipv6NeighbourDiscovery:initiateDad()sent one Neighbor Solicitation before checkingDupAddrDetectTransmits, so an interface with DAD disabled (dupAddrDetectTransmits = 0) still emitted a spurious probe for global addresses. Now short-circuits to make the address permanent immediately when DAD is disabled (RFC 4862 §5.4), fixing all call sites.sendGratuitousNaparameter (defaultfalse); when set, each newly configured address is announced with an unsolicited NA to all-nodes once DAD completes (RFC 4861 §7.2.6).MIN_DELAY_BETWEEN_RASenforcement (was an unimplemented stub) and fixed a duration-vs-absolute-time comparison in the solicited-RA scheduling (RFC 4861 §6.2.6); also plugs a small timer-message leak.Verification
pim/dm_ipv6now emits 0 DAD probes with DAD disabled, multicast delivery intact.ipv6/pmipv6still delivers pings across handover.tyf) fingerprints left as-is per convention.Not included (needs a separate effort)
The OSPFv3 "Router-LSA originates with 0 entries" issue was investigated: the origination code is correct, but the OSPFv3 neighbor adjacency never reaches
FULL_STATE(no routes installed) — a state-machine bug, out of scope here.