Skip to content

infra: send GARP and NA on MAC address change - #473

Merged
rjarry merged 1 commit into
DPDK:mainfrom
rjarry:mac-change-garp
Feb 18, 2026
Merged

infra: send GARP and NA on MAC address change#473
rjarry merged 1 commit into
DPDK:mainfrom
rjarry:mac-change-garp

Conversation

@rjarry

@rjarry rjarry commented Jan 9, 2026

Copy link
Copy Markdown
Collaborator

Add a GR_EVENT_IFACE_MAC_CHANGE event that is pushed when an interface MAC address is successfully changed via iface_set_eth_addr().

Refactor bond and port reconfig handlers to use iface_set_eth_addr() instead of calling the type-specific callback directly. Implement set_eth_addr for VLAN interfaces.

Subscribe IPv4 and IPv6 address handlers to the new event so that gratuitous ARP and neighbor advertisements are sent when the MAC changes. This allows neighbors to update their caches without waiting for stale entries to expire.

Summary by CodeRabbit

  • New Features
    • Interface MAC address change detection with system-wide event notifications.
    • MAC changes are logged in the CLI and propagated to subinterfaces and related interface types (e.g., VLAN/port).
    • IPv6 nexthop advertisement and routing react automatically to interface MAC updates.

@coderabbitai

coderabbitai Bot commented Jan 9, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a new interface event GR_EVENT_IFACE_MAC_CHANGE and wires it system-wide. Introduces and uses iface_set_eth_addr as the generic MAC setter (called from port reconfig and test/worker stubs), adds a VLAN-specific iface_vlan_set_eth_addr, and updates VLAN reconfiguration to use the setter. Subscriptions and serializers in IP/IPv6 and infra API/CLI now include the new event. Interface event handling propagates MAC changes to subinterfaces. FRR zebra dataplane integration subscribes to and processes the new event.

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


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 and usage tips.

@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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
modules/infra/control/iface.c (1)

296-313: Potential functional gap: MAC change event won’t reach subinterfaces (VLAN, etc.)

iface_set_eth_addr() now emits GR_EVENT_IFACE_MAC_CHANGE on success, but the infra iface_event handler doesn’t subscribe to (or forward) this event to iface->subinterfaces. If a parent’s MAC change affects neighbors’ reachability for IPs hosted on subinterfaces, they may not send GARP/NA, defeating the PR’s goal for those IPs.

Proposed fix (subscribe + forward MAC_CHANGE to subinterfaces)
 static void iface_event(uint32_t event, const void *obj) {
 	const struct iface *iface = obj;
 	char *str = "";
 	switch (event) {
+	case GR_EVENT_IFACE_MAC_CHANGE:
+		str = "MAC_CHANGE";
+		gr_vec_foreach (struct iface *s, iface->subinterfaces)
+			gr_event_push(event, s);
+		break;
 	case GR_EVENT_IFACE_ADD:
 		str = "ADD";
 		break;
 	...
 	}
 	LOG(DEBUG, "iface event [0x%08x] %s triggered for iface %s.", event, str, iface->name);
 }

 static struct gr_event_subscription iface_event_handler = {
 	.callback = iface_event,
-	.ev_count = 7,
+	.ev_count = 8,
 	.ev_types = {
 		GR_EVENT_IFACE_ADD,
 		GR_EVENT_IFACE_POST_ADD,
 		GR_EVENT_IFACE_PRE_REMOVE,
 		GR_EVENT_IFACE_REMOVE,
 		GR_EVENT_IFACE_POST_RECONFIG,
 		GR_EVENT_IFACE_STATUS_UP,
 		GR_EVENT_IFACE_STATUS_DOWN,
+		GR_EVENT_IFACE_MAC_CHANGE,
 	},
 };
modules/infra/control/vlan.c (1)

34-98: Critical: VLAN MAC reconfig can end up deleting a bond parent’s primary MAC from member ports

During VLAN reconfig, when GR_VLAN_SET_MAC is set you do iface_del_eth_addr(cur_parent, &cur->mac) (Line 49-53). With the new iface_vlan_set_eth_addr() “inherit from parent” path, cur->mac can be the parent’s primary MAC. If the parent is a bond, bond_mac_del() will remove that MAC from all member ports, potentially breaking the bond.

