Skip to content

IPv6: three Neighbour Discovery conformance fixes - #1119

Merged
avarga merged 1 commit into
masterfrom
topic/av/ipv6-nd-fixes
Jul 7, 2026
Merged

IPv6: three Neighbour Discovery conformance fixes#1119
avarga merged 1 commit into
masterfrom
topic/av/ipv6-nd-fixes

Conversation

@avarga

@avarga avarga commented Jul 6, 2026

Copy link
Copy Markdown
Member

Three RFC-conformance fixes in Ipv6NeighbourDiscovery:

  • DAD disabled still probedinitiateDad() sent one Neighbor Solicitation before checking DupAddrDetectTransmits, 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.
  • No gratuitous NA — new sendGratuitousNa parameter (default false); when set, each newly configured address is announced with an unsolicited NA to all-nodes once DAD completes (RFC 4861 §7.2.6).
  • RAs not rate-limited — implemented the MIN_DELAY_BETWEEN_RAS enforcement (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

  • DAD fix: pim/dm_ipv6 now emits 0 DAD probes with DAD disabled, multicast delivery intact.
  • RA fix: ipv6/pmipv6 still delivers pings across handover.
  • Fingerprints re-recorded for the affected IPv6 examples and verified deterministic across two runs; attribution confirmed by stash-testing against plain master (all pass without the change). Graphical (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.


Open in Devin Review

@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 found 1 potential issue.

Open in Devin Review

Comment on lines +1210 to +1215
if (scheduledTime < advIfEntry->nextScheduledRATime) {
scheduleAt(scheduledTime, msg);
advIfEntry->nextScheduledRATime = scheduledTime;
}
else
delete msg;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 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
  1. A periodic RA is scheduled at absolute time T1; nextScheduledRATime = T1.
  2. An RS arrives. The code computes scheduledTime = simTime() + delay (< T1), schedules a solicited RA, and sets nextScheduledRATime = scheduledTime (line 1212).
  3. 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 restore nextScheduledRATime back to T1.
  4. Now nextScheduledRATime holds a value in the past (the time the solicited RA was sent).
  5. Another RS arrives. scheduledTime = simTime() + delay is in the future, so scheduledTime < nextScheduledRATime (future < past) is FALSE → the new solicited RA message is deleted (line 1215) and no response is sent.
  6. This persists until the periodic RA timer fires at T1 and sendPeriodicRa resets nextScheduledRATime (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.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@avarga
avarga force-pushed the topic/av/ipv6-nd-fixes branch from 4f8d684 to 26f0c2d Compare July 6, 2026 14:40
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.
@avarga
avarga force-pushed the topic/av/ipv6-nd-fixes branch from 26f0c2d to 632e460 Compare July 7, 2026 07:39
@avarga
avarga merged commit 632e460 into master Jul 7, 2026
@avarga
avarga deleted the topic/av/ipv6-nd-fixes branch July 7, 2026 10:17
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