Skip to content

router mac revisit#1649

Merged
qmonnet merged 3 commits into
mainfrom
pr/fredi/router_mac_revisit
Jul 21, 2026
Merged

router mac revisit#1649
qmonnet merged 3 commits into
mainfrom
pr/fredi/router_mac_revisit

Conversation

@Fredi-raspall

Copy link
Copy Markdown
Contributor

No description provided.

@Fredi-raspall
Fredi-raspall requested a review from qmonnet July 20, 2026 15:38
@Fredi-raspall Fredi-raspall self-assigned this Jul 20, 2026
Copilot AI review requested due to automatic review settings July 20, 2026 15:38
@Fredi-raspall
Fredi-raspall requested a review from a team as a code owner July 20, 2026 15:38
@Fredi-raspall Fredi-raspall added ci:+release Enable VLAB release tests ci:+vlab Enable VLAB tests labels Jul 20, 2026
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

MAC formatting now uses a preallocated buffer. Routing retains stale RMAC entries for timed cleanup and VNI-scoped FIB refresh, while next-hop resolution records invalid state and drops unusable paths. CLI output reports both states.

Changes

MAC display formatting

Layer / File(s) Summary
Preallocated MAC rendering
net/src/eth/mac.rs
MAC addresses are built in an ArrayString and emitted with formatter padding.

RMAC stale-entry lifecycle

Layer / File(s) Summary
RMAC stale state and storage
routing/src/evpn/rmac.rs
RMAC entries retain stale timestamps, and stores track stale counts while supporting conditional deletion and expiration flushing.
RMAC mutation and validation
routing/src/evpn/rmac.rs, routing/src/router/cpi.rs, routing/src/router/rpc_adapt.rs
RPC deletion invalidates entries, RPC conversion initializes fresh entries, and tests use the updated store APIs.
RMAC cleanup, FIB refresh, and display
routing/src/router/rio.rs, routing/src/rib/vrftable.rs, routing/src/cli/display.rs
Expired RMACs trigger affected-VNI FIB refreshes, and CLI output reports stale status and counts.

Next-hop validity tracking

Layer / File(s) Summary
Invalid next-hop instruction handling
routing/src/rib/nexthop.rs, routing/src/rib/rib2fib.rs
Next-hops track invalid state, and missing interfaces or failed encapsulation resolution produce drop instructions.
Invalid status display
routing/src/cli/display.rs
CLI next-hop output appends (INVALID) for invalid entries.

Possibly related PRs

  • githedgehog/dataplane#1607: Both changes modify the router I/O loop, although that PR addresses control-channel wakeups while this one adds stale-RMAC cleanup and FIB refresh.

Suggested reviewers: qmonnet

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is related to router MAC changes, but it is too vague to identify the main change. Rename it to describe the primary change, such as router MAC invalidation and stale-entry handling.
Description check ❓ Inconclusive No description was provided, so the check cannot assess the change details beyond a minimal relation. Add a brief description summarizing the RMAC and routing changes and why they were made.
✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
routing/src/evpn/rmac.rs (1)

242-266: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Tests don't cover the production stale-marking or flush paths.

rmac_store_basic only exercises del_rmac (test-only immediate removal). The production paths del_rmac_entry (stale marking) and flush_stale (timed removal) have no test coverage. Consider adding tests that verify: (1) del_rmac_entry marks entries stale and increments the counter, (2) duplicate del_rmac_entry calls are idempotent, (3) flush_stale removes only entries older than MAX_STALE_TIME and correctly decrements the counter, (4) add_rmac_entry on a stale entry resets it and decrements the counter.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@routing/src/evpn/rmac.rs` around lines 242 - 266, Extend the RMAC store tests
around rmac_store_basic to cover the production lifecycle: verify del_rmac_entry
marks an entry stale and increments the stale counter, repeated calls are
idempotent, flush_stale removes only entries older than MAX_STALE_TIME while
decrementing the counter, and add_rmac_entry resets a stale entry while
decrementing the counter. Use controllable entry timestamps or the existing time
abstractions so the age boundary is tested deterministically.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@routing/src/evpn/rmac.rs`:
- Around line 106-119: Update del_rmac_entry so it increments self.stale and
sets the stale timestamp only when current.stale is None; leave already-stale
entries unchanged, while preserving the existing MAC-match guard and logging
behavior.
- Around line 149-179: Update flush_stale to decrement self.stale by the number
of stale entries actually removed from self.table, using the successful
remove_entry results rather than stale.len(). Preserve the existing scan,
logging, and return value while ensuring the counter remains accurate after each
flush.

---

Nitpick comments:
In `@routing/src/evpn/rmac.rs`:
- Around line 242-266: Extend the RMAC store tests around rmac_store_basic to
cover the production lifecycle: verify del_rmac_entry marks an entry stale and
increments the stale counter, repeated calls are idempotent, flush_stale removes
only entries older than MAX_STALE_TIME while decrementing the counter, and
add_rmac_entry resets a stale entry while decrementing the counter. Use
controllable entry timestamps or the existing time abstractions so the age
boundary is tested deterministically.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2d71dc34-2ec6-46d8-b107-83c670a28c6b