Minimal mitigation: don’t delete the parent’s primary MAC
 	if (reconfig) {
 		if ((cur_parent = iface_from_id(cur->parent_id)) == NULL)
 			return -errno;
 		if (set_attrs & GR_VLAN_SET_MAC) {
 			// reconfig, *not initial config*
-			// remove previous mac filter (ignore errors)
-			iface_del_eth_addr(cur_parent, &cur->mac);
+			// remove previous mac filter (ignore errors), but never delete parent's primary MAC
+			struct rte_ether_addr parent_mac;
+			if (iface_get_eth_addr(cur_parent, &parent_mac) == 0
+			    && rte_is_same_ether_addr(&parent_mac, &cur->mac)) {
+				/* inherited/primary MAC: nothing to delete */
+			} else {
+				iface_del_eth_addr(cur_parent, &cur->mac);
+			}
 		}
 	} else {
 		cur_parent = NULL;
 	}

This still leaves an edge case if the VLAN was previously “inherited” but cached an old parent MAC (e.g., parent MAC changed); the robust fix is to track whether the VLAN MAC was explicitly added to the parent (e.g., a boolean in the VLAN private info) and only delete in that case.

Also applies to: 153-168

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3c10bc6 and 9424ace.

📒 Files selected for processing (7)
  • modules/infra/api/gr_infra.h
  • modules/infra/control/bond.c
  • modules/infra/control/iface.c
  • modules/infra/control/port.c
  • modules/infra/control/vlan.c
  • modules/ip/control/address.c
  • modules/ip6/control/address.c
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{c,h}

⚙️ CodeRabbit configuration file

