Skip to content

ipv6nd: a zero Router Lifetime must not expire Route Information routes - #729

Open
akiernan wants to merge 1 commit into
NetworkConfiguration:masterfrom
akiernan:ipv6nd-a-zero-router-lifetime-must-not-expire-route-information-routes
Open

ipv6nd: a zero Router Lifetime must not expire Route Information routes#729
akiernan wants to merge 1 commit into
NetworkConfiguration:masterfrom
akiernan:ipv6nd-a-zero-router-lifetime-must-not-expire-route-information-routes

Conversation

@akiernan

@akiernan akiernan commented Sep 9, 2026

Copy link
Copy Markdown

RFC 4191 section 3.1 separates two lifetimes that a Router Advertisement carries. The Router Lifetime in the message header updates the ::/0 route and nothing else - "a type C host first updates a ::/0 route based on the Router Lifetime and Default Router Preference in the Router Advertisement message header". Each Route Information option is then processed separately and carries its own Route Lifetime, which is what the section's "if the received route's lifetime is zero, the route is removed" refers to.

RFC 4861 section 4.2 defines a Router Lifetime of zero as meaning the sender is not a default router, and RFC 4191 section 2.2 specifies the preference handling for exactly that case, so such advertisements are expected rather than malformed.

ipv6nd_expirera() conflates the two. A router advertising only Route Information with a zero Router Lifetime passes no test in the expiry loop: the Router Lifetime branch is skipped because the lifetime is zero, there are no addresses or prefixes to validate, and the ND option loop handles only DNSSL and RDNSS, so a Route Information option reaches the default case. valid stays false, the router is marked expired, and rt_build() removes its routes - although each of those routes has its own unexpired Route Lifetime, which is the only lifetime section 3.1 gives authority over them.

The next Router Advertisement clears the expired flag and reinstates the routes, so the fault presents as perpetual churn rather than as lost connectivity.

A Thread border router is the common case: it advertises the Thread mesh's off-mesh-routable prefix in a Route Information option, with a zero Router Lifetime because it is not a default router, and no Prefix Information. Measured against an Amazon Echo advertising one /64 with an 1800s Route Lifetime and re-advertising every 130-190s: 545 route add/delete events in 23 hours, 91% of all dhcpcd log output on that host.

Keep the router while any of its route information is unexpired, and let that route's remaining lifetime schedule the next expiry run.

Note that a Route Information option with a prefix length of zero is already unaffected, because it assigns rap->lifetime directly - consistent with section 3.1, where a ::/0 Route Information option overrides the header's lifetime. Only a specific-prefix route is affected.

AI-Assisted: Claude Fable 5 (Claude Code)
Closes #728

RFC 4191 section 3.1 separates two lifetimes that a Router Advertisement
carries. The Router Lifetime in the message header updates the ::/0 route
and nothing else - "a type C host first updates a ::/0 route based on the
Router Lifetime and Default Router Preference in the Router Advertisement
message header". Each Route Information option is then processed
separately and carries its own Route Lifetime, which is what the section's
"if the received route's lifetime is zero, the route is removed" refers
to.

RFC 4861 section 4.2 defines a Router Lifetime of zero as meaning the
sender is not a default router, and RFC 4191 section 2.2 specifies the
preference handling for exactly that case, so such advertisements are
expected rather than malformed.

ipv6nd_expirera() conflates the two. A router advertising only Route
Information with a zero Router Lifetime passes no test in the expiry loop:
the Router Lifetime branch is skipped because the lifetime is zero, there
are no addresses or prefixes to validate, and the ND option loop handles
only DNSSL and RDNSS, so a Route Information option reaches the default
case. `valid` stays false, the router is marked expired, and rt_build()
removes its routes - although each of those routes has its own unexpired
Route Lifetime, which is the only lifetime section 3.1 gives authority
over them.

The next Router Advertisement clears the expired flag and reinstates the
routes, so the fault presents as perpetual churn rather than as lost
connectivity.

A Thread border router is the common case: it advertises the Thread mesh's
off-mesh-routable prefix in a Route Information option, with a zero Router
Lifetime because it is not a default router, and no Prefix Information.
Measured against an Amazon Echo advertising one /64 with an 1800s Route
Lifetime and re-advertising every 130-190s: 545 route add/delete events in
23 hours, 91% of all dhcpcd log output on that host.

Keep the router while any of its route information is unexpired, and let
that route's remaining lifetime schedule the next expiry run.

Note that a Route Information option with a prefix length of zero is
already unaffected, because it assigns rap->lifetime directly - consistent
with section 3.1, where a ::/0 Route Information option overrides the
header's lifetime. Only a specific-prefix route is affected.

AI-Assisted: Claude Fable 5 (Claude Code)
Signed-off-by: Alex Kiernan <alex.kiernan@gmail.com>
@coderabbitai

coderabbitai Bot commented Sep 9, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: ad086362-bb18-4b2b-a153-d20261d22911

📥 Commits

Reviewing files that changed from the base of the PR and between 42ca579 and ca55e34.

📒 Files selected for processing (1)
  • src/ipv6nd.c

Included review availability: Your plan provides up to 2 included reviews per hour; 1 remains after this review.


Walkthrough

ipv6nd_expirera now keeps a router valid while its Route Information entries remain active. It also schedules the next expiry from those route lifetimes and skips freed entries after removal.

Changes

IPv6 route expiry

Layer / File(s) Summary
Route Information validity and expiry
src/ipv6nd.c
The expiry loop continues after removing expired entries. It marks routers with active routes as valid and uses finite route lifetimes for the next expiry timer.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to ca55e

IPv6 Route Information routes now remain available for their advertised lifetimes even when the Router Lifetime is zero, preventing route churn for route-only advertisements. The change is ready to merge with no active current-head risk identified.

Suggested reviewers: rsmarples

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 1 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: preventing a zero Router Lifetime from expiring valid Route Information routes.
Description check ✅ Passed The description directly explains the RFC requirements, the existing expiry defect, the route-churn impact, and the proposed fix.
Linked Issues check ✅ Passed The change satisfies issue #728 by preserving unexpired specific-prefix Route Information routes when Router Lifetime is zero and by scheduling expiry from the remaining route lifetime.
Out of Scope Changes check ✅ Passed The reported changes are limited to IPv6 Router Advertisement expiry handling and directly support issue #728. No unrelated changes are identified.
  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread src/ipv6nd.c
rap->iface->name, rinfo->sprefix);
TAILQ_REMOVE(&rap->rinfos, rinfo, next);
free(rinfo);
continue;

@rsmarples rsmarples Sep 12, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we also need to set expired = true here so that rt_build() is called below.

Comment thread src/ipv6nd.c
Comment on lines +1858 to +1866
* RFC 4191 section 3.1: the Router Lifetime in the
* advertisement header governs the ::/0 route only,
* while each Route Information option carries its own
* Route Lifetime. A router with a zero Router
* Lifetime is not a default router (RFC 4861 section
* 4.2) but its advertised routes remain valid for
* their own lifetimes, so keep it alive here - the
* Router Lifetime test above cannot see that, and no
* other test covers a Route Information option.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This comment is far too wordy.
The missing code here is just an oversight, not a rationale so it could live in the commit message
and this comment could just read.
/* A non expired route information option keeps the RA valid. */

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.

dhcpcd expires a route-only (RFC 4191) router on every pass, churning its routes

2 participants