📥 Commits

Reviewing files that changed from the base of the PR and between c3cd8ed and 3ddab79.

📒 Files selected for processing (5)
  • net/src/eth/mac.rs
  • routing/src/cli/display.rs
  • routing/src/evpn/rmac.rs
  • routing/src/router/rio.rs
  • routing/src/router/rpc_adapt.rs

Comment thread routing/src/evpn/rmac.rs Outdated
Comment thread routing/src/evpn/rmac.rs Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR revisits EVPN router-MAC (RMAC) handling by introducing a “stale” lifecycle for RMAC entries (mark on delete, flush after a timeout) and exposing that state via the CLI, while also adjusting MAC address formatting.

Changes:

  • Add staleness tracking to RmacEntry/RmacStore and a periodic flush_stale() garbage-collection step.
  • Update CLI output to show RMAC entry status (ok/stale) and include a stale counter in the heading.
  • Improve Mac Display formatting by rendering into a fixed-size buffer and using formatter padding.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
routing/src/router/rpc_adapt.rs Initializes new RmacEntry staleness field when adapting from RPC objects.
routing/src/router/rio.rs Periodically flushes expired stale RMAC entries during the main I/O loop.
routing/src/evpn/rmac.rs Introduces stale marking, stale counting, and timed flushing of RMAC entries.
routing/src/cli/display.rs Displays RMAC staleness state and stale count in CLI output.
net/src/eth/mac.rs Updates MAC Display implementation to use a fixed-size buffer and respect formatter padding.

Comment thread routing/src/evpn/rmac.rs Outdated
Comment thread routing/src/evpn/rmac.rs Outdated
Comment thread routing/src/evpn/rmac.rs Outdated
Copilot AI review requested due to automatic review settings July 20, 2026 18:12
@Fredi-raspall
Fredi-raspall force-pushed the pr/fredi/router_mac_revisit branch from 3ddab79 to 32ab06e Compare July 20, 2026 18:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Comment thread routing/src/evpn/rmac.rs Outdated
Comment thread routing/src/evpn/rmac.rs Outdated
Comment thread routing/src/router/rio.rs Outdated
Impl Display for Mac such that formatting is preserved. We use
ArrayString in stack so that no allocation is needed, using the
fact that a mac, as a hex string, has a fixed length.

Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
Router macs are learnt separately from evpn routes. These are
sent before the routes using them. The same occurs on deletions,
e.g. when the bgp session of a leaf switch is torn down.

Dataplane resolves next-hops, not routes, and does it pretty
agressively to keep a correct state, even on route deletions,
because next-hops are shared. When a leaf-switch bgp session
(even one the gateway does not participate in) flaps or is torn
down, the router macs get deleted, so are the routes. This means
that dataplane may attempt to resolve next-hops (with encap info)
referring to a vtep/irb for which the router mac is no longer known,
producing multiple warnings.

Omitting the resolution on route deletions is not viable since our
resolution is per next-hop, not per route.

Solve this by keeping the router macs for some time interval,
(instead of deleting them immediately), so that next-hops (which
may end up disapparing anyway) are still correctly resolved.

This should not cause any operational issue because if router macs
are gone, so are the routes using them later on. So, temporarily
keeping the macs does not harm, and up-to-date router macs may be
re-established when a (far) bgp session comes up again.

The solution marks router macs as "stale" temporarily until they
get removed or restored.

Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
Invalidate next-hops if they have vxlan encapsulation and we fail
to resolve the rmac. Such next-hops are retained and used. However,
their packet instructions are set to drop the traffic. We should
very rarely see an invalid next-hop.

Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
Copilot AI review requested due to automatic review settings July 21, 2026 12:38
@Fredi-raspall
Fredi-raspall force-pushed the pr/fredi/router_mac_revisit branch from 32ab06e to c6ab732 Compare July 21, 2026 12:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Comment thread routing/src/evpn/rmac.rs
Comment on lines 16 to 20
pub address: IpAddr,
pub mac: Mac,
pub vni: Vni,
pub stale_t: Option<Instant>, // instant when the rmac was deleted
}

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (3)
routing/src/evpn/rmac.rs (3)

98-114: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Double hashmap lookup in del_rmac.

self.table.entry(key) followed by self.table.remove_entry(&key) performs two lookups. Since the occupied entry o isn't used after the staleness check, this compiles under NLL, but it's more idiomatic (and marginally cheaper) to remove directly via the entry API.