**/*.{c,h}: - gr_vec_*() functions cannot fail. No need to check their return value.

  • gr_vec_free(x) always sets x = NULL. There is no risk of double free.
  • ec_node_*() functions consume their ec_node arguments. No leaks on error.
  • rte_node->ctx is an uint8_t array of size 16, not a pointer.
  • Never suggest to replace assert() with graceful error checking.
  • We compile with -std=gnu2x. Unnamed parameters in function definitions are valid.

Files:

  • modules/infra/api/gr_infra.h
  • modules/infra/control/iface.c
  • modules/infra/control/bond.c
  • modules/infra/control/vlan.c
  • modules/ip6/control/address.c
  • modules/ip/control/address.c
  • modules/infra/control/port.c
🧠 Learnings (3)
📓 Common learnings
Learnt from: christophefontaine
Repo: DPDK/grout PR: 466
File: modules/srv6/datapath/l2_encap.c:26-32
Timestamp: 2025-12-17T17:32:21.746Z
Learning: The GR_MBUF_PRIV_DATA_TYPE macro in modules/infra/datapath/gr_mbuf.h automatically adds `const struct iface *iface` as the first field to any structure defined with it. All types defined using this macro (e.g., srv6_dx2_mbuf_data, mbuf_data, queue_mbuf_data) will have the iface field available, followed by any custom fields provided as the second macro argument.
📚 Learning: 2025-12-17T17:32:21.746Z
Learnt from: christophefontaine
Repo: DPDK/grout PR: 466
File: modules/srv6/datapath/l2_encap.c:26-32
Timestamp: 2025-12-17T17:32:21.746Z
Learning: The GR_MBUF_PRIV_DATA_TYPE macro in modules/infra/datapath/gr_mbuf.h automatically adds `const struct iface *iface` as the first field to any structure defined with it. All types defined using this macro (e.g., srv6_dx2_mbuf_data, mbuf_data, queue_mbuf_data) will have the iface field available, followed by any custom fields provided as the second macro argument.

Applied to files:

  • modules/infra/api/gr_infra.h
  • modules/infra/control/iface.c
  • modules/ip6/control/address.c
  • modules/ip/control/address.c
📚 Learning: 2025-10-21T15:42:43.874Z
Learnt from: rjarry
Repo: DPDK/grout PR: 350
File: modules/ip/control/address.c:214-216
Timestamp: 2025-10-21T15:42:43.874Z
Learning: In C code compiled with `-std=gnu2x`, the gr_vec_foreach macro supports inline variable declarations (e.g., `gr_vec_foreach (struct nexthop *nh, vector)`). This is valid C2x syntax and does not require pre-declaring the loop variable.

Applied to files:

  • modules/ip6/control/address.c
🧬 Code graph analysis (3)
modules/infra/control/iface.c (3)
modules/infra/control/gr_iface.h (1)
  • iface (16-25)
modules/infra/control/port_test.c (1)
  • gr_event_push (24-24)
modules/infra/control/worker_test.c (1)
  • gr_event_push (37-37)
modules/infra/control/vlan.c (1)
modules/infra/control/iface.c (2)
  • iface_set_eth_addr (296-313)
  • iface_from_id (256-263)
modules/infra/control/port.c (2)
modules/infra/control/iface.c (1)
  • iface_set_eth_addr (296-313)
modules/infra/control/gr_iface.h (1)
  • iface (16-25)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: build-and-tests (clang-15, none, debugoptimized, ubuntu-22.04, false)
  • GitHub Check: build-and-tests (clang-18, none, debugoptimized, ubuntu-24.04, false)
  • GitHub Check: build-and-tests (gcc-13, none, debugoptimized, ubuntu-24.04, -Dfrr=enabled, false)
  • GitHub Check: build-and-tests (clang-16, none, debugoptimized, ubuntu-24.04, false)
  • GitHub Check: build-and-tests (gcc-14, address, debug, ubuntu-24.04, -Dfrr=enabled, true)
  • GitHub Check: rpm
  • GitHub Check: deb
🔇 Additional comments (5)
modules/ip6/control/address.c (1)

421-428: No critical issues spotted in the added GR_EVENT_IFACE_MAC_CHANGE handling/subscription.

Also applies to: 473-481

modules/infra/control/port.c (1)

333-334: No critical issues spotted in the switch to iface_set_eth_addr() for port MAC reconfig.

modules/infra/control/bond.c (1)

356-367: No critical issues spotted in the switch to iface_set_eth_addr() for bond MAC reconfig.

modules/ip/control/address.c (1)

271-275: No critical issues spotted in the updated IPv4 iface event subscription/serializer counts.

Also applies to: 276-280

modules/infra/api/gr_infra.h (1)

205-216: No critical issues spotted in adding GR_EVENT_IFACE_MAC_CHANGE.

@rjarry
rjarry force-pushed the mac-change-garp branch 2 times, most recently from cc677f9 to cceea80 Compare January 9, 2026 12:51

@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

🤖 Fix all issues with AI agents
In @modules/infra/control/vlan.c:
- Around line 97-100: The VLAN MAC handling currently copies the parent MAC and
loses the “inherit” semantics, and lacks NULL checks; add a boolean inherit_mac
(or similar) to struct iface_info_vlan, set it instead of copying when the
requested MAC is 00:00:00:00:00:00 in iface_vlan_set_eth_addr(), validate that
parent and mac pointers are non-NULL before using them, update
iface_vlan_get_eth_addr() to return the parent’s current MAC dynamically when
inherit_mac==true (only return stored vlan->mac when inherit_mac==false), and
ensure iface_vlan_fini() and the reconfig delete path only call
iface_del_eth_addr(parent, &vlan->mac) when inherit_mac==false so you never try
to delete an inherited/parent primary address.
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cc677f9 and cceea80.

📒 Files selected for processing (11)
  • modules/infra/api/gr_infra.h
  • modules/infra/api/iface.c
  • modules/infra/cli/iface.c
  • modules/infra/control/bond.c
  • modules/infra/control/iface.c
  • modules/infra/control/port.c
  • modules/infra/control/port_test.c
  • modules/infra/control/vlan.c
  • modules/infra/control/worker_test.c
  • modules/ip/control/address.c
  • modules/ip6/control/address.c
🚧 Files skipped from review as they are similar to previous changes (4)
  • modules/infra/control/port_test.c
  • modules/infra/api/gr_infra.h
  • modules/infra/control/worker_test.c
  • modules/infra/control/port.c
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{c,h}

⚙️ CodeRabbit configuration file

**/*.{c,h}: - gr_vec_*() functions cannot fail. No need to check their return value.

  • gr_vec_free(x) always sets x = NULL. There is no risk of double free.
  • ec_node_*() functions consume their ec_node arguments. No leaks on error.
  • rte_node->ctx is an uint8_t array of size 16, not a pointer.
  • Never suggest to replace assert() with graceful error checking.
  • We compile with -std=gnu2x. Unnamed parameters in function definitions are valid.

