Add the kernel-side ETHTOOL_MSG_LOOPBACK_GET,
ETHTOOL_MSG_LOOPBACK_SET, and ETHTOOL_MSG_LOOPBACK_NTF handlers using
the standard ethnl_request_ops infrastructure.
GET collects loopback entries from per-component helpers via
loopback_get_entries(). SET parses the nested entry attributes,
dispatches each to loopback_set_one(), and only sends a notification
when the state is changed.
No components are wired yet.
Signed-off-by: Björn Töpel <bjorn@kernel.org>
---
include/linux/ethtool.h | 28 +++++
net/ethtool/Makefile | 2 +-
net/ethtool/loopback.c | 246 ++++++++++++++++++++++++++++++++++++++++
net/ethtool/netlink.c | 20 ++++
net/ethtool/netlink.h | 3 +
5 files changed, 298 insertions(+), 1 deletion(-)
create mode 100644 net/ethtool/loopback.c
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index 83c375840835..b1ebfe22c355 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -846,6 +846,34 @@ void ethtool_mmsv_set_mm(struct ethtool_mmsv *mmsv, struct ethtool_mm_cfg *cfg);
void ethtool_mmsv_init(struct ethtool_mmsv *mmsv, struct net_device *dev,
const struct ethtool_mmsv_ops *ops);
+/**
+ * struct ethtool_loopback_entry - Per-component loopback configuration
+ * @id: Optional component instance identifier, 0 means not specified
+ * @supported: Bitmask of supported directions
+ * @component: Loopback component
+ * @direction: Current loopback direction, 0 means disabled
+ * @name: Subsystem-specific name for the loopback point
+ */
+struct ethtool_loopback_entry {
+ enum ethtool_loopback_component component;
+ u32 id;
+ u32 supported;
+ u32 direction;
+ char name[ETH_GSTRING_LEN];
+};
+
+#define ETHTOOL_LOOPBACK_MAX_ENTRIES 16
+
+/**
+ * struct ethtool_loopback_cfg - Loopback configuration
+ * @entries: Array of per-component loopback configurations
+ * @n_entries: Number of valid entries in the array
+ */
+struct ethtool_loopback_cfg {
+ struct ethtool_loopback_entry entries[ETHTOOL_LOOPBACK_MAX_ENTRIES];
+ u32 n_entries;
+};
+
/**
* struct ethtool_rxfh_param - RXFH (RSS) parameters
* @hfunc: Defines the current RSS hash function used by HW (or to be set to).
diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile
index 629c10916670..ef534b55d724 100644
--- a/net/ethtool/Makefile
+++ b/net/ethtool/Makefile
@@ -9,4 +9,4 @@ ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o linkmodes.o rss.o \
channels.o coalesce.o pause.o eee.o tsinfo.o cabletest.o \
tunnels.o fec.o eeprom.o stats.o phc_vclocks.o mm.o \
module.o cmis_fw_update.o cmis_cdb.o pse-pd.o plca.o \
- phy.o tsconfig.o mse.o
+ phy.o tsconfig.o mse.o loopback.o
diff --git a/net/ethtool/loopback.c b/net/ethtool/loopback.c
new file mode 100644
index 000000000000..1c6d27857f8a
--- /dev/null
+++ b/net/ethtool/loopback.c
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include "netlink.h"
+#include "common.h"
+
+struct loopback_req_info {
+ struct ethnl_req_info base;
+};
+
+struct loopback_reply_data {
+ struct ethnl_reply_data base;
+ struct ethtool_loopback_cfg cfg;
+};
+
+#define LOOPBACK_REPDATA(__reply_base) \
+ container_of(__reply_base, struct loopback_reply_data, base)
+
+/* GET */
+
+static const struct nla_policy
+ethnl_loopback_entry_policy[ETHTOOL_A_LOOPBACK_ENTRY_MAX + 1] = {
+ [ETHTOOL_A_LOOPBACK_ENTRY_COMPONENT] =
+ NLA_POLICY_MAX(NLA_U32, ETHTOOL_LOOPBACK_COMPONENT_MODULE),
+ [ETHTOOL_A_LOOPBACK_ENTRY_ID] =
+ NLA_POLICY_MIN(NLA_U32, 1),
+ [ETHTOOL_A_LOOPBACK_ENTRY_NAME] =
+ { .type = NLA_NUL_STRING, .len = ETH_GSTRING_LEN - 1 },
+ [ETHTOOL_A_LOOPBACK_ENTRY_DIRECTION] =
+ NLA_POLICY_MASK(NLA_U32, ETHTOOL_LOOPBACK_DIRECTION_NEAR_END |
+ ETHTOOL_LOOPBACK_DIRECTION_FAR_END),
+};
+
+const struct nla_policy ethnl_loopback_get_policy[] = {
+ [ETHTOOL_A_LOOPBACK_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
+};
+
+static int loopback_get_entries(struct net_device *dev,
+ struct ethtool_loopback_cfg *cfg)
+{
+ return 0;
+}
+
+static int loopback_prepare_data(const struct ethnl_req_info *req_base,
+ struct ethnl_reply_data *reply_base,
+ const struct genl_info *info)
+{
+ struct loopback_reply_data *data = LOOPBACK_REPDATA(reply_base);
+ struct net_device *dev = reply_base->dev;
+ int ret;
+
+ ret = ethnl_ops_begin(dev);
+ if (ret < 0)
+ return ret;
+
+ ret = loopback_get_entries(dev, &data->cfg);
+
+ ethnl_ops_complete(dev);
+
+ return ret;
+}
+
+static int loopback_reply_size(const struct ethnl_req_info *req_base,
+ const struct ethnl_reply_data *reply_base)
+{
+ const struct loopback_reply_data *data = LOOPBACK_REPDATA(reply_base);
+ int entry_size;
+
+ /* Per-entry: nest + component + id + name + supported + direction */
+ entry_size = nla_total_size(0) + /* nest */
+ nla_total_size(sizeof(u32)) + /* component */
+ nla_total_size(sizeof(u32)) + /* id */
+ nla_total_size(sizeof(u32)) + /* supported */
+ nla_total_size(sizeof(u32)) + /* direction */
+ nla_total_size(ETH_GSTRING_LEN); /* name */
+
+ return data->cfg.n_entries * entry_size;
+}
+
+static int loopback_fill_entry(struct sk_buff *skb,
+ const struct ethtool_loopback_entry *entry)
+{
+ struct nlattr *nest;
+
+ nest = nla_nest_start(skb, ETHTOOL_A_LOOPBACK_ENTRY);
+ if (!nest)
+ return -EMSGSIZE;
+
+ if (nla_put_u32(skb, ETHTOOL_A_LOOPBACK_ENTRY_COMPONENT,
+ entry->component))
+ goto err_cancel;
+
+ if (entry->id &&
+ nla_put_u32(skb, ETHTOOL_A_LOOPBACK_ENTRY_ID, entry->id))
+ goto err_cancel;
+
+ if (nla_put_u32(skb, ETHTOOL_A_LOOPBACK_ENTRY_SUPPORTED,
+ entry->supported) ||
+ nla_put_u32(skb, ETHTOOL_A_LOOPBACK_ENTRY_DIRECTION,
+ entry->direction) ||
+ nla_put_string(skb, ETHTOOL_A_LOOPBACK_ENTRY_NAME,
+ entry->name))
+ goto err_cancel;
+
+ nla_nest_end(skb, nest);
+ return 0;
+
+err_cancel:
+ nla_nest_cancel(skb, nest);
+ return -EMSGSIZE;
+}
+
+static int loopback_fill_reply(struct sk_buff *skb,
+ const struct ethnl_req_info *req_base,
+ const struct ethnl_reply_data *reply_base)
+{
+ const struct loopback_reply_data *data = LOOPBACK_REPDATA(reply_base);
+ const struct ethtool_loopback_cfg *cfg = &data->cfg;
+ u32 i;
+
+ for (i = 0; i < cfg->n_entries; i++) {
+ int ret = loopback_fill_entry(skb, &cfg->entries[i]);
+
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+/* SET */
+
+const struct nla_policy ethnl_loopback_set_policy[ETHTOOL_A_LOOPBACK_ENTRY + 1] = {
+ [ETHTOOL_A_LOOPBACK_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
+ [ETHTOOL_A_LOOPBACK_ENTRY] = NLA_POLICY_NESTED(ethnl_loopback_entry_policy),
+};
+
+static int loopback_parse_entry(struct nlattr *attr,
+ struct ethtool_loopback_entry *entry,
+ struct netlink_ext_ack *extack)
+{
+ struct nlattr *tb[ETHTOOL_A_LOOPBACK_ENTRY_MAX + 1];
+ int ret;
+
+ ret = nla_parse_nested(tb, ETHTOOL_A_LOOPBACK_ENTRY_MAX, attr,
+ ethnl_loopback_entry_policy, extack);
+ if (ret < 0)
+ return ret;
+
+ if (!tb[ETHTOOL_A_LOOPBACK_ENTRY_COMPONENT]) {
+ NL_SET_ERR_MSG_ATTR(extack, attr,
+ "loopback component is required");
+ return -EINVAL;
+ }
+
+ entry->component = nla_get_u32(tb[ETHTOOL_A_LOOPBACK_ENTRY_COMPONENT]);
+
+ if (tb[ETHTOOL_A_LOOPBACK_ENTRY_ID])
+ entry->id = nla_get_u32(tb[ETHTOOL_A_LOOPBACK_ENTRY_ID]);
+
+ if (!tb[ETHTOOL_A_LOOPBACK_ENTRY_NAME]) {
+ NL_SET_ERR_MSG_ATTR(extack, attr,
+ "loopback name is required");
+ return -EINVAL;
+ }
+ nla_strscpy(entry->name, tb[ETHTOOL_A_LOOPBACK_ENTRY_NAME],
+ sizeof(entry->name));
+
+ if (!tb[ETHTOOL_A_LOOPBACK_ENTRY_DIRECTION]) {
+ NL_SET_ERR_MSG_ATTR(extack, attr,
+ "loopback direction is required");
+ return -EINVAL;
+ }
+
+ entry->direction = nla_get_u32(tb[ETHTOOL_A_LOOPBACK_ENTRY_DIRECTION]);
+
+ return 0;
+}
+
+static int loopback_set_one(struct net_device *dev,
+ const struct ethtool_loopback_entry *entry,
+ struct netlink_ext_ack *extack)
+{
+ switch (entry->component) {
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ethnl_set_loopback(struct ethnl_req_info *req_info,
+ struct genl_info *info)
+{
+ struct net_device *dev = req_info->dev;
+ struct ethtool_loopback_cfg cfg = {};
+ int rem, ret, mod = 0;
+ struct nlattr *attr;
+ u32 i;
+
+ nla_for_each_attr(attr, genlmsg_data(info->genlhdr),
+ genlmsg_len(info->genlhdr), rem) {
+ if (nla_type(attr) != ETHTOOL_A_LOOPBACK_ENTRY)
+ continue;
+
+ if (cfg.n_entries >= ETHTOOL_LOOPBACK_MAX_ENTRIES) {
+ NL_SET_ERR_MSG(info->extack,
+ "too many loopback entries");
+ return -EINVAL;
+ }
+
+ ret = loopback_parse_entry(attr, &cfg.entries[cfg.n_entries],
+ info->extack);
+ if (ret < 0)
+ return ret;
+
+ cfg.n_entries++;
+ }
+
+ if (!cfg.n_entries) {
+ NL_SET_ERR_MSG(info->extack, "no loopback entries specified");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < cfg.n_entries; i++) {
+ ret = loopback_set_one(dev, &cfg.entries[i], info->extack);
+ if (ret < 0)
+ return ret;
+ if (ret > 0)
+ mod = 1;
+ }
+
+ return mod;
+}
+
+const struct ethnl_request_ops ethnl_loopback_request_ops = {
+ .request_cmd = ETHTOOL_MSG_LOOPBACK_GET,
+ .reply_cmd = ETHTOOL_MSG_LOOPBACK_GET_REPLY,
+ .hdr_attr = ETHTOOL_A_LOOPBACK_HEADER,
+ .req_info_size = sizeof(struct loopback_req_info),
+ .reply_data_size = sizeof(struct loopback_reply_data),
+
+ .prepare_data = loopback_prepare_data,
+ .reply_size = loopback_reply_size,
+ .fill_reply = loopback_fill_reply,
+
+ .set = ethnl_set_loopback,
+ .set_ntf_cmd = ETHTOOL_MSG_LOOPBACK_NTF,
+};
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index 6e5f0f4f815a..c438828ea072 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -421,6 +421,8 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = {
[ETHTOOL_MSG_TSCONFIG_SET] = ðnl_tsconfig_request_ops,
[ETHTOOL_MSG_PHY_GET] = ðnl_phy_request_ops,
[ETHTOOL_MSG_MSE_GET] = ðnl_mse_request_ops,
+ [ETHTOOL_MSG_LOOPBACK_GET] = ðnl_loopback_request_ops,
+ [ETHTOOL_MSG_LOOPBACK_SET] = ðnl_loopback_request_ops,
};
static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb)
@@ -962,6 +964,7 @@ ethnl_default_notify_ops[ETHTOOL_MSG_KERNEL_MAX + 1] = {
[ETHTOOL_MSG_MM_NTF] = ðnl_mm_request_ops,
[ETHTOOL_MSG_RSS_NTF] = ðnl_rss_request_ops,
[ETHTOOL_MSG_RSS_CREATE_NTF] = ðnl_rss_request_ops,
+ [ETHTOOL_MSG_LOOPBACK_NTF] = ðnl_loopback_request_ops,
};
/* default notification handler */
@@ -1070,6 +1073,7 @@ static const ethnl_notify_handler_t ethnl_notify_handlers[] = {
[ETHTOOL_MSG_MM_NTF] = ethnl_default_notify,
[ETHTOOL_MSG_RSS_NTF] = ethnl_default_notify,
[ETHTOOL_MSG_RSS_CREATE_NTF] = ethnl_default_notify,
+ [ETHTOOL_MSG_LOOPBACK_NTF] = ethnl_default_notify,
};
void ethnl_notify(struct net_device *dev, unsigned int cmd,
@@ -1544,6 +1548,22 @@ static const struct genl_ops ethtool_genl_ops[] = {
.policy = ethnl_mse_get_policy,
.maxattr = ARRAY_SIZE(ethnl_mse_get_policy) - 1,
},
+ {
+ .cmd = ETHTOOL_MSG_LOOPBACK_GET,
+ .doit = ethnl_default_doit,
+ .start = ethnl_default_start,
+ .dumpit = ethnl_default_dumpit,
+ .done = ethnl_default_done,
+ .policy = ethnl_loopback_get_policy,
+ .maxattr = ARRAY_SIZE(ethnl_loopback_get_policy) - 1,
+ },
+ {
+ .cmd = ETHTOOL_MSG_LOOPBACK_SET,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .doit = ethnl_default_set_doit,
+ .policy = ethnl_loopback_set_policy,
+ .maxattr = ARRAY_SIZE(ethnl_loopback_set_policy) - 1,
+ },
};
static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index 89010eaa67df..5660ce494916 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -443,6 +443,7 @@ extern const struct ethnl_request_ops ethnl_mm_request_ops;
extern const struct ethnl_request_ops ethnl_phy_request_ops;
extern const struct ethnl_request_ops ethnl_tsconfig_request_ops;
extern const struct ethnl_request_ops ethnl_mse_request_ops;
+extern const struct ethnl_request_ops ethnl_loopback_request_ops;
extern const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_FLAGS + 1];
extern const struct nla_policy ethnl_header_policy_stats[ETHTOOL_A_HEADER_FLAGS + 1];
@@ -499,6 +500,8 @@ extern const struct nla_policy ethnl_phy_get_policy[ETHTOOL_A_PHY_HEADER + 1];
extern const struct nla_policy ethnl_tsconfig_get_policy[ETHTOOL_A_TSCONFIG_HEADER + 1];
extern const struct nla_policy ethnl_tsconfig_set_policy[ETHTOOL_A_TSCONFIG_MAX + 1];
extern const struct nla_policy ethnl_mse_get_policy[ETHTOOL_A_MSE_HEADER + 1];
+extern const struct nla_policy ethnl_loopback_get_policy[ETHTOOL_A_LOOPBACK_HEADER + 1];
+extern const struct nla_policy ethnl_loopback_set_policy[ETHTOOL_A_LOOPBACK_ENTRY + 1];
int ethnl_set_features(struct sk_buff *skb, struct genl_info *info);
int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info);
--
2.53.0
Hi Björn, On 08/03/2026 13:40, Björn Töpel wrote: > Add the kernel-side ETHTOOL_MSG_LOOPBACK_GET, > ETHTOOL_MSG_LOOPBACK_SET, and ETHTOOL_MSG_LOOPBACK_NTF handlers using > the standard ethnl_request_ops infrastructure. > > GET collects loopback entries from per-component helpers via > loopback_get_entries(). SET parses the nested entry attributes, > dispatches each to loopback_set_one(), and only sends a notification > when the state is changed. > > No components are wired yet. > > Signed-off-by: Björn Töpel <bjorn@kernel.org> At a first glance, what I see is that you're using the GET ->doit method to retrieve an array of loopback entries. The pefered approach in that case is to use the GET ->dumpit command instead, issueing a netlink DUMP request to list all available loopback entities on a given netdev. If you want some reference on that, take a look at the phy.c + the 'perphy' helpers in net/ethtool/netlink.c The idea is that you can pass a netdev ifindex in the header of the DUMP request, which you can use to dump all loopbacks the passed netdev. You can also check the ethtool code itself, you'll see that when you use the "ethtool --show-phys eth0" command for example, it issues a DUMP request to the kernel. I'll continue the review w.r.t the actual content of the messages :) Maxime
Maxime Chevallier <maxime.chevallier@bootlin.com> writes: > Hi Björn, > > On 08/03/2026 13:40, Björn Töpel wrote: >> Add the kernel-side ETHTOOL_MSG_LOOPBACK_GET, >> ETHTOOL_MSG_LOOPBACK_SET, and ETHTOOL_MSG_LOOPBACK_NTF handlers using >> the standard ethnl_request_ops infrastructure. >> >> GET collects loopback entries from per-component helpers via >> loopback_get_entries(). SET parses the nested entry attributes, >> dispatches each to loopback_set_one(), and only sends a notification >> when the state is changed. >> >> No components are wired yet. >> >> Signed-off-by: Björn Töpel <bjorn@kernel.org> > > At a first glance, what I see is that you're using the GET ->doit method > to retrieve an array of loopback entries. The pefered approach in that > case is to use the GET ->dumpit command instead, issueing a netlink DUMP > request to list all available loopback entities on a given netdev. > > If you want some reference on that, take a look at the phy.c + the > 'perphy' helpers in net/ethtool/netlink.c > > The idea is that you can pass a netdev ifindex in the header of the DUMP > request, which you can use to dump all loopbacks the passed netdev. > > You can also check the ethtool code itself, you'll see that when you use > the "ethtool --show-phys eth0" command for example, it issues a DUMP > request to the kernel. Ah, got it! Thanks! > I'll continue the review w.r.t the actual content of the messages :) ...and thank you! Björn
Björn Töpel <bjorn@kernel.org> writes: > Maxime Chevallier <maxime.chevallier@bootlin.com> writes: > >> Hi Björn, >> >> On 08/03/2026 13:40, Björn Töpel wrote: >>> Add the kernel-side ETHTOOL_MSG_LOOPBACK_GET, >>> ETHTOOL_MSG_LOOPBACK_SET, and ETHTOOL_MSG_LOOPBACK_NTF handlers using >>> the standard ethnl_request_ops infrastructure. >>> >>> GET collects loopback entries from per-component helpers via >>> loopback_get_entries(). SET parses the nested entry attributes, >>> dispatches each to loopback_set_one(), and only sends a notification >>> when the state is changed. >>> >>> No components are wired yet. >>> >>> Signed-off-by: Björn Töpel <bjorn@kernel.org> >> >> At a first glance, what I see is that you're using the GET ->doit method >> to retrieve an array of loopback entries. The pefered approach in that >> case is to use the GET ->dumpit command instead, issueing a netlink DUMP >> request to list all available loopback entities on a given netdev. >> >> If you want some reference on that, take a look at the phy.c + the >> 'perphy' helpers in net/ethtool/netlink.c >> >> The idea is that you can pass a netdev ifindex in the header of the DUMP >> request, which you can use to dump all loopbacks the passed netdev. >> >> You can also check the ethtool code itself, you'll see that when you use >> the "ethtool --show-phys eth0" command for example, it issues a DUMP >> request to the kernel. > > Ah, got it! Thanks! Thanks for pointing this out! I like how using this can get rid of the fixed size array in struct ethtool_loopback_cfg, which had a bad smell to me. Thinking out loud -- Now, using a similar scheme for loopback requires some thought around the sub-iterator: Loopback has "multiple" sub-iterator: (component, [id], name)... or needs scheme to deal with one sub-iter, and loopback can share a lot of code with the perphy-functions. Björn
On 09/03/2026 15:51, Björn Töpel wrote: > Björn Töpel <bjorn@kernel.org> writes: > >> Maxime Chevallier <maxime.chevallier@bootlin.com> writes: >> >>> Hi Björn, >>> >>> On 08/03/2026 13:40, Björn Töpel wrote: >>>> Add the kernel-side ETHTOOL_MSG_LOOPBACK_GET, >>>> ETHTOOL_MSG_LOOPBACK_SET, and ETHTOOL_MSG_LOOPBACK_NTF handlers using >>>> the standard ethnl_request_ops infrastructure. >>>> >>>> GET collects loopback entries from per-component helpers via >>>> loopback_get_entries(). SET parses the nested entry attributes, >>>> dispatches each to loopback_set_one(), and only sends a notification >>>> when the state is changed. >>>> >>>> No components are wired yet. >>>> >>>> Signed-off-by: Björn Töpel <bjorn@kernel.org> >>> >>> At a first glance, what I see is that you're using the GET ->doit method >>> to retrieve an array of loopback entries. The pefered approach in that >>> case is to use the GET ->dumpit command instead, issueing a netlink DUMP >>> request to list all available loopback entities on a given netdev. >>> >>> If you want some reference on that, take a look at the phy.c + the >>> 'perphy' helpers in net/ethtool/netlink.c >>> >>> The idea is that you can pass a netdev ifindex in the header of the DUMP >>> request, which you can use to dump all loopbacks the passed netdev. >>> >>> You can also check the ethtool code itself, you'll see that when you use >>> the "ethtool --show-phys eth0" command for example, it issues a DUMP >>> request to the kernel. >> >> Ah, got it! Thanks! > > Thanks for pointing this out! I like how using this can get rid of the > fixed size array in struct ethtool_loopback_cfg, which had a bad smell > to me. > > Thinking out loud -- Now, using a similar scheme for loopback requires > some thought around the sub-iterator: Loopback has "multiple" > sub-iterator: (component, [id], name)... or needs scheme to deal with > one sub-iter, and loopback can share a lot of code with the > perphy-functions. yeah, at some point in the past I played around with the idea of more generic ethnl helpers for filtered DUMP requests : https://lore.kernel.org/netdev/20250324104012.367366-1-maxime.chevallier@bootlin.com/ At that time, the PHY usecase was the main user so I ended-up ditching that in favor of the more specific perphy helpers we have today, after discussing the matter with Jakub. Now that we're seeing more and more ethnl DUMP commands that can take a netdev ifindex in the request, maybe some of that code above can be useful for you and we can resurect this approach ? Maxime > > > Björn
© 2016 - 2026 Red Hat, Inc.