From cceea807a0d5f64ffbfd6c000efdf34db696278e Mon Sep 17 00:00:00 2001 From: Robin Jarry Date: Thu, 18 Dec 2025 09:47:26 +0100 Subject: [PATCH] ip,ip6: send GARP and NA on MAC address change 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 --- modules/infra/api/gr_infra.h | 1 + modules/infra/api/iface.c | 3 ++- modules/infra/cli/iface.c | 6 ++++- modules/infra/control/bond.c | 3 +-- modules/infra/control/iface.c | 15 +++++++++-- modules/infra/control/port.c | 2 +- modules/infra/control/port_test.c | 3 +++ modules/infra/control/vlan.c | 40 ++++++++++++++++++++--------- modules/infra/control/worker_test.c | 4 +++ modules/ip/control/address.c | 4 +-- modules/ip6/control/address.c | 4 ++- 11 files changed, 63 insertions(+), 22 deletions(-) diff --git a/modules/infra/api/gr_infra.h b/modules/infra/api/gr_infra.h index 045b5c787..5255711e2 100644 --- a/modules/infra/api/gr_infra.h +++ b/modules/infra/api/gr_infra.h @@ -212,6 +212,7 @@ typedef enum { GR_EVENT_IFACE_POST_RECONFIG = EVENT_TYPE(GR_INFRA_MODULE, 0x0005), GR_EVENT_IFACE_STATUS_UP = EVENT_TYPE(GR_INFRA_MODULE, 0x0006), GR_EVENT_IFACE_STATUS_DOWN = EVENT_TYPE(GR_INFRA_MODULE, 0x0007), + GR_EVENT_IFACE_MAC_CHANGE = EVENT_TYPE(GR_INFRA_MODULE, 0x0008), } gr_event_iface_t; // interface management /////////////////////////////////////////////////////// diff --git a/modules/infra/api/iface.c b/modules/infra/api/iface.c index 834069f6e..92dbb8957 100644 --- a/modules/infra/api/iface.c +++ b/modules/infra/api/iface.c @@ -163,7 +163,7 @@ static int iface_event_serialize(const void *obj, void **buf) { static struct gr_event_serializer iface_serializer = { .callback = iface_event_serialize, - .ev_count = 7, + .ev_count = 8, .ev_types = { GR_EVENT_IFACE_ADD, GR_EVENT_IFACE_POST_ADD, @@ -172,6 +172,7 @@ static struct gr_event_serializer iface_serializer = { GR_EVENT_IFACE_POST_RECONFIG, GR_EVENT_IFACE_STATUS_UP, GR_EVENT_IFACE_STATUS_DOWN, + GR_EVENT_IFACE_MAC_CHANGE, }, }; diff --git a/modules/infra/cli/iface.c b/modules/infra/cli/iface.c index c1cf79667..cbdd37415 100644 --- a/modules/infra/cli/iface.c +++ b/modules/infra/cli/iface.c @@ -561,6 +561,9 @@ static void iface_event_print(uint32_t event, const void *obj) { case GR_EVENT_IFACE_POST_RECONFIG: action = "reconf"; break; + case GR_EVENT_IFACE_MAC_CHANGE: + action = "mac change"; + break; default: action = "?"; break; @@ -572,7 +575,7 @@ static void iface_event_print(uint32_t event, const void *obj) { static struct cli_event_printer printer = { .print = iface_event_print, - .ev_count = 7, + .ev_count = 8, .ev_types = { GR_EVENT_IFACE_ADD, GR_EVENT_IFACE_POST_ADD, @@ -581,6 +584,7 @@ static struct cli_event_printer printer = { GR_EVENT_IFACE_STATUS_UP, GR_EVENT_IFACE_STATUS_DOWN, GR_EVENT_IFACE_POST_RECONFIG, + GR_EVENT_IFACE_MAC_CHANGE, }, }; diff --git a/modules/infra/control/bond.c b/modules/infra/control/bond.c index 5852e1966..ae1ec9a05 100644 --- a/modules/infra/control/bond.c +++ b/modules/infra/control/bond.c @@ -362,9 +362,8 @@ static int bond_reconfig( } else { mac = api->mac; } - if (bond_mac_set(iface, &mac) < 0) + if (iface_set_eth_addr(iface, &mac) < 0) return errno_set(errno); - bond->mac = mac; } if (set_attrs & (GR_BOND_SET_MEMBERS | GR_BOND_SET_PRIMARY)) diff --git a/modules/infra/control/iface.c b/modules/infra/control/iface.c index af08ba1ff..f80032ca7 100644 --- a/modules/infra/control/iface.c +++ b/modules/infra/control/iface.c @@ -295,6 +295,7 @@ void iface_del_subinterface(struct iface *parent, struct iface *sub) { int iface_set_eth_addr(struct iface *iface, const struct rte_ether_addr *mac) { const struct iface_type *type; + int ret; if (iface == NULL) return errno_set(EINVAL); @@ -304,7 +305,11 @@ int iface_set_eth_addr(struct iface *iface, const struct rte_ether_addr *mac) { if (type->set_eth_addr == NULL) return errno_set(EOPNOTSUPP); - return type->set_eth_addr(iface, mac); + ret = type->set_eth_addr(iface, mac); + if (ret == 0) + gr_event_push(GR_EVENT_IFACE_MAC_CHANGE, iface); + + return ret; } int iface_add_eth_addr(struct iface *iface, const struct rte_ether_addr *mac) { @@ -512,6 +517,11 @@ static void iface_event(uint32_t event, const void *obj) { gr_event_push(event, s); } break; + case GR_EVENT_IFACE_MAC_CHANGE: + str = "MAC_CHANGE"; + gr_vec_foreach (struct iface *s, iface->subinterfaces) + gr_event_push(event, s); + break; default: str = "?"; break; @@ -521,7 +531,7 @@ static void iface_event(uint32_t event, const void *obj) { 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, @@ -530,6 +540,7 @@ static struct gr_event_subscription iface_event_handler = { GR_EVENT_IFACE_POST_RECONFIG, GR_EVENT_IFACE_STATUS_UP, GR_EVENT_IFACE_STATUS_DOWN, + GR_EVENT_IFACE_MAC_CHANGE, }, }; diff --git a/modules/infra/control/port.c b/modules/infra/control/port.c index 5823c5505..b29ca6fef 100644 --- a/modules/infra/control/port.c +++ b/modules/infra/control/port.c @@ -330,7 +330,7 @@ static int iface_port_reconfig( return ret; } - if (set_attrs & GR_PORT_SET_MAC && (ret = port_mac_set(iface, &api->mac)) < 0) + if (set_attrs & GR_PORT_SET_MAC && (ret = iface_set_eth_addr(iface, &api->mac)) < 0) return ret; if (!p->started && (ret = rte_eth_dev_start(p->port_id)) < 0) diff --git a/modules/infra/control/port_test.c b/modules/infra/control/port_test.c index 01a1afe2a..a24f9761c 100644 --- a/modules/infra/control/port_test.c +++ b/modules/infra/control/port_test.c @@ -28,6 +28,9 @@ struct rte_rcu_qsbr *gr_datapath_rcu(void) { static struct rte_rcu_qsbr rcu; return &rcu; } +int iface_set_eth_addr(struct iface *, const struct rte_ether_addr *) { + return 0; +} mock_func(struct iface *, iface_from_id(uint16_t)); mock_func(struct iface *, iface_next(gr_iface_type_t, const struct iface *)); mock_func(int, port_unplug(struct iface_info_port *)); diff --git a/modules/infra/control/vlan.c b/modules/infra/control/vlan.c index 0b9c9fe39..304cde93e 100644 --- a/modules/infra/control/vlan.c +++ b/modules/infra/control/vlan.c @@ -47,9 +47,14 @@ static int iface_vlan_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); + 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 { + // remove previous mac filter (ignore errors) + iface_del_eth_addr(cur_parent, &cur->mac); + } } } else { cur_parent = NULL; @@ -90,15 +95,8 @@ static int iface_vlan_reconfig( } if (set_attrs & GR_VLAN_SET_MAC) { - struct iface *parent = iface_from_id(cur->parent_id); - if (rte_is_zero_ether_addr(&next->mac)) { - if ((ret = iface_get_eth_addr(parent, &cur->mac)) < 0) - return ret; - } else { - if ((ret = iface_add_eth_addr(parent, &next->mac)) < 0) - return ret; - cur->mac = next->mac; - } + if ((ret = iface_set_eth_addr(iface, &next->mac)) < 0) + return ret; } return 0; @@ -157,6 +155,23 @@ static int iface_vlan_get_eth_addr(const struct iface *iface, struct rte_ether_a return 0; } +static int iface_vlan_set_eth_addr(struct iface *iface, const struct rte_ether_addr *mac) { + struct iface_info_vlan *vlan = iface_info_vlan(iface); + struct iface *parent = iface_from_id(vlan->parent_id); + int ret; + + if (rte_is_zero_ether_addr(mac)) { + if ((ret = iface_get_eth_addr(parent, &vlan->mac)) < 0) + return ret; + } else { + if ((ret = iface_add_eth_addr(parent, mac)) < 0) + return ret; + vlan->mac = *mac; + } + + return 0; +} + static int iface_vlan_add_eth_addr(struct iface *iface, const struct rte_ether_addr *mac) { const struct iface_info_vlan *vlan = iface_info_vlan(iface); struct iface *parent = iface_from_id(vlan->parent_id); @@ -194,6 +209,7 @@ static struct iface_type iface_type_vlan = { .fini = iface_vlan_fini, .set_up_down = iface_vlan_up_down, .get_eth_addr = iface_vlan_get_eth_addr, + .set_eth_addr = iface_vlan_set_eth_addr, .add_eth_addr = iface_vlan_add_eth_addr, .del_eth_addr = iface_vlan_del_eth_addr, .to_api = vlan_to_api, diff --git a/modules/infra/control/worker_test.c b/modules/infra/control/worker_test.c index b28f07e68..18e6a8509 100644 --- a/modules/infra/control/worker_test.c +++ b/modules/infra/control/worker_test.c @@ -57,6 +57,10 @@ struct iface *iface_next(gr_iface_type_t /*type_id*/, const struct iface *prev) return NULL; } +int iface_set_eth_addr(struct iface *, const struct rte_ether_addr *) { + return 0; +} + mock_func(int, worker_graph_reload(struct worker *, gr_vec struct iface_info_port **)); mock_func(int, worker_graph_reload_all(gr_vec struct iface_info_port **)); mock_func(void, worker_graph_free(struct worker *)); diff --git a/modules/ip/control/address.c b/modules/ip/control/address.c index 3298e6cc3..cc49e5c66 100644 --- a/modules/ip/control/address.c +++ b/modules/ip/control/address.c @@ -270,8 +270,8 @@ static struct gr_event_subscription iface_pre_rm_subscription = { }; static struct gr_event_subscription iface_up_subscription = { .callback = iface_up_cb, - .ev_count = 1, - .ev_types = {GR_EVENT_IFACE_STATUS_UP}, + .ev_count = 2, + .ev_types = {GR_EVENT_IFACE_STATUS_UP, GR_EVENT_IFACE_MAC_CHANGE}, }; static struct gr_event_serializer iface_addr_serializer = { .size = sizeof(struct gr_ip4_ifaddr), diff --git a/modules/ip6/control/address.c b/modules/ip6/control/address.c index 217113aed..e1df94860 100644 --- a/modules/ip6/control/address.c +++ b/modules/ip6/control/address.c @@ -419,6 +419,7 @@ static void ip6_iface_event_handler(uint32_t event, const void *obj) { gr_vec_free(addrs->nh); break; case GR_EVENT_IFACE_STATUS_UP: + case GR_EVENT_IFACE_MAC_CHANGE: addrs = &iface_addrs[iface->id]; gr_vec_foreach (nh, addrs->nh) { if (nh6_advertise(nh, NULL) < 0) @@ -471,11 +472,12 @@ static struct gr_module addr6_module = { static struct gr_event_subscription iface_event_subscription = { .callback = ip6_iface_event_handler, - .ev_count = 3, + .ev_count = 4, .ev_types = { GR_EVENT_IFACE_POST_ADD, GR_EVENT_IFACE_PRE_REMOVE, GR_EVENT_IFACE_STATUS_UP, + GR_EVENT_IFACE_MAC_CHANGE, }, };