Files:

  • modules/ip6/control/address.c
  • modules/infra/control/iface.c
  • modules/infra/cli/iface.c
  • modules/infra/api/iface.c
  • modules/ip/control/address.c
  • modules/infra/control/vlan.c
  • modules/infra/control/bond.c
🧠 Learnings (3)
📓 Common learnings
Learnt from: christophefontaine
Repo: DPDK/grout PR: 466
File: modules/srv6/datapath/l2_encap.c:26-32
Timestamp: 2025-12-17T17:32:21.746Z
Learning: The GR_MBUF_PRIV_DATA_TYPE macro in modules/infra/datapath/gr_mbuf.h automatically adds `const struct iface *iface` as the first field to any structure defined with it. All types defined using this macro (e.g., srv6_dx2_mbuf_data, mbuf_data, queue_mbuf_data) will have the iface field available, followed by any custom fields provided as the second macro argument.
Learnt from: maxime-leroy
Repo: DPDK/grout PR: 372
File: smoke/cross_vrf_forward_test.sh:18-18
Timestamp: 2025-11-05T13:55:26.189Z
Learning: In the DPDK/grout codebase, VRF interfaces (named gr-vrf<id>) are automatically created when an interface is added to a non-existing VRF using port_add. The VRF creation is handled automatically by the event system in vrf_netlink.c, so no explicit VRF interface creation commands are needed in test scripts.
📚 Learning: 2025-12-17T17:32:21.746Z
Learnt from: christophefontaine
Repo: DPDK/grout PR: 466
File: modules/srv6/datapath/l2_encap.c:26-32
Timestamp: 2025-12-17T17:32:21.746Z
Learning: The GR_MBUF_PRIV_DATA_TYPE macro in modules/infra/datapath/gr_mbuf.h automatically adds `const struct iface *iface` as the first field to any structure defined with it. All types defined using this macro (e.g., srv6_dx2_mbuf_data, mbuf_data, queue_mbuf_data) will have the iface field available, followed by any custom fields provided as the second macro argument.

Applied to files:

  • modules/ip6/control/address.c
  • modules/infra/control/iface.c
  • modules/infra/cli/iface.c
  • modules/infra/api/iface.c
  • modules/ip/control/address.c
📚 Learning: 2025-10-21T15:42:43.874Z
Learnt from: rjarry
Repo: DPDK/grout PR: 350
File: modules/ip/control/address.c:214-216
Timestamp: 2025-10-21T15:42:43.874Z
Learning: In C code compiled with `-std=gnu2x`, the gr_vec_foreach macro supports inline variable declarations (e.g., `gr_vec_foreach (struct nexthop *nh, vector)`). This is valid C2x syntax and does not require pre-declaring the loop variable.

Applied to files:

  • modules/ip6/control/address.c
🧬 Code graph analysis (3)
modules/infra/control/iface.c (1)
modules/infra/control/gr_iface.h (1)
  • iface (16-25)
modules/infra/control/vlan.c (3)
modules/infra/control/iface.c (5)
  • iface_get_eth_addr (265-277)
  • iface_del_eth_addr (329-341)
  • iface_set_eth_addr (296-313)
  • iface_from_id (256-263)
  • iface_add_eth_addr (315-327)
modules/infra/control/port_test.c (1)
  • iface_set_eth_addr (31-33)
modules/infra/control/worker_test.c (2)
  • iface_set_eth_addr (60-62)
  • iface_from_id (45-47)
modules/infra/control/bond.c (3)
modules/infra/control/iface.c (1)
  • iface_set_eth_addr (296-313)
modules/infra/control/port_test.c (1)
  • iface_set_eth_addr (31-33)