♻️ Suggested refactor
     pub fn del_rmac(&mut self, address: IpAddr, vni: Vni) -> Option<RmacEntry> {
         let key = (address, vni);
-
-        if let Entry::Occupied(o) = self.table.entry(key) {
-            if !o.get().is_stale() {
-                return None;
-            }
-            let (_key, deleted) = self.table.remove_entry(&key).unzip();
-            debug_assert!(deleted.is_some());
-            if deleted.is_some() {
-                self.stale = self.stale.saturating_sub(1);
-            }
-            return deleted;
-        }
+        if let Entry::Occupied(o) = self.table.entry(key) {
+            if !o.get().is_stale() {
+                return None;
+            }
+            self.stale = self.stale.saturating_sub(1);
+            return Some(o.remove());
+        }
         warn!("Could not delete rmac entry for vni:{vni} address:{address}: not found");
         None
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@routing/src/evpn/rmac.rs` around lines 98 - 114, Update del_rmac to remove
stale entries directly through the occupied Entry API instead of calling
self.table.remove_entry(&key) after the initial entry lookup. Preserve the
existing non-stale early return, stale counter decrement, debug assertion,
return value, and not-found warning behavior.

70-90: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

add_rmac_entry assumes the incoming entry is never itself stale.

The stale counter is only adjusted for the old entry being replaced (old.stale_t); it never checks the new entry.stale_t. This is safe today because RmacEntry::try_from(&Rmac) always sets stale_t: None (see rpc_adapt.rs), but the invariant is implicit rather than enforced. A debug_assert! would make this contract explicit and catch future misuse (e.g., if a caller ever constructs an RmacEntry with stale_t already set).

pub fn add_rmac_entry(&mut self, entry: RmacEntry) {
    debug_assert!(entry.stale_t.is_none(), "newly added rmac entries must not be pre-marked stale");
    ...
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@routing/src/evpn/rmac.rs` around lines 70 - 90, Update
RmacTable::add_rmac_entry to assert that the incoming RmacEntry has no
pre-existing stale_t value before modifying the table, using a debug assertion
with a clear contract message; preserve the existing replacement and
stale-counter logic.

173-209: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

No direct test coverage for flush_stale_rmacs.

rmac_store_basic exercises add_rmac_entry, invalidate_rmac_entry, and del_rmac, but flush_stale_rmacs (the actual periodic-cleanup entry point invoked from rio.rs) is untested. Since MAX_STALE_TIME is a hardcoded const, testing the timeout path is awkward without either sleeping or making the duration injectable for tests.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@routing/src/evpn/rmac.rs` around lines 173 - 209, The `flush_stale_rmacs`
cleanup entry point lacks direct coverage, especially for removing entries older
than `MAX_STALE_TIME`. Add focused tests that exercise no stale entries,
stale-but-not-expired entries, and expired stale entries with the expected VNI
results; make the timeout testable without sleeping by introducing an
appropriate injectable or test-configurable time source while preserving
production behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@routing/src/evpn/rmac.rs`:
- Around line 98-114: Update del_rmac to remove stale entries directly through
the occupied Entry API instead of calling self.table.remove_entry(&key) after
the initial entry lookup. Preserve the existing non-stale early return, stale
counter decrement, debug assertion, return value, and not-found warning
behavior.
- Around line 70-90: Update RmacTable::add_rmac_entry to assert that the
incoming RmacEntry has no pre-existing stale_t value before modifying the table,
using a debug assertion with a clear contract message; preserve the existing
replacement and stale-counter logic.
- Around line 173-209: The `flush_stale_rmacs` cleanup entry point lacks direct
coverage, especially for removing entries older than `MAX_STALE_TIME`. Add
focused tests that exercise no stale entries, stale-but-not-expired entries, and
expired stale entries with the expected VNI results; make the timeout testable
without sleeping by introducing an appropriate injectable or test-configurable
time source while preserving production behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 420fc172-a81a-46f9-9388-4943b5caa9a5

📥 Commits

Reviewing files that changed from the base of the PR and between 32ab06e and c6ab732.

📒 Files selected for processing (9)
  • net/src/eth/mac.rs
  • routing/src/cli/display.rs
  • routing/src/evpn/rmac.rs
  • routing/src/rib/nexthop.rs
  • routing/src/rib/rib2fib.rs
  • routing/src/rib/vrftable.rs
  • routing/src/router/cpi.rs
  • routing/src/router/rio.rs
  • routing/src/router/rpc_adapt.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • routing/src/router/rpc_adapt.rs

@qmonnet qmonnet left a comment

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.

Seems OK to me

@qmonnet
qmonnet added this pull request to the merge queue Jul 21, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jul 21, 2026
@qmonnet
qmonnet added this pull request to the merge queue Jul 21, 2026
Merged via the queue into main with commit 8d65310 Jul 21, 2026
46 of 53 checks passed
@qmonnet
qmonnet deleted the pr/fredi/router_mac_revisit branch July 21, 2026 18:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci:+release Enable VLAB release tests ci:+vlab Enable VLAB tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants