From c774169bb6f9addf6a846c08099c7de972ca2d57 Mon Sep 17 00:00:00 2001 From: Maxime Leroy Date: Thu, 14 Aug 2025 12:07:19 +0200 Subject: [PATCH 1/6] zebra: add dplane helpers to provide interface speed Add dplane ctx helpers to carry interface speed. Signed-off-by: Maxime Leroy --- zebra/interface.c | 12 +++++++++--- zebra/zebra_dplane.c | 31 +++++++++++++++++++++++++++++++ zebra/zebra_dplane.h | 4 ++++ 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/zebra/interface.c b/zebra/interface.c index 6ff1791b76ec..40d19cdd9fab 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -2036,8 +2036,8 @@ static void zebra_if_dplane_ifp_handling(struct zebra_dplane_ctx *ctx) uint32_t mtu; ns_id_t link_nsid; struct zebra_if *zif; - bool protodown, protodown_set, startup; - uint32_t rc_bitfield; + bool protodown, protodown_set, startup, speed_set; + uint32_t rc_bitfield, speed; uint8_t old_hw_addr[INTERFACE_HWADDR_MAX]; char *desc; uint8_t family; @@ -2069,6 +2069,8 @@ static void zebra_if_dplane_ifp_handling(struct zebra_dplane_ctx *ctx) family = dplane_ctx_get_ifp_family(ctx); change_flags = dplane_ctx_get_ifp_change_flags(ctx); cchanges = dplane_ctx_get_intf_carrier_changes(ctx); + speed_set = dplane_ctx_get_ifp_speed_set(ctx); + speed = dplane_ctx_get_ifp_speed(ctx); #ifndef AF_BRIDGE /* @@ -2107,7 +2109,9 @@ static void zebra_if_dplane_ifp_handling(struct zebra_dplane_ctx *ctx) if_update_state_mtu(ifp, mtu); if_update_state_mtu6(ifp, mtu); if_update_state_metric(ifp, 0); - if_update_state_speed(ifp, kernel_get_speed(ifp, NULL)); + if (!speed_set) + speed = kernel_get_speed(ifp, NULL); + if_update_state_speed(ifp, speed); ifp->ptm_status = ZEBRA_PTM_STATUS_UNKNOWN; ifp->txqlen = dplane_ctx_get_intf_txqlen(ctx); @@ -2211,6 +2215,8 @@ static void zebra_if_dplane_ifp_handling(struct zebra_dplane_ctx *ctx) if_update_state_mtu(ifp, mtu); if_update_state_mtu6(ifp, mtu); if_update_state_metric(ifp, 0); + if (speed_set) + if_update_state_speed(ifp, speed); ifp->txqlen = dplane_ctx_get_intf_txqlen(ctx); /* diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 90e2b37fdc06..3204d6d5a6f6 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -240,6 +240,9 @@ struct dplane_intf_info { uint32_t flags; uint32_t change_flags; + bool speed_set; + uint32_t speed; + bool protodown; bool protodown_set; bool pd_reason_val; @@ -1882,6 +1885,34 @@ void dplane_ctx_set_ifp_zif_type(struct zebra_dplane_ctx *ctx, ctx->u.intf.zif_type = zif_type; } +void dplane_ctx_set_ifp_speed_set(struct zebra_dplane_ctx *ctx, bool set) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.speed_set = set; +} + +bool dplane_ctx_get_ifp_speed_set(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.speed_set; +} + +void dplane_ctx_set_ifp_speed(struct zebra_dplane_ctx *ctx, uint32_t speed) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.speed = speed; +} + +uint32_t dplane_ctx_get_ifp_speed(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.speed; +} + void dplane_ctx_set_ifname(struct zebra_dplane_ctx *ctx, const char *ifname) { DPLANE_CTX_VALID(ctx); diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 15417b8e29bd..c4abdc2d1084 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -459,6 +459,10 @@ uint8_t dplane_ctx_get_ifp_family(const struct zebra_dplane_ctx *ctx); struct zebra_vxlan_vni_array; void dplane_ctx_set_ifp_vxlan_vni_array(struct zebra_dplane_ctx *ctx, struct zebra_vxlan_vni_array *vniarray); +void dplane_ctx_set_ifp_speed_set(struct zebra_dplane_ctx *ctx, bool set); +bool dplane_ctx_get_ifp_speed_set(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_ifp_speed(struct zebra_dplane_ctx *ctx, uint32_t speed); +uint32_t dplane_ctx_get_ifp_speed(const struct zebra_dplane_ctx *ctx); /* * These defines mirror the values for bridge values in linux From 53f268d8a7bbdbe1394aca0b5f61deb9fb07bfca Mon Sep 17 00:00:00 2001 From: Maxime Leroy Date: Mon, 25 May 2026 11:22:10 +0200 Subject: [PATCH 2/6] zebra: thread user-data through netlink parse callbacks Add a void *arg to netlink_parse_info(), netlink_talk(), netlink_talk_info() and ge_netlink_talk() and to the filter signature (new typedef netlink_parse_filter_t). Existing filters ignore arg and callsites pass NULL; no runtime change. Needed for the next commit. Signed-off-by: Maxime Leroy --- zebra/ge_netlink.c | 12 +++---- zebra/ge_netlink.h | 2 +- zebra/if_netlink.c | 32 +++++++++---------- zebra/if_netlink.h | 7 ++--- zebra/kernel_netlink.c | 69 ++++++++++++++++++----------------------- zebra/kernel_netlink.h | 18 ++++++----- zebra/netconf_netlink.c | 2 +- zebra/netconf_netlink.h | 3 +- zebra/rt_netlink.c | 60 +++++++++++++++++------------------ zebra/rt_netlink.h | 5 ++- zebra/rule_netlink.c | 8 ++--- zebra/rule_netlink.h | 2 +- zebra/tc_netlink.c | 8 ++--- zebra/tc_netlink.h | 8 ++--- 14 files changed, 108 insertions(+), 128 deletions(-) diff --git a/zebra/ge_netlink.c b/zebra/ge_netlink.c index 72598584d86b..786a58122c00 100644 --- a/zebra/ge_netlink.c +++ b/zebra/ge_netlink.c @@ -55,7 +55,7 @@ */ static int16_t seg6_genl_family = -1; -static int genl_parse_getfamily(struct nlmsghdr *h, ns_id_t ns_id, int startup) +static int genl_parse_getfamily(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *arg) { int len; struct rtattr *tb[CTRL_ATTR_MAX + 1]; @@ -133,7 +133,7 @@ int genl_resolve_family(const char *family) strlen(family) + 1)) return -1; - return ge_netlink_talk(genl_parse_getfamily, &req.n, zns, false); + return ge_netlink_talk(genl_parse_getfamily, &req.n, zns, false, NULL); } /* @@ -234,7 +234,7 @@ netlink_put_sr_tunsrc_set_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx zns = zebra_ns_lookup(dplane_ctx_get_ns_sock(ctx)); - return ge_netlink_talk(netlink_talk_filter, &req.n, zns, false); + return ge_netlink_talk(netlink_talk_filter, &req.n, zns, false, NULL); } /** @@ -246,7 +246,7 @@ netlink_put_sr_tunsrc_set_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx * * Return: Result status */ -int netlink_sr_tunsrc_reply_read(struct nlmsghdr *h, ns_id_t ns_id, int startup) +int netlink_sr_tunsrc_reply_read(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *arg) { int len; struct genlmsghdr *ghdr; @@ -340,8 +340,8 @@ int netlink_sr_tunsrc_read(struct zebra_ns *zns) ret = netlink_request_sr_tunsrc(zns); if (ret < 0) return ret; - ret = netlink_parse_info(netlink_sr_tunsrc_reply_read, - &zns->ge_netlink_cmd, &dp_info, 0, true); + ret = netlink_parse_info(netlink_sr_tunsrc_reply_read, &zns->ge_netlink_cmd, &dp_info, 0, + true, NULL); if (ret < 0) return ret; diff --git a/zebra/ge_netlink.h b/zebra/ge_netlink.h index 567bcd2eca00..c6ec2d1cb3f4 100644 --- a/zebra/ge_netlink.h +++ b/zebra/ge_netlink.h @@ -42,7 +42,7 @@ extern enum netlink_msg_status netlink_put_sr_tunsrc_set_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx); -int netlink_sr_tunsrc_reply_read(struct nlmsghdr *h, ns_id_t ns_id, int startup); +int netlink_sr_tunsrc_reply_read(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *arg); int netlink_sr_tunsrc_read(struct zebra_ns *zns); extern void ge_netlink_init(struct zebra_ns *zns); diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index c7ee4b180006..5b619c35ae6c 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -907,8 +907,7 @@ int interface_lookup_netlink(struct zebra_ns *zns) ret = netlink_request_intf_addr(netlink_cmd, AF_PACKET, RTM_GETLINK, 0); if (ret < 0) return ret; - ret = netlink_parse_info(netlink_link_change, netlink_cmd, &dp_info, 0, - true); + ret = netlink_parse_info(netlink_link_change, netlink_cmd, &dp_info, 0, true, NULL); if (ret < 0) return ret; @@ -917,8 +916,7 @@ int interface_lookup_netlink(struct zebra_ns *zns) RTEXT_FILTER_BRVLAN); if (ret < 0) return ret; - ret = netlink_parse_info(netlink_link_change, netlink_cmd, &dp_info, 0, - true); + ret = netlink_parse_info(netlink_link_change, netlink_cmd, &dp_info, 0, true, NULL); if (ret < 0) return ret; @@ -966,8 +964,8 @@ static int interface_addr_lookup_netlink(struct zebra_ns *zns) ret = netlink_request_intf_addr(netlink_cmd, AF_INET, RTM_GETADDR, 0); if (ret < 0) return ret; - ret = netlink_parse_info(netlink_interface_addr_dplane, netlink_cmd, - &dp_info, 0, true); + ret = netlink_parse_info(netlink_interface_addr_dplane, netlink_cmd, &dp_info, 0, true, + NULL); if (ret < 0) return ret; @@ -975,8 +973,8 @@ static int interface_addr_lookup_netlink(struct zebra_ns *zns) ret = netlink_request_intf_addr(netlink_cmd, AF_INET6, RTM_GETADDR, 0); if (ret < 0) return ret; - ret = netlink_parse_info(netlink_interface_addr_dplane, netlink_cmd, - &dp_info, 0, true); + ret = netlink_parse_info(netlink_interface_addr_dplane, netlink_cmd, &dp_info, 0, true, + NULL); if (ret < 0) return ret; @@ -1012,8 +1010,7 @@ int kernel_interface_set_master(struct interface *master, return -1; } - return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, - false); + return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, false, NULL); } /* Interface address modification. */ @@ -1133,8 +1130,8 @@ netlink_put_intf_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx) * This runs in the dplane pthread; the context is enqueued to the * main pthread for processing. */ -int netlink_interface_addr_dplane(struct nlmsghdr *h, ns_id_t ns_id, - int startup /*ignored*/) +int netlink_interface_addr_dplane(struct nlmsghdr *h, ns_id_t ns_id, int startup /*ignored*/, + void *arg) { int len; struct ifaddrmsg *ifa; @@ -1369,7 +1366,7 @@ int netlink_interface_addr_dplane(struct nlmsghdr *h, ns_id_t ns_id, return 0; } -int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) +int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *arg) { int len; struct ifinfomsg *ifi; @@ -1763,8 +1760,8 @@ static int tunneldump_walk_cb(struct interface *ifp, void *arg) return NS_WALK_STOP; } - ret = netlink_parse_info(netlink_link_change, &(ctx->zns->netlink_cmd), - ctx->dp_info, 0, true); + ret = netlink_parse_info(netlink_link_change, &(ctx->zns->netlink_cmd), ctx->dp_info, 0, + true, NULL); if (ret < 0) { ctx->ret = ret; @@ -1798,7 +1795,7 @@ static uint8_t netlink_get_dplane_vlan_state(uint8_t state) * * Return: Result status */ -int netlink_vlan_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) +int netlink_vlan_change(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *arg) { int len, rem; struct br_vlan_msg *bvm; @@ -1958,8 +1955,7 @@ int netlink_vlan_read(struct zebra_ns *zns) if (ret < 0) return ret; - ret = netlink_parse_info(netlink_vlan_change, &zns->netlink_cmd, - &dp_info, 0, 1); + ret = netlink_parse_info(netlink_vlan_change, &zns->netlink_cmd, &dp_info, 0, 1, NULL); return ret; } diff --git a/zebra/if_netlink.h b/zebra/if_netlink.h index dc1f71cb7769..603d803f7d52 100644 --- a/zebra/if_netlink.h +++ b/zebra/if_netlink.h @@ -16,13 +16,12 @@ extern "C" { * Parse an incoming interface address change message, generate a dplane * context object for processing. */ -int netlink_interface_addr_dplane(struct nlmsghdr *h, ns_id_t ns_id, - int startup); +int netlink_interface_addr_dplane(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *arg); -extern int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup); +extern int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *arg); extern int interface_lookup_netlink(struct zebra_ns *zns); -extern int netlink_vlan_change(struct nlmsghdr *h, ns_id_t ns_id, int startup); +extern int netlink_vlan_change(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *arg); extern int netlink_vlan_read(struct zebra_ns *zns); extern ssize_t netlink_intf_msg_encode(uint16_t cmd, diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index 06e3276d61bf..fa07a0c00c3e 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -231,7 +231,7 @@ void netlink_set_batch_buffer_size(uint32_t size, uint32_t threshold, bool set) memory_order_relaxed); } -int netlink_talk_filter(struct nlmsghdr *h, ns_id_t ns_id, int startup) +int netlink_talk_filter(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *arg) { /* * This is an error condition that must be handled during @@ -379,8 +379,7 @@ static int netlink_socket(struct nlsock *nl, unsigned long groups, * Dispatch an incoming netlink message; used by the zebra main pthread's * netlink event reader. */ -static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, - int startup) +static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *arg) { /* * When we handle new message types here @@ -393,17 +392,17 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, */ switch (h->nlmsg_type) { case RTM_NEWROUTE: - return netlink_route_change(h, ns_id, startup); + return netlink_route_change(h, ns_id, startup, arg); case RTM_DELROUTE: - return netlink_route_change(h, ns_id, startup); + return netlink_route_change(h, ns_id, startup, arg); case RTM_NEWRULE: - return netlink_rule_change(h, ns_id, startup); + return netlink_rule_change(h, ns_id, startup, arg); case RTM_DELRULE: - return netlink_rule_change(h, ns_id, startup); + return netlink_rule_change(h, ns_id, startup, arg); case RTM_NEWNEXTHOP: - return netlink_nexthop_change(h, ns_id, startup); + return netlink_nexthop_change(h, ns_id, startup, arg); case RTM_DELNEXTHOP: - return netlink_nexthop_change(h, ns_id, startup); + return netlink_nexthop_change(h, ns_id, startup, arg); /* Messages we may receive, but ignore */ case RTM_NEWCHAIN: @@ -454,8 +453,8 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, * Dispatch an incoming netlink message; used by the dataplane pthread's * netlink event reader code. */ -static int dplane_netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, - int startup) +static int dplane_netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, int startup, + void *arg) { /* * Dispatch the incoming messages that the dplane pthread handles @@ -463,21 +462,21 @@ static int dplane_netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, switch (h->nlmsg_type) { case RTM_NEWADDR: case RTM_DELADDR: - return netlink_interface_addr_dplane(h, ns_id, startup); + return netlink_interface_addr_dplane(h, ns_id, startup, arg); case RTM_NEWNETCONF: case RTM_DELNETCONF: - return netlink_netconf_change(h, ns_id, startup); + return netlink_netconf_change(h, ns_id, startup, arg); /* TODO -- other messages for the dplane socket and pthread */ case RTM_NEWLINK: case RTM_DELLINK: - return netlink_link_change(h, ns_id, startup); + return netlink_link_change(h, ns_id, startup, arg); case RTM_NEWVLAN: case RTM_DELVLAN: - return netlink_vlan_change(h, ns_id, startup); + return netlink_vlan_change(h, ns_id, startup, arg); case RTM_NEWNEIGH: case RTM_DELNEIGH: @@ -486,13 +485,13 @@ static int dplane_netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, case RTM_NEWQDISC: case RTM_DELQDISC: - return netlink_qdisc_change(h, ns_id, startup); + return netlink_qdisc_change(h, ns_id, startup, arg); case RTM_NEWTCLASS: case RTM_DELTCLASS: - return netlink_tclass_change(h, ns_id, startup); + return netlink_tclass_change(h, ns_id, startup, arg); case RTM_NEWTFILTER: case RTM_DELTFILTER: - return netlink_tfilter_change(h, ns_id, startup); + return netlink_tfilter_change(h, ns_id, startup, arg); default: break; @@ -509,8 +508,7 @@ static void kernel_read(struct event *event) /* Capture key info from ns struct */ zebra_dplane_info_from_zns(&dp_info, zns, false); - netlink_parse_info(netlink_information_fetch, &zns->netlink, &dp_info, - 5, false); + netlink_parse_info(netlink_information_fetch, &zns->netlink, &dp_info, 5, false, NULL); event_add_read(zrouter.master, kernel_read, zns, zns->netlink.sock, &zns->t_netlink); @@ -523,8 +521,7 @@ int kernel_dplane_read(struct zebra_dplane_info *info) { struct nlsock *nl = kernel_netlink_nlsock_lookup(info->sock); - netlink_parse_info(dplane_netlink_information_fetch, nl, info, 5, - false); + netlink_parse_info(dplane_netlink_information_fetch, nl, info, 5, false, NULL); return 0; } @@ -928,9 +925,8 @@ static int netlink_parse_error(const struct nlsock *nl, struct nlmsghdr *h, * startup -> Are we reading in under startup conditions? passed to * the filter. */ -int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int), - struct nlsock *nl, const struct zebra_dplane_info *zns, - int count, bool startup) +int netlink_parse_info(netlink_parse_filter_t filter, struct nlsock *nl, + const struct zebra_dplane_info *zns, int count, bool startup, void *arg) { int status; int ret = 0; @@ -1004,7 +1000,7 @@ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int), continue; } - error = (*filter)(h, zns->ns_id, startup); + error = (*filter)(h, zns->ns_id, startup, arg); if (error < 0) { zlog_debug("%s filter function error", nl->name); @@ -1040,10 +1036,8 @@ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int), * startup -> Are we reading in under startup conditions * This is passed through eventually to filter. */ -static int netlink_talk_info(int (*filter)(struct nlmsghdr *, ns_id_t, - int startup), - struct nlmsghdr *n, - struct zebra_dplane_info *dp_info, bool startup) +static int netlink_talk_info(netlink_parse_filter_t filter, struct nlmsghdr *n, + struct zebra_dplane_info *dp_info, bool startup, void *arg) { struct nlsock *nl; @@ -1065,16 +1059,15 @@ static int netlink_talk_info(int (*filter)(struct nlmsghdr *, ns_id_t, * Get reply from netlink socket. * The reply should either be an acknowledgement or an error. */ - return netlink_parse_info(filter, nl, dp_info, 0, startup); + return netlink_parse_info(filter, nl, dp_info, 0, startup, arg); } /* * Synchronous version of netlink_talk_info. Converts args to suit the * common version, which is suitable for both sync and async use. */ -int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), - struct nlmsghdr *n, struct nlsock *nl, struct zebra_ns *zns, - bool startup) +int netlink_talk(netlink_parse_filter_t filter, struct nlmsghdr *n, struct nlsock *nl, + struct zebra_ns *zns, bool startup, void *arg) { struct zebra_dplane_info dp_info; @@ -1086,15 +1079,15 @@ int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), /* Capture info in intermediate info struct */ zebra_dplane_info_from_zns(&dp_info, zns, (nl == &(zns->netlink_cmd))); - return netlink_talk_info(filter, n, &dp_info, startup); + return netlink_talk_info(filter, n, &dp_info, startup, arg); } /* * Synchronous version of netlink_talk_info. Converts args to suit the * common version, which is suitable for both sync and async use. */ -int ge_netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), - struct nlmsghdr *n, struct zebra_ns *zns, bool startup) +int ge_netlink_talk(netlink_parse_filter_t filter, struct nlmsghdr *n, struct zebra_ns *zns, + bool startup, void *arg) { struct zebra_dplane_info dp_info; @@ -1113,7 +1106,7 @@ int ge_netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), dp_info.sock = zns->ge_netlink_cmd.sock; dp_info.seq = zns->ge_netlink_cmd.seq; - return netlink_talk_info(filter, n, &dp_info, startup); + return netlink_talk_info(filter, n, &dp_info, startup, arg); } /* Issue request message to kernel via netlink socket. GET messages diff --git a/zebra/kernel_netlink.h b/zebra/kernel_netlink.h index 6a5141ca7095..db6744822c9b 100644 --- a/zebra/kernel_netlink.h +++ b/zebra/kernel_netlink.h @@ -43,14 +43,16 @@ extern const char *nl_rtproto_to_str(uint8_t rtproto); extern const char *nl_family_to_str(uint8_t family); extern const char *nl_rttype_to_str(uint8_t rttype); -extern int netlink_parse_info(int (*filter)(struct nlmsghdr *h, ns_id_t ns_id, int startup), - struct nlsock *nl, const struct zebra_dplane_info *dp_info, - int count, bool startup); -extern int netlink_talk_filter(struct nlmsghdr *h, ns_id_t ns, int startup); -extern int netlink_talk(int (*filter)(struct nlmsghdr *h, ns_id_t ns_id, int startup), - struct nlmsghdr *n, struct nlsock *nl, struct zebra_ns *zns, bool startup); -extern int ge_netlink_talk(int (*filter)(struct nlmsghdr *h, ns_id_t ns_id, int startup), - struct nlmsghdr *n, struct zebra_ns *zns, bool startup); +typedef int (*netlink_parse_filter_t)(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *arg); + +extern int netlink_parse_info(netlink_parse_filter_t filter, struct nlsock *nl, + const struct zebra_dplane_info *dp_info, int count, bool startup, + void *arg); +extern int netlink_talk_filter(struct nlmsghdr *h, ns_id_t ns, int startup, void *arg); +extern int netlink_talk(netlink_parse_filter_t filter, struct nlmsghdr *n, struct nlsock *nl, + struct zebra_ns *zns, bool startup, void *arg); +extern int ge_netlink_talk(netlink_parse_filter_t filter, struct nlmsghdr *n, struct zebra_ns *zns, + bool startup, void *arg); extern int netlink_request(struct nlsock *nl, void *req); enum netlink_msg_status { diff --git a/zebra/netconf_netlink.c b/zebra/netconf_netlink.c index 8b4966def76a..7cbe42f03abf 100644 --- a/zebra/netconf_netlink.c +++ b/zebra/netconf_netlink.c @@ -62,7 +62,7 @@ netlink_netconf_dplane_update(ns_id_t ns_id, afi_t afi, ifindex_t ifindex, /* * Parse and process an incoming netlink netconf update. */ -int netlink_netconf_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) +int netlink_netconf_change(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *arg) { struct netconfmsg *ncm; struct rtattr *tb[NETCONFA_MAX + 1] = {}; diff --git a/zebra/netconf_netlink.h b/zebra/netconf_netlink.h index 3abc72e2fbbc..795998e52552 100644 --- a/zebra/netconf_netlink.h +++ b/zebra/netconf_netlink.h @@ -17,8 +17,7 @@ extern "C" { #endif /* Parse and handle a NETCONF message. */ -extern int netlink_netconf_change(struct nlmsghdr *h, ns_id_t ns_id, - int startup); +extern int netlink_netconf_change(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *arg); /* Request info from the host OS. */ int netlink_request_netconf(int sockfd); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 81a1d653d426..d04304a630fd 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1391,16 +1391,16 @@ static int netlink_route_change_read_unicast_internal(struct nlmsghdr *h, return ret; } -static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id, - int startup) +static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id, int startup, + void *arg) { return netlink_route_change_read_unicast_internal(h, ns_id, startup); } static struct mcast_route_data *mroute = NULL; -static int netlink_route_change_read_multicast(struct nlmsghdr *h, - ns_id_t ns_id, int startup) +static int netlink_route_change_read_multicast(struct nlmsghdr *h, ns_id_t ns_id, int startup, + void *arg) { int len; struct rtmsg *rtm; @@ -1512,7 +1512,7 @@ static int netlink_route_change_read_multicast(struct nlmsghdr *h, return 0; } -int netlink_route_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) +int netlink_route_change(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *arg) { int len; struct rtmsg *rtm; @@ -1571,7 +1571,7 @@ int netlink_route_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) if (rtm->rtm_type == RTN_MULTICAST) return 0; - netlink_route_change_read_unicast(h, ns_id, startup); + netlink_route_change_read_unicast(h, ns_id, startup, arg); return 0; } @@ -1606,8 +1606,8 @@ int netlink_route_read(struct zebra_ns *zns) ret = netlink_request_route(zns, AF_INET, RTM_GETROUTE); if (ret < 0) return ret; - ret = netlink_parse_info(netlink_route_change_read_unicast, - &zns->netlink_cmd, &dp_info, 0, true); + ret = netlink_parse_info(netlink_route_change_read_unicast, &zns->netlink_cmd, &dp_info, 0, + true, NULL); if (ret < 0) return ret; @@ -1615,8 +1615,8 @@ int netlink_route_read(struct zebra_ns *zns) ret = netlink_request_route(zns, AF_INET6, RTM_GETROUTE); if (ret < 0) return ret; - ret = netlink_parse_info(netlink_route_change_read_unicast, - &zns->netlink_cmd, &dp_info, 0, true); + ret = netlink_parse_info(netlink_route_change_read_unicast, &zns->netlink_cmd, &dp_info, 0, + true, NULL); if (ret < 0) return ret; @@ -2501,8 +2501,7 @@ static int netlink_neigh_update(int cmd, int ifindex, void *addr, char *lla, ifp->vrf->vrf_id); } } - return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, - false); + return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, false, NULL); } static bool nexthop_set_src(const struct nexthop *nexthop, int family, @@ -2986,8 +2985,8 @@ int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *in) return 0; } - suc = netlink_talk(netlink_route_change_read_multicast, &req.n, - &zns->netlink_cmd, zns, false); + suc = netlink_talk(netlink_route_change_read_multicast, &req.n, &zns->netlink_cmd, zns, + false, NULL); mroute = NULL; return suc; @@ -3663,7 +3662,7 @@ static int netlink_nexthop_process_group(struct rtattr **tb, * * Return: Result status */ -int netlink_nexthop_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) +int netlink_nexthop_change(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *arg) { int len; /* nexthop group id */ @@ -3831,8 +3830,8 @@ int netlink_nexthop_read(struct zebra_ns *zns) ret = netlink_request_nexthop(zns, AF_UNSPEC, RTM_GETNEXTHOP); if (ret < 0) return ret; - ret = netlink_parse_info(netlink_nexthop_change, &zns->netlink_cmd, - &dp_info, 0, true); + ret = netlink_parse_info(netlink_nexthop_change, &zns->netlink_cmd, &dp_info, 0, true, + NULL); if (!ret) /* If we successfully read in nexthop objects, @@ -4175,7 +4174,7 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id) return 0; } -static int netlink_macfdb_table(struct nlmsghdr *h, ns_id_t ns_id, int startup) +static int netlink_macfdb_table(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *arg) { int len; struct ndmsg *ndm; @@ -4235,7 +4234,7 @@ static int netlink_macfdb_read(struct nlsock *nl, const struct zebra_dplane_info return ret; /* We are reading entire table. */ filter_vlan = 0; - ret = netlink_parse_info(netlink_macfdb_table, nl, dp_info, 0, true); + ret = netlink_parse_info(netlink_macfdb_table, nl, dp_info, 0, true, NULL); return ret; } @@ -4261,7 +4260,7 @@ static int netlink_macfdb_read_for_bridge(struct nlsock *nl, if (ret < 0) goto done; - ret = netlink_parse_info(netlink_macfdb_table, nl, dp_info, 0, false); + ret = netlink_parse_info(netlink_macfdb_table, nl, dp_info, 0, false, NULL); done: /* Reset VLAN filter. */ @@ -4338,7 +4337,7 @@ static int netlink_macfdb_read_specific_mac(struct nlsock *nl, if (ret < 0) return ret; - return netlink_parse_info(netlink_macfdb_table, nl, dp_info, 1, false); + return netlink_parse_info(netlink_macfdb_table, nl, dp_info, 1, false, NULL); } static int netlink_macfdb_read_mcast_for_vni(struct nlsock *nl, @@ -4354,7 +4353,7 @@ static int netlink_macfdb_read_mcast_for_vni(struct nlsock *nl, if (ret < 0) return ret; - return netlink_parse_info(netlink_macfdb_table, nl, dp_info, 1, false); + return netlink_parse_info(netlink_macfdb_table, nl, dp_info, 1, false, NULL); } void kernel_read_macfdb(struct zebra_dplane_ctx *ctx) @@ -4637,7 +4636,7 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id) return 0; } -static int netlink_neigh_table(struct nlmsghdr *h, ns_id_t ns_id, int startup) +static int netlink_neigh_table(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *arg) { int len; struct ndmsg *ndm; @@ -4747,15 +4746,15 @@ void kernel_read_neigh(struct zebra_dplane_ctx *ctx) if (ip->ipa_type != IPADDR_NONE) { ret = netlink_request_specific_neigh_in_vlan(nl, RTM_GETNEIGH, ip, ifindex); if (ret >= 0) - netlink_parse_info(netlink_neigh_table, nl, dp_info, 1, false); + netlink_parse_info(netlink_neigh_table, nl, dp_info, 1, false, NULL); } else if (ifindex) { ret = netlink_request_neigh(nl, AF_UNSPEC, RTM_GETNEIGH, ifindex); if (ret >= 0) - netlink_parse_info(netlink_neigh_table, nl, dp_info, 0, false); + netlink_parse_info(netlink_neigh_table, nl, dp_info, 0, false, NULL); } else { ret = netlink_request_neigh(nl, AF_UNSPEC, RTM_GETNEIGH, 0); if (ret >= 0) - netlink_parse_info(netlink_neigh_table, nl, dp_info, 0, true); + netlink_parse_info(netlink_neigh_table, nl, dp_info, 0, true, NULL); } dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS); @@ -5172,8 +5171,7 @@ static int netlink_fdb_nh_update(uint32_t nh_id, struct ipaddr *vtep_ip) if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_EVPN_MH_NH) zlog_debug("Tx %s fdb-nh 0x%x %pIA", nl_msg_type_to_str(cmd), nh_id, vtep_ip); - return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, - false); + return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, false, NULL); } static int netlink_fdb_nh_del(uint32_t nh_id) @@ -5205,8 +5203,7 @@ static int netlink_fdb_nh_del(uint32_t nh_id) nl_msg_type_to_str(cmd), nh_id); } - return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, - false); + return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, false, NULL); } static int netlink_fdb_nhg_update(uint32_t nhg_id, uint32_t nh_cnt, @@ -5263,8 +5260,7 @@ static int netlink_fdb_nhg_update(uint32_t nhg_id, uint32_t nh_cnt, nl_msg_type_to_str(cmd), nhg_id, vtep_str); } - return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, - false); + return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, false, NULL); } static int netlink_fdb_nhg_del(uint32_t nhg_id) diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h index dc883cd891d9..15f762ad1fd0 100644 --- a/zebra/rt_netlink.h +++ b/zebra/rt_netlink.h @@ -61,7 +61,7 @@ extern ssize_t netlink_route_multipath_msg_encode(int cmd, extern ssize_t netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, void *data, size_t datalen); -extern int netlink_route_change(struct nlmsghdr *h, ns_id_t ns_id, int startup); +extern int netlink_route_change(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *arg); extern int netlink_route_read(struct zebra_ns *zns); /* @@ -73,8 +73,7 @@ extern int netlink_route_read(struct zebra_ns *zns); int netlink_route_notify_read_ctx(struct nlmsghdr *h, ns_id_t ns_id, struct zebra_dplane_ctx *ctx); -extern int netlink_nexthop_change(struct nlmsghdr *h, ns_id_t ns_id, - int startup); +extern int netlink_nexthop_change(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *arg); extern int netlink_nexthop_read(struct zebra_ns *zns); extern ssize_t netlink_nexthop_msg_encode(uint16_t cmd, const struct zebra_dplane_ctx *ctx, diff --git a/zebra/rule_netlink.c b/zebra/rule_netlink.c index 1d6e9ebe1fc4..e7ba0971a8e5 100644 --- a/zebra/rule_netlink.c +++ b/zebra/rule_netlink.c @@ -245,7 +245,7 @@ netlink_put_rule_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx) * from a previous instance and should have been removed on shutdown. * */ -int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) +int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *arg) { struct zebra_ns *zns; struct fib_rule_hdr *frh; @@ -416,8 +416,7 @@ int netlink_rules_read(struct zebra_ns *zns) if (ret < 0) return ret; - ret = netlink_parse_info(netlink_rule_change, &zns->netlink_cmd, - &dp_info, 0, true); + ret = netlink_parse_info(netlink_rule_change, &zns->netlink_cmd, &dp_info, 0, true, NULL); if (ret < 0) return ret; @@ -425,8 +424,7 @@ int netlink_rules_read(struct zebra_ns *zns) if (ret < 0) return ret; - ret = netlink_parse_info(netlink_rule_change, &zns->netlink_cmd, - &dp_info, 0, true); + ret = netlink_parse_info(netlink_rule_change, &zns->netlink_cmd, &dp_info, 0, true, NULL); return ret; } diff --git a/zebra/rule_netlink.h b/zebra/rule_netlink.h index 8ffca49b6f0d..04ac038d4936 100644 --- a/zebra/rule_netlink.h +++ b/zebra/rule_netlink.h @@ -17,7 +17,7 @@ extern "C" { /* * Handle netlink notification informing a rule add or delete. */ -extern int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup); +extern int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *arg); /* * Get to know existing PBR rules in the kernel - typically called at startup. diff --git a/zebra/tc_netlink.c b/zebra/tc_netlink.c index da433cbbb1a2..bb4aa039a89e 100644 --- a/zebra/tc_netlink.c +++ b/zebra/tc_netlink.c @@ -705,7 +705,7 @@ static int netlink_request_qdiscs(struct nlsock *nl, int family, int type) return netlink_request(nl, &req); } -int netlink_qdisc_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) +int netlink_qdisc_change(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *arg) { struct tcmsg *tcm; enum tc_qdisc_kind kind = TC_QDISC_UNSPEC; @@ -747,7 +747,7 @@ int netlink_qdisc_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) return 0; } -int netlink_tclass_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) +int netlink_tclass_change(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *arg) { struct tcmsg *tcm; @@ -782,7 +782,7 @@ int netlink_tclass_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) return 0; } -int netlink_tfilter_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) +int netlink_tfilter_change(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *arg) { struct tcmsg *tcm; @@ -822,7 +822,7 @@ void kernel_read_tc_qdisc(struct zebra_dplane_ctx *ctx) ret = netlink_request_qdiscs(nl, AF_UNSPEC, RTM_GETQDISC); if (ret >= 0) - netlink_parse_info(netlink_qdisc_change, nl, dp_info, 0, true); + netlink_parse_info(netlink_qdisc_change, nl, dp_info, 0, true, NULL); dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS); diff --git a/zebra/tc_netlink.h b/zebra/tc_netlink.h index b23ef3cf02b9..afdd4f5e8c46 100644 --- a/zebra/tc_netlink.h +++ b/zebra/tc_netlink.h @@ -49,11 +49,9 @@ netlink_put_tc_filter_update_msg(struct nl_batch *bth, * the sake of consistency with kernel message types (RTM_NEWTFILTER etc.) */ -extern int netlink_tfilter_change(struct nlmsghdr *h, ns_id_t ns_id, - int startup); -extern int netlink_tclass_change(struct nlmsghdr *h, ns_id_t ns_id, - int startup); -extern int netlink_qdisc_change(struct nlmsghdr *h, ns_id_t ns_id, int startup); +extern int netlink_tfilter_change(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *arg); +extern int netlink_tclass_change(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *arg); +extern int netlink_qdisc_change(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *arg); #ifdef __cplusplus From c78988f7504f07404300b16787df47f51cdc8630 Mon Sep 17 00:00:00 2001 From: Maxime Leroy Date: Mon, 25 May 2026 12:45:05 +0200 Subject: [PATCH 3/6] zebra: thread netlink error code through parse callbacks Add a int *nl_err out parameter to netlink_parse_info(), netlink_talk(), netlink_talk_info() and ge_netlink_talk(). On NLMSG_ERROR replies the kernel-reported errno from nlmsgerr->error is written into *nl_err if non-NULL, so the caller can distinguish e.g. -ENODEV from other failures. Existing callers pass NULL and see no behavior change. Needed for the next commit, which uses the error code to drive the speed retry logic. Signed-off-by: Maxime Leroy --- zebra/ge_netlink.c | 6 +++--- zebra/if_netlink.c | 14 +++++++------- zebra/kernel_netlink.c | 34 +++++++++++++++++++++++----------- zebra/kernel_netlink.h | 6 +++--- zebra/rt_netlink.c | 30 +++++++++++++++--------------- zebra/rule_netlink.c | 4 ++-- zebra/tc_netlink.c | 2 +- 7 files changed, 54 insertions(+), 42 deletions(-) diff --git a/zebra/ge_netlink.c b/zebra/ge_netlink.c index 786a58122c00..364a0244e1cb 100644 --- a/zebra/ge_netlink.c +++ b/zebra/ge_netlink.c @@ -133,7 +133,7 @@ int genl_resolve_family(const char *family) strlen(family) + 1)) return -1; - return ge_netlink_talk(genl_parse_getfamily, &req.n, zns, false, NULL); + return ge_netlink_talk(genl_parse_getfamily, &req.n, zns, false, NULL, NULL); } /* @@ -234,7 +234,7 @@ netlink_put_sr_tunsrc_set_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx zns = zebra_ns_lookup(dplane_ctx_get_ns_sock(ctx)); - return ge_netlink_talk(netlink_talk_filter, &req.n, zns, false, NULL); + return ge_netlink_talk(netlink_talk_filter, &req.n, zns, false, NULL, NULL); } /** @@ -341,7 +341,7 @@ int netlink_sr_tunsrc_read(struct zebra_ns *zns) if (ret < 0) return ret; ret = netlink_parse_info(netlink_sr_tunsrc_reply_read, &zns->ge_netlink_cmd, &dp_info, 0, - true, NULL); + true, NULL, NULL); if (ret < 0) return ret; diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index 5b619c35ae6c..7944a00ba673 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -907,7 +907,7 @@ int interface_lookup_netlink(struct zebra_ns *zns) ret = netlink_request_intf_addr(netlink_cmd, AF_PACKET, RTM_GETLINK, 0); if (ret < 0) return ret; - ret = netlink_parse_info(netlink_link_change, netlink_cmd, &dp_info, 0, true, NULL); + ret = netlink_parse_info(netlink_link_change, netlink_cmd, &dp_info, 0, true, NULL, NULL); if (ret < 0) return ret; @@ -916,7 +916,7 @@ int interface_lookup_netlink(struct zebra_ns *zns) RTEXT_FILTER_BRVLAN); if (ret < 0) return ret; - ret = netlink_parse_info(netlink_link_change, netlink_cmd, &dp_info, 0, true, NULL); + ret = netlink_parse_info(netlink_link_change, netlink_cmd, &dp_info, 0, true, NULL, NULL); if (ret < 0) return ret; @@ -965,7 +965,7 @@ static int interface_addr_lookup_netlink(struct zebra_ns *zns) if (ret < 0) return ret; ret = netlink_parse_info(netlink_interface_addr_dplane, netlink_cmd, &dp_info, 0, true, - NULL); + NULL, NULL); if (ret < 0) return ret; @@ -974,7 +974,7 @@ static int interface_addr_lookup_netlink(struct zebra_ns *zns) if (ret < 0) return ret; ret = netlink_parse_info(netlink_interface_addr_dplane, netlink_cmd, &dp_info, 0, true, - NULL); + NULL, NULL); if (ret < 0) return ret; @@ -1010,7 +1010,7 @@ int kernel_interface_set_master(struct interface *master, return -1; } - return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, false, NULL); + return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, false, NULL, NULL); } /* Interface address modification. */ @@ -1761,7 +1761,7 @@ static int tunneldump_walk_cb(struct interface *ifp, void *arg) } ret = netlink_parse_info(netlink_link_change, &(ctx->zns->netlink_cmd), ctx->dp_info, 0, - true, NULL); + true, NULL, NULL); if (ret < 0) { ctx->ret = ret; @@ -1955,7 +1955,7 @@ int netlink_vlan_read(struct zebra_ns *zns) if (ret < 0) return ret; - ret = netlink_parse_info(netlink_vlan_change, &zns->netlink_cmd, &dp_info, 0, 1, NULL); + ret = netlink_parse_info(netlink_vlan_change, &zns->netlink_cmd, &dp_info, 0, 1, NULL, NULL); return ret; } diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index fa07a0c00c3e..cac8f92d9324 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -508,7 +508,7 @@ static void kernel_read(struct event *event) /* Capture key info from ns struct */ zebra_dplane_info_from_zns(&dp_info, zns, false); - netlink_parse_info(netlink_information_fetch, &zns->netlink, &dp_info, 5, false, NULL); + netlink_parse_info(netlink_information_fetch, &zns->netlink, &dp_info, 5, false, NULL, NULL); event_add_read(zrouter.master, kernel_read, zns, zns->netlink.sock, &zns->t_netlink); @@ -521,7 +521,7 @@ int kernel_dplane_read(struct zebra_dplane_info *info) { struct nlsock *nl = kernel_netlink_nlsock_lookup(info->sock); - netlink_parse_info(dplane_netlink_information_fetch, nl, info, 5, false, NULL); + netlink_parse_info(dplane_netlink_information_fetch, nl, info, 5, false, NULL, NULL); return 0; } @@ -926,7 +926,8 @@ static int netlink_parse_error(const struct nlsock *nl, struct nlmsghdr *h, * the filter. */ int netlink_parse_info(netlink_parse_filter_t filter, struct nlsock *nl, - const struct zebra_dplane_info *zns, int count, bool startup, void *arg) + const struct zebra_dplane_info *zns, int count, bool startup, void *arg, + int *nl_err) { int status; int ret = 0; @@ -965,8 +966,18 @@ int netlink_parse_info(netlink_parse_filter_t filter, struct nlsock *nl, if (!(h->nlmsg_flags & NLM_F_MULTI)) return 0; continue; - } else - return err; + } + /* + * Real error: expose the kernel-reported code + * to the caller if requested, then bail out. + */ + if (nl_err && + h->nlmsg_len >= NLMSG_LENGTH(sizeof(struct nlmsgerr))) { + struct nlmsgerr *nlerr = NLMSG_DATA(h); + + *nl_err = nlerr->error; + } + return err; } /* @@ -1037,7 +1048,8 @@ int netlink_parse_info(netlink_parse_filter_t filter, struct nlsock *nl, * This is passed through eventually to filter. */ static int netlink_talk_info(netlink_parse_filter_t filter, struct nlmsghdr *n, - struct zebra_dplane_info *dp_info, bool startup, void *arg) + struct zebra_dplane_info *dp_info, bool startup, void *arg, + int *nl_err) { struct nlsock *nl; @@ -1059,7 +1071,7 @@ static int netlink_talk_info(netlink_parse_filter_t filter, struct nlmsghdr *n, * Get reply from netlink socket. * The reply should either be an acknowledgement or an error. */ - return netlink_parse_info(filter, nl, dp_info, 0, startup, arg); + return netlink_parse_info(filter, nl, dp_info, 0, startup, arg, nl_err); } /* @@ -1067,7 +1079,7 @@ static int netlink_talk_info(netlink_parse_filter_t filter, struct nlmsghdr *n, * common version, which is suitable for both sync and async use. */ int netlink_talk(netlink_parse_filter_t filter, struct nlmsghdr *n, struct nlsock *nl, - struct zebra_ns *zns, bool startup, void *arg) + struct zebra_ns *zns, bool startup, void *arg, int *nl_err) { struct zebra_dplane_info dp_info; @@ -1079,7 +1091,7 @@ int netlink_talk(netlink_parse_filter_t filter, struct nlmsghdr *n, struct nlsoc /* Capture info in intermediate info struct */ zebra_dplane_info_from_zns(&dp_info, zns, (nl == &(zns->netlink_cmd))); - return netlink_talk_info(filter, n, &dp_info, startup, arg); + return netlink_talk_info(filter, n, &dp_info, startup, arg, nl_err); } /* @@ -1087,7 +1099,7 @@ int netlink_talk(netlink_parse_filter_t filter, struct nlmsghdr *n, struct nlsoc * common version, which is suitable for both sync and async use. */ int ge_netlink_talk(netlink_parse_filter_t filter, struct nlmsghdr *n, struct zebra_ns *zns, - bool startup, void *arg) + bool startup, void *arg, int *nl_err) { struct zebra_dplane_info dp_info; @@ -1106,7 +1118,7 @@ int ge_netlink_talk(netlink_parse_filter_t filter, struct nlmsghdr *n, struct ze dp_info.sock = zns->ge_netlink_cmd.sock; dp_info.seq = zns->ge_netlink_cmd.seq; - return netlink_talk_info(filter, n, &dp_info, startup, arg); + return netlink_talk_info(filter, n, &dp_info, startup, arg, nl_err); } /* Issue request message to kernel via netlink socket. GET messages diff --git a/zebra/kernel_netlink.h b/zebra/kernel_netlink.h index db6744822c9b..d920d2581093 100644 --- a/zebra/kernel_netlink.h +++ b/zebra/kernel_netlink.h @@ -47,12 +47,12 @@ typedef int (*netlink_parse_filter_t)(struct nlmsghdr *h, ns_id_t ns_id, int sta extern int netlink_parse_info(netlink_parse_filter_t filter, struct nlsock *nl, const struct zebra_dplane_info *dp_info, int count, bool startup, - void *arg); + void *arg, int *nl_err); extern int netlink_talk_filter(struct nlmsghdr *h, ns_id_t ns, int startup, void *arg); extern int netlink_talk(netlink_parse_filter_t filter, struct nlmsghdr *n, struct nlsock *nl, - struct zebra_ns *zns, bool startup, void *arg); + struct zebra_ns *zns, bool startup, void *arg, int *nl_err); extern int ge_netlink_talk(netlink_parse_filter_t filter, struct nlmsghdr *n, struct zebra_ns *zns, - bool startup, void *arg); + bool startup, void *arg, int *nl_err); extern int netlink_request(struct nlsock *nl, void *req); enum netlink_msg_status { diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index d04304a630fd..e396f29fe4f9 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1607,7 +1607,7 @@ int netlink_route_read(struct zebra_ns *zns) if (ret < 0) return ret; ret = netlink_parse_info(netlink_route_change_read_unicast, &zns->netlink_cmd, &dp_info, 0, - true, NULL); + true, NULL, NULL); if (ret < 0) return ret; @@ -1616,7 +1616,7 @@ int netlink_route_read(struct zebra_ns *zns) if (ret < 0) return ret; ret = netlink_parse_info(netlink_route_change_read_unicast, &zns->netlink_cmd, &dp_info, 0, - true, NULL); + true, NULL, NULL); if (ret < 0) return ret; @@ -2501,7 +2501,7 @@ static int netlink_neigh_update(int cmd, int ifindex, void *addr, char *lla, ifp->vrf->vrf_id); } } - return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, false, NULL); + return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, false, NULL, NULL); } static bool nexthop_set_src(const struct nexthop *nexthop, int family, @@ -2986,7 +2986,7 @@ int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *in) } suc = netlink_talk(netlink_route_change_read_multicast, &req.n, &zns->netlink_cmd, zns, - false, NULL); + false, NULL, NULL); mroute = NULL; return suc; @@ -3831,7 +3831,7 @@ int netlink_nexthop_read(struct zebra_ns *zns) if (ret < 0) return ret; ret = netlink_parse_info(netlink_nexthop_change, &zns->netlink_cmd, &dp_info, 0, true, - NULL); + NULL, NULL); if (!ret) /* If we successfully read in nexthop objects, @@ -4234,7 +4234,7 @@ static int netlink_macfdb_read(struct nlsock *nl, const struct zebra_dplane_info return ret; /* We are reading entire table. */ filter_vlan = 0; - ret = netlink_parse_info(netlink_macfdb_table, nl, dp_info, 0, true, NULL); + ret = netlink_parse_info(netlink_macfdb_table, nl, dp_info, 0, true, NULL, NULL); return ret; } @@ -4260,7 +4260,7 @@ static int netlink_macfdb_read_for_bridge(struct nlsock *nl, if (ret < 0) goto done; - ret = netlink_parse_info(netlink_macfdb_table, nl, dp_info, 0, false, NULL); + ret = netlink_parse_info(netlink_macfdb_table, nl, dp_info, 0, false, NULL, NULL); done: /* Reset VLAN filter. */ @@ -4337,7 +4337,7 @@ static int netlink_macfdb_read_specific_mac(struct nlsock *nl, if (ret < 0) return ret; - return netlink_parse_info(netlink_macfdb_table, nl, dp_info, 1, false, NULL); + return netlink_parse_info(netlink_macfdb_table, nl, dp_info, 1, false, NULL, NULL); } static int netlink_macfdb_read_mcast_for_vni(struct nlsock *nl, @@ -4353,7 +4353,7 @@ static int netlink_macfdb_read_mcast_for_vni(struct nlsock *nl, if (ret < 0) return ret; - return netlink_parse_info(netlink_macfdb_table, nl, dp_info, 1, false, NULL); + return netlink_parse_info(netlink_macfdb_table, nl, dp_info, 1, false, NULL, NULL); } void kernel_read_macfdb(struct zebra_dplane_ctx *ctx) @@ -4746,15 +4746,15 @@ void kernel_read_neigh(struct zebra_dplane_ctx *ctx) if (ip->ipa_type != IPADDR_NONE) { ret = netlink_request_specific_neigh_in_vlan(nl, RTM_GETNEIGH, ip, ifindex); if (ret >= 0) - netlink_parse_info(netlink_neigh_table, nl, dp_info, 1, false, NULL); + netlink_parse_info(netlink_neigh_table, nl, dp_info, 1, false, NULL, NULL); } else if (ifindex) { ret = netlink_request_neigh(nl, AF_UNSPEC, RTM_GETNEIGH, ifindex); if (ret >= 0) - netlink_parse_info(netlink_neigh_table, nl, dp_info, 0, false, NULL); + netlink_parse_info(netlink_neigh_table, nl, dp_info, 0, false, NULL, NULL); } else { ret = netlink_request_neigh(nl, AF_UNSPEC, RTM_GETNEIGH, 0); if (ret >= 0) - netlink_parse_info(netlink_neigh_table, nl, dp_info, 0, true, NULL); + netlink_parse_info(netlink_neigh_table, nl, dp_info, 0, true, NULL, NULL); } dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS); @@ -5171,7 +5171,7 @@ static int netlink_fdb_nh_update(uint32_t nh_id, struct ipaddr *vtep_ip) if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_EVPN_MH_NH) zlog_debug("Tx %s fdb-nh 0x%x %pIA", nl_msg_type_to_str(cmd), nh_id, vtep_ip); - return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, false, NULL); + return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, false, NULL, NULL); } static int netlink_fdb_nh_del(uint32_t nh_id) @@ -5203,7 +5203,7 @@ static int netlink_fdb_nh_del(uint32_t nh_id) nl_msg_type_to_str(cmd), nh_id); } - return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, false, NULL); + return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, false, NULL, NULL); } static int netlink_fdb_nhg_update(uint32_t nhg_id, uint32_t nh_cnt, @@ -5260,7 +5260,7 @@ static int netlink_fdb_nhg_update(uint32_t nhg_id, uint32_t nh_cnt, nl_msg_type_to_str(cmd), nhg_id, vtep_str); } - return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, false, NULL); + return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, false, NULL, NULL); } static int netlink_fdb_nhg_del(uint32_t nhg_id) diff --git a/zebra/rule_netlink.c b/zebra/rule_netlink.c index e7ba0971a8e5..9747af6ccb78 100644 --- a/zebra/rule_netlink.c +++ b/zebra/rule_netlink.c @@ -416,7 +416,7 @@ int netlink_rules_read(struct zebra_ns *zns) if (ret < 0) return ret; - ret = netlink_parse_info(netlink_rule_change, &zns->netlink_cmd, &dp_info, 0, true, NULL); + ret = netlink_parse_info(netlink_rule_change, &zns->netlink_cmd, &dp_info, 0, true, NULL, NULL); if (ret < 0) return ret; @@ -424,7 +424,7 @@ int netlink_rules_read(struct zebra_ns *zns) if (ret < 0) return ret; - ret = netlink_parse_info(netlink_rule_change, &zns->netlink_cmd, &dp_info, 0, true, NULL); + ret = netlink_parse_info(netlink_rule_change, &zns->netlink_cmd, &dp_info, 0, true, NULL, NULL); return ret; } diff --git a/zebra/tc_netlink.c b/zebra/tc_netlink.c index bb4aa039a89e..67b2018295b0 100644 --- a/zebra/tc_netlink.c +++ b/zebra/tc_netlink.c @@ -822,7 +822,7 @@ void kernel_read_tc_qdisc(struct zebra_dplane_ctx *ctx) ret = netlink_request_qdiscs(nl, AF_UNSPEC, RTM_GETQDISC); if (ret >= 0) - netlink_parse_info(netlink_qdisc_change, nl, dp_info, 0, true, NULL); + netlink_parse_info(netlink_qdisc_change, nl, dp_info, 0, true, NULL, NULL); dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS); From 9be87789b7acae005df52c64dd658db257f57a7c Mon Sep 17 00:00:00 2001 From: Maxime Leroy Date: Mon, 25 May 2026 11:33:40 +0200 Subject: [PATCH 4/6] zebra: query interface speed via ethtool over netlink Replace the SIOCETHTOOL ioctl path of kernel_get_speed() with a generic-netlink query (ETHTOOL_MSG_LINKMODES_GET) issued on the per-ns ge_netlink_cmd socket. The socket is pre-opened and pinned to its netns at zns creation, so the query no longer needs vrf_socket -> setns -> vrf_lookup_by_id, removing the master-only work from this path. The reply parser uses the new netlink_parse_filter_t arg to return the speed in a stack-allocated struct instead of a file-scoped global. Kernel sentinel UINT32_MAX and "no LINKMODES_SPEED attr" both map to INTERFACE_SPEED_ERROR_UNKNOWN (retry-worthy); ENODEV and "ethtool family not registered" (kernel < 5.5) map to INTERFACE_SPEED_ERROR_READ (permanent). Requires Linux 5.5+ for ethtool over netlink. Older kernels report the speed as unknown and the existing speed-update path falls back to its default. Signed-off-by: Maxime Leroy --- include/linux/ethtool_netlink.h | 153 ++++++++++++++++++++++++++++++++ include/subdir.am | 1 + zebra/ge_netlink.c | 149 +++++++++++++++++++++++++++++++ zebra/ge_netlink.h | 2 + zebra/if_netlink.c | 73 +-------------- 5 files changed, 306 insertions(+), 72 deletions(-) create mode 100644 include/linux/ethtool_netlink.h diff --git a/include/linux/ethtool_netlink.h b/include/linux/ethtool_netlink.h new file mode 100644 index 000000000000..4f1d0234d60c --- /dev/null +++ b/include/linux/ethtool_netlink.h @@ -0,0 +1,153 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ +/* + * include/uapi/linux/ethtool_netlink.h - netlink interface for ethtool + * + * See Documentation/networking/ethtool-netlink.rst in kernel source tree for + * documentation of the interface. + */ + +#ifndef _LINUX_ETHTOOL_NETLINK_H_ +#define _LINUX_ETHTOOL_NETLINK_H_ + +/* message types - userspace to kernel */ +enum { + ETHTOOL_MSG_USER_NONE, + ETHTOOL_MSG_STRSET_GET, + ETHTOOL_MSG_LINKINFO_GET, + ETHTOOL_MSG_LINKINFO_SET, + ETHTOOL_MSG_LINKMODES_GET, + ETHTOOL_MSG_LINKMODES_SET, + ETHTOOL_MSG_LINKSTATE_GET, + ETHTOOL_MSG_DEBUG_GET, + ETHTOOL_MSG_DEBUG_SET, + ETHTOOL_MSG_WOL_GET, + ETHTOOL_MSG_WOL_SET, + ETHTOOL_MSG_FEATURES_GET, + ETHTOOL_MSG_FEATURES_SET, + ETHTOOL_MSG_PRIVFLAGS_GET, + ETHTOOL_MSG_PRIVFLAGS_SET, + ETHTOOL_MSG_RINGS_GET, + ETHTOOL_MSG_RINGS_SET, + ETHTOOL_MSG_CHANNELS_GET, + ETHTOOL_MSG_CHANNELS_SET, + ETHTOOL_MSG_COALESCE_GET, + ETHTOOL_MSG_COALESCE_SET, + ETHTOOL_MSG_PAUSE_GET, + ETHTOOL_MSG_PAUSE_SET, + ETHTOOL_MSG_EEE_GET, + ETHTOOL_MSG_EEE_SET, + ETHTOOL_MSG_TSINFO_GET, + ETHTOOL_MSG_CABLE_TEST_ACT, + ETHTOOL_MSG_CABLE_TEST_TDR_ACT, + ETHTOOL_MSG_TUNNEL_INFO_GET, + ETHTOOL_MSG_FEC_GET, + ETHTOOL_MSG_FEC_SET, + ETHTOOL_MSG_MODULE_EEPROM_GET, + ETHTOOL_MSG_STATS_GET, + ETHTOOL_MSG_PHC_VCLOCKS_GET, + ETHTOOL_MSG_MODULE_GET, + ETHTOOL_MSG_MODULE_SET, + ETHTOOL_MSG_PSE_GET, + ETHTOOL_MSG_PSE_SET, + ETHTOOL_MSG_RSS_GET, + ETHTOOL_MSG_PLCA_GET_CFG, + ETHTOOL_MSG_PLCA_SET_CFG, + ETHTOOL_MSG_PLCA_GET_STATUS, + ETHTOOL_MSG_MM_GET, + ETHTOOL_MSG_MM_SET, + + /* add new constants above here */ + __ETHTOOL_MSG_USER_CNT, + ETHTOOL_MSG_USER_MAX = __ETHTOOL_MSG_USER_CNT - 1 +}; + +/* message types - kernel to userspace */ +enum { + ETHTOOL_MSG_KERNEL_NONE, + ETHTOOL_MSG_STRSET_GET_REPLY, + ETHTOOL_MSG_LINKINFO_GET_REPLY, + ETHTOOL_MSG_LINKINFO_NTF, + ETHTOOL_MSG_LINKMODES_GET_REPLY, + ETHTOOL_MSG_LINKMODES_NTF, + ETHTOOL_MSG_LINKSTATE_GET_REPLY, + ETHTOOL_MSG_DEBUG_GET_REPLY, + ETHTOOL_MSG_DEBUG_NTF, + ETHTOOL_MSG_WOL_GET_REPLY, + ETHTOOL_MSG_WOL_NTF, + ETHTOOL_MSG_FEATURES_GET_REPLY, + ETHTOOL_MSG_FEATURES_SET_REPLY, + ETHTOOL_MSG_FEATURES_NTF, + ETHTOOL_MSG_PRIVFLAGS_GET_REPLY, + ETHTOOL_MSG_PRIVFLAGS_NTF, + ETHTOOL_MSG_RINGS_GET_REPLY, + ETHTOOL_MSG_RINGS_NTF, + ETHTOOL_MSG_CHANNELS_GET_REPLY, + ETHTOOL_MSG_CHANNELS_NTF, + ETHTOOL_MSG_COALESCE_GET_REPLY, + ETHTOOL_MSG_COALESCE_NTF, + ETHTOOL_MSG_PAUSE_GET_REPLY, + ETHTOOL_MSG_PAUSE_NTF, + ETHTOOL_MSG_EEE_GET_REPLY, + ETHTOOL_MSG_EEE_NTF, + ETHTOOL_MSG_TSINFO_GET_REPLY, + ETHTOOL_MSG_CABLE_TEST_NTF, + ETHTOOL_MSG_CABLE_TEST_TDR_NTF, + ETHTOOL_MSG_TUNNEL_INFO_GET_REPLY, + ETHTOOL_MSG_FEC_GET_REPLY, + ETHTOOL_MSG_FEC_NTF, + ETHTOOL_MSG_MODULE_EEPROM_GET_REPLY, + ETHTOOL_MSG_STATS_GET_REPLY, + ETHTOOL_MSG_PHC_VCLOCKS_GET_REPLY, + ETHTOOL_MSG_MODULE_GET_REPLY, + ETHTOOL_MSG_MODULE_NTF, + ETHTOOL_MSG_PSE_GET_REPLY, + ETHTOOL_MSG_RSS_GET_REPLY, + ETHTOOL_MSG_PLCA_GET_CFG_REPLY, + ETHTOOL_MSG_PLCA_GET_STATUS_REPLY, + ETHTOOL_MSG_PLCA_NTF, + ETHTOOL_MSG_MM_GET_REPLY, + ETHTOOL_MSG_MM_NTF, + + /* add new constants above here */ + __ETHTOOL_MSG_KERNEL_CNT, + ETHTOOL_MSG_KERNEL_MAX = __ETHTOOL_MSG_KERNEL_CNT - 1 +}; + +/* request header */ + +enum { + ETHTOOL_A_HEADER_UNSPEC, + ETHTOOL_A_HEADER_DEV_INDEX, /* u32 */ + ETHTOOL_A_HEADER_DEV_NAME, /* string */ + ETHTOOL_A_HEADER_FLAGS, /* u32 - ETHTOOL_FLAG_* */ + + /* add new constants above here */ + __ETHTOOL_A_HEADER_CNT, + ETHTOOL_A_HEADER_MAX = __ETHTOOL_A_HEADER_CNT - 1 +}; + +/* LINKMODES */ + +enum { + ETHTOOL_A_LINKMODES_UNSPEC, + ETHTOOL_A_LINKMODES_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_LINKMODES_AUTONEG, /* u8 */ + ETHTOOL_A_LINKMODES_OURS, /* bitset */ + ETHTOOL_A_LINKMODES_PEER, /* bitset */ + ETHTOOL_A_LINKMODES_SPEED, /* u32 */ + ETHTOOL_A_LINKMODES_DUPLEX, /* u8 */ + ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG, /* u8 */ + ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE, /* u8 */ + ETHTOOL_A_LINKMODES_LANES, /* u32 */ + ETHTOOL_A_LINKMODES_RATE_MATCHING, /* u8 */ + + /* add new constants above here */ + __ETHTOOL_A_LINKMODES_CNT, + ETHTOOL_A_LINKMODES_MAX = __ETHTOOL_A_LINKMODES_CNT - 1 +}; + +/* generic netlink info */ +#define ETHTOOL_GENL_NAME "ethtool" +#define ETHTOOL_GENL_VERSION 1 + +#endif /* _LINUX_ETHTOOL_NETLINK_H_ */ diff --git a/include/subdir.am b/include/subdir.am index bcf3ef7b9bf8..6988e9c3b249 100644 --- a/include/subdir.am +++ b/include/subdir.am @@ -1,6 +1,7 @@ noinst_HEADERS += \ include/linux/compiler_types.h \ include/linux/libc-compat.h \ + include/linux/ethtool_netlink.h \ include/linux/if.h \ include/linux/if_addr.h \ include/linux/if_bridge.h \ diff --git a/zebra/ge_netlink.c b/zebra/ge_netlink.c index 364a0244e1cb..3fb370229ec2 100644 --- a/zebra/ge_netlink.c +++ b/zebra/ge_netlink.c @@ -18,15 +18,18 @@ #include #include #include +#include #include "lib/ns.h" #include "zebra/ge_netlink.h" #include "zebra/debug.h" +#include "zebra/interface.h" #include "zebra/zebra_errors.h" #include "zebra/kernel_netlink.h" #include "zebra/zebra_router.h" #include "zebra/zebra_srv6.h" #include "zebra/zebra_dplane.h" +#include "zebra/zebra_trace.h" #include "lib/netlink_parser.h" #include "lib/lib_errors.h" @@ -55,6 +58,11 @@ */ static int16_t seg6_genl_family = -1; +/* + * Numeric family identifier used to query ethtool information through Generic Netlink. + */ +static int16_t ethtool_genl_family = -1; + static int genl_parse_getfamily(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *arg) { int len; @@ -101,6 +109,8 @@ static int genl_parse_getfamily(struct nlmsghdr *h, ns_id_t ns_id, int startup, if (strmatch(family, "SEG6")) seg6_genl_family = *(int16_t *)RTA_DATA(tb[CTRL_ATTR_FAMILY_ID]); + else if (strmatch(family, ETHTOOL_GENL_NAME)) + ethtool_genl_family = *(int16_t *)RTA_DATA(tb[CTRL_ATTR_FAMILY_ID]); else { if (IS_ZEBRA_DEBUG_KERNEL) flog_err(EC_ZEBRA_NETLINK_INVALID_AF, @@ -348,6 +358,136 @@ int netlink_sr_tunsrc_read(struct zebra_ns *zns) return 0; } +struct ethtool_speed_result { + uint32_t speed; + bool speed_set; + int nl_err; /* kernel-reported errno from NLMSG_ERROR, 0 if none */ +}; + +static int netlink_ethtool_linkmodes_reply_read(struct nlmsghdr *h, ns_id_t ns_id, int startup, + void *arg) +{ + struct ethtool_speed_result *res = arg; + int len; + struct genlmsghdr *ghdr; + struct rtattr *tb[ETHTOOL_A_LINKMODES_MAX + 1] = {}; + struct rtattr *attrs; + + res->speed_set = false; + + if (h->nlmsg_type != ethtool_genl_family) + return 0; + + len = h->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN); + if (len < 0) { + zlog_warn("%s: Message received from netlink is of a broken size %d %zu", __func__, + h->nlmsg_len, (size_t)NLMSG_LENGTH(GENL_HDRLEN)); + return -1; + } + + ghdr = NLMSG_DATA(h); + + if (ghdr->cmd != ETHTOOL_MSG_LINKMODES_GET_REPLY) + return 0; + + attrs = (struct rtattr *)((char *)ghdr + GENL_HDRLEN); + netlink_parse_rtattr(tb, ETHTOOL_A_LINKMODES_MAX, attrs, len); + + if (tb[ETHTOOL_A_LINKMODES_SPEED] == NULL) + return 0; + + res->speed = *(uint32_t *)RTA_DATA(tb[ETHTOOL_A_LINKMODES_SPEED]); + res->speed_set = true; + + return 0; +} + +/** + * netlink_get_interface_speed() - Query interface speed via ethtool over netlink + * @zns: Per-namespace netlink sockets holder (uses zns->ge_netlink_cmd) + * @ifname: Interface name + * @error: Out param, set to 0 on success, INTERFACE_SPEED_ERROR_* otherwise + * + * Sends ETHTOOL_MSG_LINKMODES_GET on the per-ns generic netlink command socket + * and parses the reply. The socket is pinned to the right namespace at zns + * creation time, so no setns is required from the caller. + * + * Returns the link speed in Mbps, or 0 with *error set on failure. + */ +uint32_t netlink_get_interface_speed(struct zebra_ns *zns, const char *ifname, int *error) +{ + struct ethtool_speed_result res = {}; + struct genl_request req = {}; + struct rtattr *header; + int ret; + + if (error) + *error = 0; + + if (zns == NULL || zns->ge_netlink_cmd.sock < 0) { + if (error) + *error = INTERFACE_SPEED_ERROR_READ; + return 0; + } + + if (ethtool_genl_family < 0) { + /* Kernel < 5.5 or ethtool family not registered: permanent, + * retrying will not help. + */ + if (error) + *error = INTERFACE_SPEED_ERROR_READ; + return 0; + } + + req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = ethtool_genl_family; + req.n.nlmsg_pid = zns->ge_netlink_cmd.snl.nl_pid; + + req.g.cmd = ETHTOOL_MSG_LINKMODES_GET; + req.g.version = ETHTOOL_GENL_VERSION; + + header = nl_attr_nest(&req.n, sizeof(req), ETHTOOL_A_LINKMODES_HEADER); + if (!header) { + if (error) + *error = INTERFACE_SPEED_ERROR_READ; + return 0; + } + if (!nl_attr_put(&req.n, sizeof(req), ETHTOOL_A_HEADER_DEV_NAME, ifname, + strlen(ifname) + 1)) { + if (error) + *error = INTERFACE_SPEED_ERROR_READ; + return 0; + } + nl_attr_nest_end(&req.n, header); + + ret = ge_netlink_talk(netlink_ethtool_linkmodes_reply_read, &req.n, zns, false, &res, + &res.nl_err); + if (ret < 0) { + int nl_errno = res.nl_err ? -res.nl_err : errno; + + if (error) + *error = (res.nl_err == -ENODEV || + res.nl_err == -EOPNOTSUPP) + ? INTERFACE_SPEED_ERROR_READ + : INTERFACE_SPEED_ERROR_UNKNOWN; + + if (nl_errno != EOPNOTSUPP) + frrtrace(4, frr_zebra, get_iflink_speed, ifname, + nl_errno, safe_strerror(nl_errno), 1); + + return 0; + } + + if (!res.speed_set || res.speed == UINT32_MAX) { + if (error) + *error = INTERFACE_SPEED_ERROR_UNKNOWN; + return 0; + } + + return res.speed; +} + void ge_netlink_init(struct zebra_ns *zns) { if (zns->ge_netlink_cmd.sock < 0) @@ -363,6 +503,15 @@ void ge_netlink_init(struct zebra_ns *zns) zlog_warn( "Kernel does not support 'SEG6' Generic Netlink family. Any attempt to set the encapsulation parameters under the SRv6 configuration will fail"); + /* + * Resolve the 'ethtool' Generic Netlink family. Available since Linux + * 5.5. On older kernels netlink_get_interface_speed() will report the + * speed as unknown. + */ + if (genl_resolve_family(ETHTOOL_GENL_NAME)) + zlog_warn("Kernel does not support '%s' Generic Netlink family; interface speed will be reported as unknown", + ETHTOOL_GENL_NAME); + /** * Retrieve the actual SRv6 encap source address from the kernel * (default namespace) and save it to zebra SRv6 config diff --git a/zebra/ge_netlink.h b/zebra/ge_netlink.h index c6ec2d1cb3f4..3bfa83cededd 100644 --- a/zebra/ge_netlink.h +++ b/zebra/ge_netlink.h @@ -45,6 +45,8 @@ netlink_put_sr_tunsrc_set_msg(struct nl_batch *bth, int netlink_sr_tunsrc_reply_read(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *arg); int netlink_sr_tunsrc_read(struct zebra_ns *zns); +extern uint32_t netlink_get_interface_speed(struct zebra_ns *zns, const char *ifname, int *error); + extern void ge_netlink_init(struct zebra_ns *zns); #ifdef __cplusplus diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index 7944a00ba673..88c583418f62 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -269,80 +269,9 @@ static void netlink_vrf_change(struct nlmsghdr *h, struct rtattr *tb, ctx, *(uint32_t *)RTA_DATA(attr[IFLA_VRF_TABLE])); } -static uint32_t get_iflink_speed(struct interface *interface, int *error) -{ - struct ifreq ifdata; - struct ethtool_cmd ecmd; - int sd; - int rc; - const char *ifname = interface->name; - uint32_t ret; - - if (error) - *error = 0; - /* initialize struct */ - memset(&ifdata, 0, sizeof(ifdata)); - - /* set interface name */ - strlcpy(ifdata.ifr_name, ifname, sizeof(ifdata.ifr_name)); - - /* initialize ethtool interface */ - memset(&ecmd, 0, sizeof(ecmd)); - ecmd.cmd = ETHTOOL_GSET; /* ETHTOOL_GLINK */ - ifdata.ifr_data = (caddr_t)&ecmd; - - /* use ioctl to get speed of an interface */ - frr_with_privs(&zserv_privs) { - sd = vrf_socket(PF_INET, SOCK_DGRAM, IPPROTO_IP, - interface->vrf->vrf_id, NULL); - if (sd < 0) { - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("Failure to read interface %s speed: %d %s", - ifname, errno, safe_strerror(errno)); - /* no vrf socket creation may probably mean vrf issue */ - if (error) - *error = INTERFACE_SPEED_ERROR_READ; - - frrtrace(4, frr_zebra, get_iflink_speed, ifname, errno, - safe_strerror(errno), 1); - - return 0; - } - /* Get the current link state for the interface */ - rc = vrf_ioctl(interface->vrf->vrf_id, sd, SIOCETHTOOL, - (char *)&ifdata); - } - if (rc < 0) { - if (errno != EOPNOTSUPP && IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "IOCTL failure to read interface %s speed: %d %s", - ifname, errno, safe_strerror(errno)); - /* no device means interface unreachable */ - if (errno == ENODEV && error) - *error = INTERFACE_SPEED_ERROR_READ; - - if (errno != EOPNOTSUPP) - frrtrace(4, frr_zebra, get_iflink_speed, ifname, errno, - safe_strerror(errno), 2); - - ecmd.speed_hi = 0; - ecmd.speed = 0; - } - - close(sd); - - ret = ((uint32_t)ecmd.speed_hi << 16) | ecmd.speed; - if (ret == UINT32_MAX) { - if (error) - *error = INTERFACE_SPEED_ERROR_UNKNOWN; - ret = 0; - } - return ret; -} - uint32_t kernel_get_speed(struct interface *ifp, int *error) { - return get_iflink_speed(ifp, error); + return netlink_get_interface_speed(zebra_ns_lookup(ifp->vrf->vrf_id), ifp->name, error); } static ssize_t From aeb72c6404f1117ba8378280f1ada332e4a045af Mon Sep 17 00:00:00 2001 From: Maxime Leroy Date: Wed, 6 May 2026 20:42:23 +0200 Subject: [PATCH 5/6] zebra: get interface speed from dataplane via ethtool This introduces DPLANE_OP_INTF_SPEED_GET so link speed is resolved in the dataplane via ethtool and reported to zebra. Zebra no longer performs synchronous speed reads; it simply applies the value provided by the dataplane. If speed is already known during interface creation or modification, it can be included in INTF_INSTALL/INTF_UPDATE and zebra will use it directly. If speed is not provided, the zebra main thread issues a follow-up INTF_SPEED_GET to request the dataplane to fetch the speed asynchronously. For dataplane providers that implement only INTF_INSTALL/INTF_UPDATE and do not support INTF_SPEED_GET, zebra relies on any speed value provided by install/update. If speed is missing, zebra attempts a single INTF_SPEED_GET query and stops if the operation is unsupported or fails. Signed-off-by: Maxime Leroy --- zebra/dplane_fpm_nl.c | 1 + zebra/if_netlink.c | 52 +++++++++++++++++++++++++++++---- zebra/interface.c | 66 +++++++++++++++++++++++++++--------------- zebra/kernel_netlink.c | 1 + zebra/kernel_socket.c | 1 + zebra/rt.h | 2 +- zebra/rt_socket.c | 5 ++-- zebra/zebra_dplane.c | 66 ++++++++++++++++++++++++++++++++++++++++++ zebra/zebra_dplane.h | 2 ++ zebra/zebra_rib.c | 1 + zebra/zebra_script.c | 1 + 11 files changed, 165 insertions(+), 33 deletions(-) diff --git a/zebra/dplane_fpm_nl.c b/zebra/dplane_fpm_nl.c index 2fa061f105ea..8e120837097f 100644 --- a/zebra/dplane_fpm_nl.c +++ b/zebra/dplane_fpm_nl.c @@ -1102,6 +1102,7 @@ static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx) case DPLANE_OP_INTF_INSTALL: case DPLANE_OP_INTF_UPDATE: case DPLANE_OP_INTF_DELETE: + case DPLANE_OP_INTF_SPEED_GET: case DPLANE_OP_TC_QDISC_INSTALL: case DPLANE_OP_TC_QDISC_UNINSTALL: case DPLANE_OP_TC_CLASS_ADD: diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index 88c583418f62..2e169905bdb6 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -269,9 +269,36 @@ static void netlink_vrf_change(struct nlmsghdr *h, struct rtattr *tb, ctx, *(uint32_t *)RTA_DATA(attr[IFLA_VRF_TABLE])); } -uint32_t kernel_get_speed(struct interface *ifp, int *error) +void kernel_read_intf_speed(struct zebra_dplane_ctx *ctx) { - return netlink_get_interface_speed(zebra_ns_lookup(ifp->vrf->vrf_id), ifp->name, error); + const char *ifname = dplane_ctx_get_ifname(ctx); + struct zebra_ns *zns = zebra_ns_lookup(dplane_ctx_get_ns_id(ctx)); + uint32_t speed; + int error = 0; + + speed = netlink_get_interface_speed(zns, ifname, &error); + switch (error) { + case 0: + dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS); + dplane_ctx_set_ifp_speed(ctx, speed); + dplane_ctx_set_ifp_speed_set(ctx, true); + break; + case INTERFACE_SPEED_ERROR_UNKNOWN: + dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS); + dplane_ctx_set_ifp_speed_set(ctx, false); + break; + case INTERFACE_SPEED_ERROR_READ: + dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_FAILURE); + dplane_ctx_set_ifp_speed_set(ctx, false); + break; + default: + dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_FAILURE); + dplane_ctx_set_ifp_speed_set(ctx, false); + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("netlink_get_interface_speed returned an unknown error %d", + error); + break; + } } static ssize_t @@ -1315,6 +1342,8 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *ar uint8_t bypass = 0; uint32_t txqlen = 0; uint32_t cchanges = 0; + int speed_err = 0; + uint32_t speed = 0; frrtrace(3, frr_zebra, netlink_interface, h, ns_id, startup); @@ -1475,8 +1504,22 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *ar } else zif_slave_type = ZEBRA_IF_SLAVE_OTHER; } + + vrf_id_t ifp_vrf_id = vrf_is_backend_netns() ? ns_id : vrf_id; + + if (startup) { + speed = netlink_get_interface_speed(zebra_ns_lookup(ifp_vrf_id), + name, &speed_err); + if (speed_err == 0) { + dplane_ctx_set_ifp_speed(ctx, speed); + dplane_ctx_set_ifp_speed_set(ctx, true); + } else + dplane_ctx_set_ifp_speed_set(ctx, false); + } else + dplane_ctx_set_ifp_speed_set(ctx, false); + dplane_ctx_set_ifp_zif_slave_type(ctx, zif_slave_type); - dplane_ctx_set_ifp_vrf_id(ctx, vrf_id); + dplane_ctx_set_ifp_vrf_id(ctx, ifp_vrf_id); dplane_ctx_set_ifp_master_ifindex(ctx, master_infindex); dplane_ctx_set_ifp_bridge_ifindex(ctx, bridge_ifindex); dplane_ctx_set_ifp_bond_ifindex(ctx, bond_ifindex); @@ -1484,9 +1527,6 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup, void *ar dplane_ctx_set_ifp_zltype( ctx, netlink_to_zebra_link_type(ifi->ifi_type)); - if (vrf_is_backend_netns()) - dplane_ctx_set_ifp_vrf_id(ctx, ns_id); - dplane_ctx_set_ifp_flags(ctx, ifi->ifi_flags & 0x0000fffff); dplane_ctx_set_ifp_change_flags(ctx, ifi->ifi_change & 0x0000fffff); diff --git a/zebra/interface.c b/zebra/interface.c index 40d19cdd9fab..0bc5ae796ad0 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -66,22 +66,40 @@ static const char *if_zebra_data_state(uint8_t state) static void if_zebra_speed_update(struct event *event) { struct interface *ifp = EVENT_ARG(event); - struct zebra_if *zif = ifp->info; + + dplane_intf_speed_get(ifp); +} + +static void zebra_if_schedule_speed_update(struct zebra_if *zif, int timeout) +{ + event_add_timer(zrouter.master, if_zebra_speed_update, zif->ifp, timeout, + &zif->speed_update); + event_ignore_late_timer(zif->speed_update); +} + +static void zebra_if_speed_update_ctx(struct zebra_dplane_ctx *ctx, struct interface *ifp) +{ + enum zebra_dplane_result dp_res; + bool speed_set, changed = false; + struct zebra_if *zif; uint32_t new_speed; - bool changed = false; - int error = 0; - zif->speed_checked++; + if (!ifp) + return; - new_speed = kernel_get_speed(ifp, &error); + zif = ifp->info; + zif->speed_checked++; + dp_res = dplane_ctx_get_status(ctx); /* error may indicate vrf not available or * interfaces not available. * note that loopback & virtual interfaces can return 0 as speed */ - if (error == INTERFACE_SPEED_ERROR_READ) + if (dp_res == ZEBRA_DPLANE_REQUEST_FAILURE) return; + speed_set = dplane_ctx_get_ifp_speed_set(ctx); + new_speed = speed_set ? dplane_ctx_get_ifp_speed(ctx) : 0; if (new_speed != ifp->speed) { zlog_info("%s: %s old speed: %u new speed: %u", __func__, ifp->name, ifp->speed, new_speed); @@ -90,7 +108,7 @@ static void if_zebra_speed_update(struct event *event) changed = true; } - if (changed || error == INTERFACE_SPEED_ERROR_UNKNOWN) { + if (changed || !speed_set) { #define SPEED_UPDATE_SLEEP_TIME 5 #define SPEED_UPDATE_COUNT_MAX (4 * 60 / SPEED_UPDATE_SLEEP_TIME) /* @@ -105,14 +123,11 @@ static void if_zebra_speed_update(struct event *event) * to not update the system to keep track of that. This * is far simpler to just stop trying after 4 minutes */ - if (error == INTERFACE_SPEED_ERROR_UNKNOWN && - zif->speed_update_count == SPEED_UPDATE_COUNT_MAX) + if (!speed_set && zif->speed_update_count == SPEED_UPDATE_COUNT_MAX) return; zif->speed_update_count++; - event_add_timer(zrouter.master, if_zebra_speed_update, ifp, - SPEED_UPDATE_SLEEP_TIME, &zif->speed_update); - event_ignore_late_timer(zif->speed_update); + zebra_if_schedule_speed_update(zif, SPEED_UPDATE_SLEEP_TIME); } } @@ -170,9 +185,7 @@ static int if_zebra_new_hook(struct interface *ifp) */ zebra_if->speed_update_count = 0; zebra_if->speed_checked = 0; - event_add_timer(zrouter.master, if_zebra_speed_update, ifp, 15, - &zebra_if->speed_update); - event_ignore_late_timer(zebra_if->speed_update); + zebra_if_schedule_speed_update(zebra_if, 15); return 0; } @@ -950,9 +963,7 @@ static void if_handle_bond_speed_change(struct interface *ifp) if (part_of_bond->bond_if) { zif = part_of_bond->bond_if->info; - if (!event_is_scheduled(zif->speed_update)) - event_add_timer(zrouter.master, if_zebra_speed_update, part_of_bond->bond_if, 1, - &zif->speed_update); + zebra_if_schedule_speed_update(zif, 1); } } @@ -1014,9 +1025,8 @@ void if_up(struct interface *ifp, bool install_connected) if (zif->flags & ZIF_FLAG_EVPN_MH_UPLINK) zebra_evpn_mh_uplink_oper_update(zif); - event_add_timer(zrouter.master, if_zebra_speed_update, ifp, 0, - &zif->speed_update); - event_ignore_late_timer(zif->speed_update); + event_cancel(&zif->speed_update); + dplane_intf_speed_get(ifp); if_addr_wakeup(ifp); @@ -2109,8 +2119,12 @@ static void zebra_if_dplane_ifp_handling(struct zebra_dplane_ctx *ctx) if_update_state_mtu(ifp, mtu); if_update_state_mtu6(ifp, mtu); if_update_state_metric(ifp, 0); - if (!speed_set) - speed = kernel_get_speed(ifp, NULL); + if (!speed_set) { + speed = 0; + /* Query initial speed if not provided by dplane */ + dplane_intf_speed_get(ifp); + } else + zif->speed_checked++; if_update_state_speed(ifp, speed); ifp->ptm_status = ZEBRA_PTM_STATUS_UNKNOWN; ifp->txqlen = dplane_ctx_get_intf_txqlen(ctx); @@ -2215,8 +2229,10 @@ static void zebra_if_dplane_ifp_handling(struct zebra_dplane_ctx *ctx) if_update_state_mtu(ifp, mtu); if_update_state_mtu6(ifp, mtu); if_update_state_metric(ifp, 0); - if (speed_set) + if (speed_set) { + zif->speed_checked++; if_update_state_speed(ifp, speed); + } ifp->txqlen = dplane_ctx_get_intf_txqlen(ctx); /* @@ -2426,6 +2442,8 @@ void zebra_if_dplane_result(struct zebra_dplane_ctx *ctx) zebra_if_update_ctx(ctx, ifp); } else if (op == DPLANE_OP_INTF_NETCONFIG) { zebra_if_netconf_update_ctx(ctx, ifp, ifindex); + } else if (op == DPLANE_OP_INTF_SPEED_GET) { + zebra_if_speed_update_ctx(ctx, ifp); } } diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index cac8f92d9324..563fb6c4abb0 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -1477,6 +1477,7 @@ static enum netlink_msg_status nl_put_msg(struct nl_batch *bth, case DPLANE_OP_BR_PORT_UPDATE: return FRR_NETLINK_SUCCESS; + case DPLANE_OP_INTF_SPEED_GET: case DPLANE_OP_IPTABLE_ADD: case DPLANE_OP_IPTABLE_DELETE: case DPLANE_OP_IPSET_ADD: diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index 80b5a3828f1d..5d508ce50f53 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -1692,6 +1692,7 @@ void kernel_update_multi(struct dplane_ctx_list_head *ctx_list) case DPLANE_OP_GRE_SET: case DPLANE_OP_INTF_ADDR_ADD: case DPLANE_OP_INTF_ADDR_DEL: + case DPLANE_OP_INTF_SPEED_GET: case DPLANE_OP_STARTUP_STAGE: case DPLANE_OP_SRV6_ENCAP_SRCADDR_SET: case DPLANE_OP_VLAN_INSTALL: diff --git a/zebra/rt.h b/zebra/rt.h index f8feb1ea1a54..6eaeba04400c 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -77,7 +77,6 @@ extern int mpls_kernel_init(void); void kernel_router_init(void); void kernel_router_terminate(void); -extern uint32_t kernel_get_speed(struct interface *ifp, int *error); extern int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *mroute); /* @@ -92,6 +91,7 @@ extern void kernel_terminate(struct zebra_ns *zns, bool complete); extern void kernel_read_macfdb(struct zebra_dplane_ctx *ctx); extern void kernel_read_neigh(struct zebra_dplane_ctx *ctx); extern void kernel_read_tc_qdisc(struct zebra_dplane_ctx *ctx); +extern void kernel_read_intf_speed(struct zebra_dplane_ctx *ctx); extern void route_read(struct zebra_ns *zns); extern int kernel_upd_mac_nh(uint32_t nh_id, struct ipaddr *vtep_ip); extern int kernel_del_mac_nh(uint32_t nh_id); diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index 3f7b052e8eb9..591cdcd67a50 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -383,9 +383,10 @@ extern int kernel_interface_set_master(struct interface *master, return 0; } -uint32_t kernel_get_speed(struct interface *ifp, int *error) +void kernel_read_intf_speed(struct zebra_dplane_ctx *ctx) { - return ifp->speed; + dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_FAILURE); + dplane_ctx_set_ifp_speed_set(ctx, false); } int kernel_upd_mac_nh(uint32_t nh_id, struct ipaddr *vtep_ip) diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 3204d6d5a6f6..d301e45c5b16 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -691,6 +691,9 @@ static struct zebra_dplane_globals { _Atomic uint32_t dg_intfs_in; _Atomic uint32_t dg_intf_errors; + _Atomic uint32_t dg_intf_speed_get_in; + _Atomic uint32_t dg_intf_speed_get_errors; + _Atomic uint32_t dg_tcs_in; _Atomic uint32_t dg_tcs_errors; @@ -969,6 +972,7 @@ static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx) break; case DPLANE_OP_GRE_SET: case DPLANE_OP_INTF_NETCONFIG: + case DPLANE_OP_INTF_SPEED_GET: case DPLANE_OP_STARTUP_STAGE: case DPLANE_OP_SRV6_ENCAP_SRCADDR_SET: break; @@ -1258,6 +1262,9 @@ const char *dplane_op2str(enum dplane_op_e op) case DPLANE_OP_INTF_DELETE: return "INTF_DELETE"; + case DPLANE_OP_INTF_SPEED_GET: + return "INTF_SPEED_GET"; + case DPLANE_OP_TC_QDISC_INSTALL: return "TC_QDISC_INSTALL"; case DPLANE_OP_TC_QDISC_UNINSTALL: @@ -5706,6 +5713,45 @@ enum zebra_dplane_result dplane_intf_update(const struct interface *ifp) return ret; } +/* + * Enqueue a interface speed query for the dataplane. + */ +enum zebra_dplane_result dplane_intf_speed_get(const struct interface *ifp) +{ + enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; + struct zebra_dplane_ctx *ctx = NULL; + struct zebra_ns *zns; + int ret; + + ctx = dplane_ctx_alloc(); + + ctx->zd_op = DPLANE_OP_INTF_SPEED_GET; + ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS; + ctx->zd_vrf_id = ifp->vrf->vrf_id; + + strlcpy(ctx->zd_ifname, ifp->name, sizeof(ctx->zd_ifname)); + ctx->zd_ifindex = ifp->ifindex; + + zns = zebra_ns_lookup(ifp->vrf->vrf_id); + dplane_ctx_ns_init(ctx, zns, false); + + ret = dplane_update_enqueue(ctx); + + /* Increment counter */ + atomic_fetch_add_explicit(&zdplane_info.dg_intf_speed_get_in, 1, memory_order_relaxed); + + if (ret == AOK) + result = ZEBRA_DPLANE_REQUEST_QUEUED; + else { + atomic_fetch_add_explicit(&zdplane_info.dg_intf_speed_get_errors, 1, + memory_order_relaxed); + if (ctx) + dplane_ctx_free(&ctx); + } + + return result; +} + /* * Enqueue vxlan/evpn mac add (or update). */ @@ -6716,6 +6762,11 @@ int dplane_show_helper(struct vty *vty, bool detailed) vty_out(vty, "Intf change updates: %" PRIu64 "\n", incoming); vty_out(vty, "Intf change errors: %" PRIu64 "\n", errs); + incoming = atomic_load_explicit(&zdplane_info.dg_intf_speed_get_in, memory_order_relaxed); + errs = atomic_load_explicit(&zdplane_info.dg_intf_speed_get_errors, memory_order_relaxed); + vty_out(vty, "Intf speed query: %" PRIu64 "\n", incoming); + vty_out(vty, "Intf speed errors: %" PRIu64 "\n", errs); + incoming = atomic_load_explicit(&zdplane_info.dg_macs_in, memory_order_relaxed); errs = atomic_load_explicit(&zdplane_info.dg_mac_errors, @@ -7359,6 +7410,10 @@ static void kernel_dplane_log_detail(struct zebra_dplane_ctx *ctx) dplane_ctx_get_ifindex(ctx), dplane_ctx_intf_is_protodown(ctx)); break; + case DPLANE_OP_INTF_SPEED_GET: + zlog_debug("Dplane intf %s, idx %u", dplane_op2str(dplane_ctx_get_op(ctx)), + dplane_ctx_get_ifindex(ctx)); + break; /* TODO: more detailed log */ case DPLANE_OP_TC_QDISC_INSTALL: @@ -7578,6 +7633,7 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx) case DPLANE_OP_INTF_ADDR_ADD: case DPLANE_OP_INTF_ADDR_DEL: case DPLANE_OP_INTF_NETCONFIG: + case DPLANE_OP_INTF_SPEED_GET: case DPLANE_OP_VLAN_INSTALL: break; @@ -7646,6 +7702,14 @@ static void kernel_dplane_process_tc_qdisc_read(struct zebra_dplane_provider *pr dplane_provider_enqueue_out_ctx(prov, ctx); } +/* Runs in the dplane pthread. */ +static void kernel_dplane_process_if_speed(struct zebra_dplane_provider *prov, + struct zebra_dplane_ctx *ctx) +{ + kernel_read_intf_speed(ctx); + dplane_provider_enqueue_out_ctx(prov, ctx); +} + /* * Kernel provider callback */ @@ -7710,6 +7774,8 @@ static int kernel_dplane_process_func(struct zebra_dplane_provider *prov) kernel_dplane_process_neigh_read(prov, ctx); else if (dplane_ctx_get_op(ctx) == DPLANE_OP_TC_QDISC_READ) kernel_dplane_process_tc_qdisc_read(prov, ctx); + else if (dplane_ctx_get_op(ctx) == DPLANE_OP_INTF_SPEED_GET) + kernel_dplane_process_if_speed(prov, ctx); else dplane_ctx_list_add_tail(&work_list, ctx); } diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index c4abdc2d1084..b1585065a225 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -193,6 +193,7 @@ enum dplane_op_e { /* Incoming interface config events */ DPLANE_OP_INTF_NETCONFIG, + DPLANE_OP_INTF_SPEED_GET, /* Interface update */ DPLANE_OP_INTF_INSTALL, @@ -1020,6 +1021,7 @@ enum zebra_dplane_result dplane_intf_addr_unset(const struct interface *ifp, */ enum zebra_dplane_result dplane_intf_add(const struct interface *ifp); enum zebra_dplane_result dplane_intf_update(const struct interface *ifp); +enum zebra_dplane_result dplane_intf_speed_get(const struct interface *ifp); /* * Enqueue tc link changes for the dataplane. diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 64d8f154ab16..da7a0ae80896 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -5279,6 +5279,7 @@ static void rib_process_dplane_results(struct event *event) case DPLANE_OP_INTF_UPDATE: case DPLANE_OP_INTF_DELETE: case DPLANE_OP_INTF_NETCONFIG: + case DPLANE_OP_INTF_SPEED_GET: zebra_if_dplane_result(ctx); break; diff --git a/zebra/zebra_script.c b/zebra/zebra_script.c index a3e92d18bc82..08fa3c7f925d 100644 --- a/zebra/zebra_script.c +++ b/zebra/zebra_script.c @@ -424,6 +424,7 @@ void lua_pushzebra_dplane_ctx(lua_State *L, const struct zebra_dplane_ctx *ctx) case DPLANE_OP_TC_FILTER_UPDATE: /* Not currently handled */ case DPLANE_OP_INTF_NETCONFIG: /*NYI*/ + case DPLANE_OP_INTF_SPEED_GET: case DPLANE_OP_SRV6_ENCAP_SRCADDR_SET: case DPLANE_OP_NONE: case DPLANE_OP_STARTUP_STAGE: From 47b5dba5ad3c52c8a7620acf9e36614085e198c6 Mon Sep 17 00:00:00 2001 From: Maxime Leroy Date: Fri, 12 Dec 2025 15:28:23 +0100 Subject: [PATCH 6/6] zebra: schedule speed-query timer only when the interface exists The 15-second timer used to re-query interface speed is currently scheduled in if_zebra_new_hook() for every newly created interface. However, at that point the interface may not yet exist in the OS, and in some cases it may never be created. Because of this, the speed query will usually fail (e.g. INTERFACE_SPEED_ERROR_READ) since the interface doesn't exist. There is also a race condition: even if the interface is created, the timer may run before the RTM_NEWLINK message is processed. As a result, ifp->ifp_index can remain IFINDEX_INTERNAL (0). When if_add_update() calls zebra_ns_link_ifp(), the interface tree is updated with this incorrect index. If this happens for multiple interfaces, the tree can end up with duplicate keys, eventually causing a zebra crash. A check was added to zebra_ns_link_ifp() to avoid adding an interface with an IFINDEX_INTERNAL index, but the root cause remained. This change fixes the underlying issue by scheduling the speed-update timer only when a valid RTM_NEWLINK has been received. The scheduling logic is moved from if_zebra_new_hook() to zebra_if_dplane_ifp_handling(), and runs only once the interface has a correct ifindex. Fixes: dc7b3ca ("zebra: Add one-shot thread to recheck speed") Signed-off-by: Maxime Leroy --- zebra/interface.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/zebra/interface.c b/zebra/interface.c index 0bc5ae796ad0..b4017b6923ee 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -176,17 +176,6 @@ static int if_zebra_new_hook(struct interface *ifp) ifp->info = zebra_if; - /* - * Some platforms are telling us that the interface is - * up and ready to go. When we check the speed we - * sometimes get the wrong value. Wait a couple - * of seconds and ask again. Hopefully it's all settled - * down upon startup. - */ - zebra_if->speed_update_count = 0; - zebra_if->speed_checked = 0; - zebra_if_schedule_speed_update(zebra_if, 15); - return 0; } @@ -2123,6 +2112,17 @@ static void zebra_if_dplane_ifp_handling(struct zebra_dplane_ctx *ctx) speed = 0; /* Query initial speed if not provided by dplane */ dplane_intf_speed_get(ifp); + + /* + * Some platforms are telling us that the interface is + * up and ready to go. When we check the speed we + * sometimes get the wrong value. Wait a couple + * of seconds and ask again. Hopefully it's all settled + * down upon startup. + */ + zif->speed_update_count = 0; + zif->speed_checked = 0; + zebra_if_schedule_speed_update(zif, 15); } else zif->speed_checked++; if_update_state_speed(ifp, speed);