modules/infra/control/gr_iface.h (1)
  • iface (16-25)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: build-and-tests (clang-16, none, debugoptimized, ubuntu-24.04, false)
  • GitHub Check: build-and-tests (gcc-13, none, debugoptimized, ubuntu-24.04, -Dfrr=enabled, false)
  • GitHub Check: build-and-tests (clang-18, none, debugoptimized, ubuntu-24.04, false)
  • GitHub Check: build-and-tests (clang-15, none, debugoptimized, ubuntu-22.04, false)
  • GitHub Check: build-and-tests (gcc-14, address, debug, ubuntu-24.04, -Dfrr=enabled, true)
  • GitHub Check: rpm
  • GitHub Check: deb

Comment thread modules/infra/control/iface.c
Comment thread modules/infra/control/vlan.c

@aharivel aharivel left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM - couple of very minor questions / changes...

Comment thread modules/infra/control/vlan.c
Comment thread modules/infra/control/bond.c Outdated

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
modules/ip6/control/address.c (1)

539-549: ⚠️ Potential issue | 🔴 Critical

ev_count not updated — GR_EVENT_IFACE_MAC_CHANGE subscription is silently dropped.

ev_count = 4 was not incremented when GR_EVENT_IFACE_MAC_CHANGE was added as the 5th entry in ev_types. The subscription infrastructure iterates only entries [0..ev_count-1], so this event is never registered and ip6_iface_event_handler will never fire for it. IPv6 NA won't be sent on MAC change.

Fix
 static struct gr_event_subscription iface_event_subscription = {
 	.callback = ip6_iface_event_handler,
-	.ev_count = 4,
+	.ev_count = 5,
 	.ev_types = {
 		GR_EVENT_IFACE_POST_ADD,
 		GR_EVENT_IFACE_POST_RECONFIG,
 		GR_EVENT_IFACE_PRE_REMOVE,
 		GR_EVENT_IFACE_STATUS_UP,
 		GR_EVENT_IFACE_MAC_CHANGE,
 	},
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@modules/ip6/control/address.c` around lines 539 - 549, The subscription
struct iface_event_subscription lists five event types but its ev_count is still
4, so GR_EVENT_IFACE_MAC_CHANGE is never registered; update ev_count to match
the number of entries (e.g., change ev_count = 4 to ev_count = 5) or replace the
hardcoded value with a size expression (compute from the ev_types array) so
ip6_iface_event_handler will be subscribed for GR_EVENT_IFACE_MAC_CHANGE.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@modules/ip6/control/address.c`:
- Around line 539-549: The subscription struct iface_event_subscription lists
five event types but its ev_count is still 4, so GR_EVENT_IFACE_MAC_CHANGE is
never registered; update ev_count to match the number of entries (e.g., change
ev_count = 4 to ev_count = 5) or replace the hardcoded value with a size
expression (compute from the ev_types array) so ip6_iface_event_handler will be
subscribed for GR_EVENT_IFACE_MAC_CHANGE.

---

Duplicate comments:
In `@modules/infra/control/vlan.c`:
- Around line 161-176: Validate inputs and stop caching a stale parent MAC: in
iface_vlan_set_eth_addr check that mac is not NULL before calling
rte_is_zero_ether_addr and check that parent (from iface_from_id) is not NULL
before calling iface_get_eth_addr/iface_add_eth_addr; when mac is zero (inherit
case) do not copy the parent's address into vlan->mac (which becomes stale if
parent changes) — instead either leave vlan->mac zeroed or set an explicit
inherit flag on the iface_info_vlan struct so callers use the parent's MAC
dynamically (reference iface_vlan_set_eth_addr, iface_info_vlan, iface_from_id,
iface_get_eth_addr, iface_add_eth_addr, and rte_is_zero_ether_addr when making
changes).

@rjarry
rjarry requested a review from aharivel February 18, 2026 09:46

@aharivel aharivel left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM

When an interface MAC address changes, neighbors must be notified so
they can update their caches without waiting for stale entries to
expire.

Introduce GR_EVENT_IFACE_MAC_CHANGE which is pushed from
iface_set_eth_addr() on success. Have bond and port reconfig handlers
call the generic iface_set_eth_addr() wrapper instead of their
type-specific callbacks. Implement set_eth_addr for VLAN interfaces.

Subscribe IPv4 and IPv6 address handlers to this event to trigger
gratuitous ARP and unsolicited neighbor advertisements respectively.

Signed-off-by: Robin Jarry <rjarry@redhat.com>
Reviewed-by: Anthony Harivel <aharivel@redhat.com>

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
modules/infra/control/vlan.c (1)

107-121: ⚠️ Potential issue | 🟠 Major

iface_vlan_fini returns a spurious error when destroying a VLAN with an inherited MAC.

iface_vlan_reconfig (lines 50–56) correctly skips iface_del_eth_addr when the current VLAN MAC equals the parent's primary MAC. iface_vlan_fini has no such guard: it unconditionally calls iface_del_eth_addr(parent, &vlan->mac), which will fail when vlan->mac is the parent's primary/default MAC (set via the inherit path in iface_vlan_set_eth_addr). That failure propagates through iface_destroy back to the API caller as a hard error, even though the hash entry was already removed and the subinterface detached — i.e., the VLAN was cleaned up but the response says it failed.

Apply the same guard used in the reconfig path:

🐛 Proposed fix
 static int iface_vlan_fini(struct iface *iface) {
 	struct iface_info_vlan *vlan = iface_info_vlan(iface);
 	struct iface *parent = iface_from_id(vlan->parent_id);
 	int ret, status = 0;

 	rte_hash_del_key(vlan_hash, &(struct vlan_key) {vlan->parent_id, vlan->vlan_id});

-	if ((ret = iface_del_eth_addr(parent, &vlan->mac)) < 0)
-		status = status ?: ret;
+	struct rte_ether_addr parent_mac;
+	if (parent == NULL
+	    || iface_get_eth_addr(parent, &parent_mac) < 0
+	    || !rte_is_same_ether_addr(&parent_mac, &vlan->mac)) {
+		if ((ret = iface_del_eth_addr(parent, &vlan->mac)) < 0)
+			status = status ?: ret;
+	}

 	if ((ret = iface_del_subinterface(parent, iface)) < 0)
 		status = status ?: ret;

 	return status;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@modules/infra/control/vlan.c` around lines 107 - 121, The cleanup function
iface_vlan_fini currently always calls iface_del_eth_addr(parent, &vlan->mac)
which yields a spurious error when the VLAN inherited the parent's primary MAC;
update iface_vlan_fini to use the same guard as iface_vlan_reconfig and skip
calling iface_del_eth_addr if vlan->mac equals the parent's primary/default MAC
(i.e., compare vlan->mac with the parent's primary MAC value used by
iface_vlan_set_eth_addr), then proceed to delete the hash entry
(rte_hash_del_key) and call iface_del_subinterface as before so a successful
cleanup isn't reported as an error.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@modules/infra/control/vlan.c`:
- Around line 107-121: The cleanup function iface_vlan_fini currently always
calls iface_del_eth_addr(parent, &vlan->mac) which yields a spurious error when
the VLAN inherited the parent's primary MAC; update iface_vlan_fini to use the
same guard as iface_vlan_reconfig and skip calling iface_del_eth_addr if
vlan->mac equals the parent's primary/default MAC (i.e., compare vlan->mac with
the parent's primary MAC value used by iface_vlan_set_eth_addr), then proceed to
delete the hash entry (rte_hash_del_key) and call iface_del_subinterface as
before so a successful cleanup isn't reported as an error.

---

Duplicate comments:
In `@modules/infra/control/vlan.c`:
- Around line 161-176: iface_vlan_set_eth_addr currently snapshots the parent's
MAC into vlan->mac when given a zero (inherit) address, so later parent MAC
changes aren't reflected; fix this by recording that the VLAN is in "inherit"
mode (add a flag like mac_inherit to struct iface_info_vlan and set it in
iface_vlan_set_eth_addr when mac is zero) and then, in the iface event path that
handles GR_EVENT_IFACE_MAC_CHANGE (where iface_event updates subinterfaces),
detect mac_inherit and refresh vlan->mac from the parent (call
iface_get_eth_addr(parent, &vlan->mac) or equivalent) so iface_vlan_get_eth_addr
returns the up-to-date value; ensure iface_vlan_get_eth_addr respects the
mac_inherit flag (or always consult parent when mac_inherit is set).

@rjarry
rjarry merged commit 5870ab0 into DPDK:main Feb 18, 2026
7 checks passed
@rjarry
rjarry deleted the mac-change-garp branch February 18, 2026 10:27
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.

2 participants