From: Ralf Lici <ralf@mandelbit.com>
To: netdev@vger.kernel.org
Cc: "Daniel Gröber" <dxld@darkboxed.org>,
"Ralf Lici" <ralf@mandelbit.com>,
"Antonio Quartulli" <antonio@mandelbit.com>,
"Donald Hunter" <donald.hunter@gmail.com>,
"Jakub Kicinski" <kuba@kernel.org>,
"David S. Miller" <davem@davemloft.net>,
"Eric Dumazet" <edumazet@google.com>,
"Paolo Abeni" <pabeni@redhat.com>,
"Simon Horman" <horms@kernel.org>,
"Andrew Lunn" <andrew+netdev@lunn.ch>,
linux-kernel@vger.kernel.org
Subject: [RFC net-next 13/15] ipxlat: add netlink control plane and uapi
Date: Thu, 19 Mar 2026 16:12:22 +0100 [thread overview]
Message-ID: <20260319151230.655687-14-ralf@mandelbit.com> (raw)
In-Reply-To: <20260319151230.655687-1-ralf@mandelbit.com>
Expose runtime configuration through netlink with validated set/get/dump
operations and generated policy glue from the YAML spec. The API
configures the translator prefix and MTU threshold used by the data
path.
Signed-off-by: Ralf Lici <ralf@mandelbit.com>
---
Documentation/netlink/specs/ipxlat.yaml | 97 +++++++
drivers/net/ipxlat/Makefile | 2 +
drivers/net/ipxlat/main.c | 9 +
drivers/net/ipxlat/netlink-gen.c | 71 +++++
drivers/net/ipxlat/netlink-gen.h | 31 +++
drivers/net/ipxlat/netlink.c | 348 ++++++++++++++++++++++++
drivers/net/ipxlat/netlink.h | 27 ++
drivers/net/ipxlat/translate_46.c | 3 +-
include/uapi/linux/ipxlat.h | 48 ++++
9 files changed, 635 insertions(+), 1 deletion(-)
create mode 100644 Documentation/netlink/specs/ipxlat.yaml
create mode 100644 drivers/net/ipxlat/netlink-gen.c
create mode 100644 drivers/net/ipxlat/netlink-gen.h
create mode 100644 drivers/net/ipxlat/netlink.c
create mode 100644 drivers/net/ipxlat/netlink.h
create mode 100644 include/uapi/linux/ipxlat.h
diff --git a/Documentation/netlink/specs/ipxlat.yaml b/Documentation/netlink/specs/ipxlat.yaml
new file mode 100644
index 000000000000..d0df5ef16e04
--- /dev/null
+++ b/Documentation/netlink/specs/ipxlat.yaml
@@ -0,0 +1,97 @@
+# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
+#
+# Copyright (C) 2026- Mandelbit SRL
+#
+# Author: Antonio Quartulli <antonio@mandelbit.com>
+# Ralf Lici <ralf@mandelbit.com>
+#
+---
+name: ipxlat
+protocol: genetlink
+doc: Netlink protocol to control IPXLAT (SIIT) network devices.
+
+definitions:
+ -
+ type: const
+ name: xlat-prefix6-max-prefix-len
+ value: 96
+ doc: Maximum prefix length accepted for xlat-prefix6.
+
+attribute-sets:
+ -
+ name: pool
+ attributes:
+ -
+ name: prefix
+ type: binary
+ checks:
+ exact-len: 16
+ -
+ name: prefix-len
+ type: u8
+ checks:
+ max: xlat-prefix6-max-prefix-len
+ -
+ name: cfg
+ attributes:
+ -
+ name: xlat-prefix6
+ type: nest
+ doc: IPv6 translation prefix.
+ nested-attributes: pool
+ -
+ name: lowest-ipv6-mtu
+ type: u32
+ checks:
+ min: 1280
+ -
+ name: dev
+ attributes:
+ -
+ name: ifindex
+ type: u32
+ doc: Index of the ipxlat interface to operate on.
+ -
+ name: netnsid
+ type: s32
+ doc: ID of the netns the device lives in.
+ -
+ name: config
+ type: nest
+ doc: Ipxlat device configuration.
+ nested-attributes: cfg
+
+operations:
+ list:
+ -
+ name: dev-get
+ attribute-set: dev
+ doc: Get / dump configuration of ipxlat devices.
+ do:
+ pre: ipxlat-nl-pre-doit
+ post: ipxlat-nl-post-doit
+ request:
+ attributes:
+ - ifindex
+ reply: &dev-all
+ attributes:
+ - ifindex
+ - netnsid
+ - config
+ dump:
+ reply: *dev-all
+
+ -
+ name: dev-set
+ doc: Set configuration of an ipxlat device.
+ attribute-set: dev
+ flags: [admin-perm]
+ do:
+ request:
+ attributes:
+ - ifindex
+ - config
+ reply:
+ attributes: []
+ pre: ipxlat-nl-pre-doit
+ post: ipxlat-nl-post-doit
diff --git a/drivers/net/ipxlat/Makefile b/drivers/net/ipxlat/Makefile
index 2ded504902e3..b906d5698351 100644
--- a/drivers/net/ipxlat/Makefile
+++ b/drivers/net/ipxlat/Makefile
@@ -13,3 +13,5 @@ ipxlat-objs += translate_46.o
ipxlat-objs += translate_64.o
ipxlat-objs += icmp_46.o
ipxlat-objs += icmp_64.o
+ipxlat-objs += netlink.o
+ipxlat-objs += netlink-gen.o
diff --git a/drivers/net/ipxlat/main.c b/drivers/net/ipxlat/main.c
index a1b4bcd39478..bef67ed634b6 100644
--- a/drivers/net/ipxlat/main.c
+++ b/drivers/net/ipxlat/main.c
@@ -18,6 +18,7 @@
#include "dispatch.h"
#include "ipxlpriv.h"
#include "main.h"
+#include "netlink.h"
MODULE_AUTHOR("Alberto Leiva Popper <ydahhrk@gmail.com>");
MODULE_AUTHOR("Antonio Quartulli <antonio@mandelbit.com>");
@@ -127,11 +128,19 @@ static int __init ipxlat_init(void)
return err;
}
+ err = ipxlat_nl_register();
+ if (err) {
+ pr_err("ipxlat: failed to register netlink family: %d\n", err);
+ rtnl_link_unregister(&ipxlat_link_ops);
+ return err;
+ }
+
return 0;
}
static void __exit ipxlat_exit(void)
{
+ ipxlat_nl_unregister();
rtnl_link_unregister(&ipxlat_link_ops);
}
diff --git a/drivers/net/ipxlat/netlink-gen.c b/drivers/net/ipxlat/netlink-gen.c
new file mode 100644
index 000000000000..e2cfaa6bb4dc
--- /dev/null
+++ b/drivers/net/ipxlat/netlink-gen.c
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
+/* Do not edit directly, auto-generated from: */
+/* Documentation/netlink/specs/ipxlat.yaml */
+/* YNL-GEN kernel source */
+/* To regenerate run: tools/net/ynl/ynl-regen.sh */
+
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+#include "netlink-gen.h"
+
+#include <uapi/linux/ipxlat.h>
+
+/* Common nested types */
+const struct nla_policy ipxlat_cfg_nl_policy[IPXLAT_A_CFG_LOWEST_IPV6_MTU + 1] = {
+ [IPXLAT_A_CFG_XLAT_PREFIX6] = NLA_POLICY_NESTED(ipxlat_pool_nl_policy),
+ [IPXLAT_A_CFG_LOWEST_IPV6_MTU] = NLA_POLICY_MIN(NLA_U32, 1280),
+};
+
+const struct nla_policy ipxlat_pool_nl_policy[IPXLAT_A_POOL_PREFIX_LEN + 1] = {
+ [IPXLAT_A_POOL_PREFIX] = NLA_POLICY_EXACT_LEN(16),
+ [IPXLAT_A_POOL_PREFIX_LEN] = NLA_POLICY_MAX(NLA_U8, IPXLAT_XLAT_PREFIX6_MAX_PREFIX_LEN),
+};
+
+/* IPXLAT_CMD_DEV_GET - do */
+static const struct nla_policy ipxlat_dev_get_nl_policy[IPXLAT_A_DEV_IFINDEX + 1] = {
+ [IPXLAT_A_DEV_IFINDEX] = { .type = NLA_U32, },
+};
+
+/* IPXLAT_CMD_DEV_SET - do */
+static const struct nla_policy ipxlat_dev_set_nl_policy[IPXLAT_A_DEV_CONFIG + 1] = {
+ [IPXLAT_A_DEV_IFINDEX] = { .type = NLA_U32, },
+ [IPXLAT_A_DEV_CONFIG] = NLA_POLICY_NESTED(ipxlat_cfg_nl_policy),
+};
+
+/* Ops table for ipxlat */
+static const struct genl_split_ops ipxlat_nl_ops[] = {
+ {
+ .cmd = IPXLAT_CMD_DEV_GET,
+ .pre_doit = ipxlat_nl_pre_doit,
+ .doit = ipxlat_nl_dev_get_doit,
+ .post_doit = ipxlat_nl_post_doit,
+ .policy = ipxlat_dev_get_nl_policy,
+ .maxattr = IPXLAT_A_DEV_IFINDEX,
+ .flags = GENL_CMD_CAP_DO,
+ },
+ {
+ .cmd = IPXLAT_CMD_DEV_GET,
+ .dumpit = ipxlat_nl_dev_get_dumpit,
+ .flags = GENL_CMD_CAP_DUMP,
+ },
+ {
+ .cmd = IPXLAT_CMD_DEV_SET,
+ .pre_doit = ipxlat_nl_pre_doit,
+ .doit = ipxlat_nl_dev_set_doit,
+ .post_doit = ipxlat_nl_post_doit,
+ .policy = ipxlat_dev_set_nl_policy,
+ .maxattr = IPXLAT_A_DEV_CONFIG,
+ .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+ },
+};
+
+struct genl_family ipxlat_nl_family __ro_after_init = {
+ .name = IPXLAT_FAMILY_NAME,
+ .version = IPXLAT_FAMILY_VERSION,
+ .netnsok = true,
+ .parallel_ops = true,
+ .module = THIS_MODULE,
+ .split_ops = ipxlat_nl_ops,
+ .n_split_ops = ARRAY_SIZE(ipxlat_nl_ops),
+};
diff --git a/drivers/net/ipxlat/netlink-gen.h b/drivers/net/ipxlat/netlink-gen.h
new file mode 100644
index 000000000000..2a766d05e0b4
--- /dev/null
+++ b/drivers/net/ipxlat/netlink-gen.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+/* Do not edit directly, auto-generated from: */
+/* Documentation/netlink/specs/ipxlat.yaml */
+/* YNL-GEN kernel header */
+/* To regenerate run: tools/net/ynl/ynl-regen.sh */
+
+#ifndef _LINUX_IPXLAT_GEN_H
+#define _LINUX_IPXLAT_GEN_H
+
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+#include <uapi/linux/ipxlat.h>
+
+/* Common nested types */
+extern const struct nla_policy ipxlat_cfg_nl_policy[IPXLAT_A_CFG_LOWEST_IPV6_MTU + 1];
+extern const struct nla_policy ipxlat_pool_nl_policy[IPXLAT_A_POOL_PREFIX_LEN + 1];
+
+int ipxlat_nl_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+ struct genl_info *info);
+void
+ipxlat_nl_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+ struct genl_info *info);
+
+int ipxlat_nl_dev_get_doit(struct sk_buff *skb, struct genl_info *info);
+int ipxlat_nl_dev_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb);
+int ipxlat_nl_dev_set_doit(struct sk_buff *skb, struct genl_info *info);
+
+extern struct genl_family ipxlat_nl_family;
+
+#endif /* _LINUX_IPXLAT_GEN_H */
diff --git a/drivers/net/ipxlat/netlink.c b/drivers/net/ipxlat/netlink.c
new file mode 100644
index 000000000000..02d097726f22
--- /dev/null
+++ b/drivers/net/ipxlat/netlink.c
@@ -0,0 +1,348 @@
+// SPDX-License-Identifier: GPL-2.0
+/* IPXLAT - Stateless IP/ICMP Translation (SIIT) virtual device driver
+ *
+ * Copyright (C) 2026- Mandelbit SRL
+ * Copyright (C) 2026- Daniel Gröber <dxld@darkboxed.org>
+ *
+ * Author: Antonio Quartulli <antonio@mandelbit.com>
+ * Daniel Gröber <dxld@darkboxed.org>
+ * Ralf Lici <ralf@mandelbit.com>
+ */
+
+#include <net/genetlink.h>
+#include <net/ipv6.h>
+
+#include <uapi/linux/ipxlat.h>
+
+#include "netlink.h"
+#include "main.h"
+#include "netlink-gen.h"
+#include "ipxlpriv.h"
+
+MODULE_ALIAS_GENL_FAMILY(IPXLAT_FAMILY_NAME);
+
+struct ipxlat_nl_info_ctx {
+ struct ipxlat_priv *ipxlat;
+ netdevice_tracker tracker;
+};
+
+struct ipxlat_nl_dump_ctx {
+ unsigned long last_ifindex;
+};
+
+/**
+ * ipxlat_get_from_attrs - retrieve ipxlat private data for target netdev
+ * @net: network namespace where to look for the interface
+ * @info: generic netlink info from the user request
+ * @tracker: tracker object to be used for the netdev reference acquisition
+ *
+ * Return: the ipxlat private data, if found, or an error otherwise
+ */
+static struct ipxlat_priv *ipxlat_get_from_attrs(struct net *net,
+ struct genl_info *info,
+ netdevice_tracker *tracker)
+{
+ struct ipxlat_priv *ipxlat;
+ struct net_device *dev;
+ int ifindex;
+
+ if (GENL_REQ_ATTR_CHECK(info, IPXLAT_A_DEV_IFINDEX))
+ return ERR_PTR(-EINVAL);
+ ifindex = nla_get_u32(info->attrs[IPXLAT_A_DEV_IFINDEX]);
+
+ rcu_read_lock();
+ dev = dev_get_by_index_rcu(net, ifindex);
+ if (!dev) {
+ rcu_read_unlock();
+ NL_SET_ERR_MSG_MOD(info->extack,
+ "ifindex does not match any interface");
+ return ERR_PTR(-ENODEV);
+ }
+
+ if (!ipxlat_dev_is_valid(dev)) {
+ rcu_read_unlock();
+ NL_SET_ERR_MSG_MOD(info->extack,
+ "specified interface is not ipxlat");
+ NL_SET_BAD_ATTR(info->extack,
+ info->attrs[IPXLAT_A_DEV_IFINDEX]);
+ return ERR_PTR(-EINVAL);
+ }
+
+ ipxlat = netdev_priv(dev);
+ netdev_hold(dev, tracker, GFP_ATOMIC);
+ rcu_read_unlock();
+
+ return ipxlat;
+}
+
+int ipxlat_nl_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct ipxlat_nl_info_ctx *ctx = (struct ipxlat_nl_info_ctx *)info->ctx;
+ struct ipxlat_priv *ipxlat;
+
+ BUILD_BUG_ON(sizeof(*ctx) > sizeof(info->ctx));
+
+ ipxlat = ipxlat_get_from_attrs(genl_info_net(info), info,
+ &ctx->tracker);
+ if (IS_ERR(ipxlat))
+ return PTR_ERR(ipxlat);
+
+ ctx->ipxlat = ipxlat;
+ return 0;
+}
+
+void ipxlat_nl_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct ipxlat_nl_info_ctx *ctx = (struct ipxlat_nl_info_ctx *)info->ctx;
+
+ if (ctx->ipxlat)
+ netdev_put(ctx->ipxlat->dev, &ctx->tracker);
+}
+
+static int ipxlat_nl_send_dev(struct sk_buff *skb, struct ipxlat_priv *ipxlat,
+ struct net *src_net, const u32 portid,
+ const u32 seq, int flags)
+{
+ struct nlattr *attr_cfg, *attr_pool;
+ struct ipv6_prefix xlat_prefix6;
+ int id, ret = -EMSGSIZE;
+ u32 lowest_ipv6_mtu;
+ void *hdr;
+
+ /* snapshot settings under lock so userspace sees a coherent state */
+ mutex_lock(&ipxlat->cfg_lock);
+ xlat_prefix6 = ipxlat->xlat_prefix6;
+ lowest_ipv6_mtu = ipxlat->lowest_ipv6_mtu;
+ mutex_unlock(&ipxlat->cfg_lock);
+
+ hdr = genlmsg_put(skb, portid, seq, &ipxlat_nl_family, flags,
+ IPXLAT_CMD_DEV_GET);
+ if (!hdr)
+ return -ENOBUFS;
+
+ if (nla_put_u32(skb, IPXLAT_A_DEV_IFINDEX, ipxlat->dev->ifindex))
+ goto err;
+
+ if (!net_eq(src_net, dev_net(ipxlat->dev))) {
+ id = peernet2id_alloc(src_net, dev_net(ipxlat->dev),
+ GFP_ATOMIC);
+ if (id < 0) {
+ ret = id;
+ goto err;
+ }
+ if (nla_put_s32(skb, IPXLAT_A_DEV_NETNSID, id))
+ goto err;
+ }
+
+ attr_cfg = nla_nest_start(skb, IPXLAT_A_DEV_CONFIG);
+ if (!attr_cfg)
+ goto err;
+
+ attr_pool = nla_nest_start(skb, IPXLAT_A_CFG_XLAT_PREFIX6);
+ if (!attr_pool)
+ goto err;
+
+ if (nla_put_in6_addr(skb, IPXLAT_A_POOL_PREFIX, &xlat_prefix6.addr) ||
+ nla_put_u8(skb, IPXLAT_A_POOL_PREFIX_LEN, xlat_prefix6.len))
+ goto err;
+
+ nla_nest_end(skb, attr_pool);
+
+ if (nla_put_u32(skb, IPXLAT_A_CFG_LOWEST_IPV6_MTU, lowest_ipv6_mtu))
+ goto err;
+
+ nla_nest_end(skb, attr_cfg);
+ genlmsg_end(skb, hdr);
+
+ return 0;
+err:
+ genlmsg_cancel(skb, hdr);
+ return ret;
+}
+
+int ipxlat_nl_dev_get_doit(struct sk_buff *skb, struct genl_info *info)
+{
+ struct ipxlat_nl_info_ctx *ctx = (struct ipxlat_nl_info_ctx *)info->ctx;
+ struct sk_buff *reply;
+ int ret;
+
+ if (GENL_REQ_ATTR_CHECK(info, IPXLAT_A_DEV_IFINDEX))
+ return -EINVAL;
+
+ reply = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!reply)
+ return -ENOMEM;
+
+ ret = ipxlat_nl_send_dev(reply, ctx->ipxlat, genl_info_net(info),
+ info->snd_portid, info->snd_seq, 0);
+ if (ret < 0) {
+ nlmsg_free(reply);
+ return ret;
+ }
+
+ return genlmsg_reply(reply, info);
+}
+
+int ipxlat_nl_dev_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct ipxlat_nl_dump_ctx *state = (struct ipxlat_nl_dump_ctx *)cb->ctx;
+ struct net *net = sock_net(cb->skb->sk);
+ netdevice_tracker tracker;
+ struct net_device *dev;
+ int ret;
+
+ rcu_read_lock();
+ for_each_netdev_dump(net, dev, state->last_ifindex) {
+ if (!ipxlat_dev_is_valid(dev))
+ continue;
+
+ netdev_hold(dev, &tracker, GFP_ATOMIC);
+ rcu_read_unlock();
+
+ ret = ipxlat_nl_send_dev(skb, netdev_priv(dev), net,
+ NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq, NLM_F_MULTI);
+
+ rcu_read_lock();
+ netdev_put(dev, &tracker);
+
+ if (ret < 0) {
+ if (skb->len > 0)
+ break;
+ rcu_read_unlock();
+ return ret;
+ }
+ }
+ rcu_read_unlock();
+ return skb->len;
+}
+
+static int ipxlat_nl_validate_xlat_prefix6(const struct ipv6_prefix *prefix,
+ struct netlink_ext_ack *extack)
+{
+ if (prefix->len != 32 && prefix->len != 40 && prefix->len != 48 &&
+ prefix->len != 56 && prefix->len != 64 && prefix->len != 96) {
+ NL_SET_ERR_MSG_FMT_MOD(extack,
+ "unsupported RFC 6052 prefix length: %u",
+ prefix->len);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ipxlat_nl_parse_xlat_prefix6(struct nlattr *attr,
+ struct ipv6_prefix *xlat_prefix6,
+ struct netlink_ext_ack *extack)
+{
+ struct nlattr *attrs_pool[IPXLAT_A_POOL_MAX + 1];
+ struct ipv6_prefix new_xlat_prefix6;
+ int ret;
+
+ new_xlat_prefix6 = *xlat_prefix6;
+
+ ret = nla_parse_nested(attrs_pool, IPXLAT_A_POOL_MAX, attr,
+ ipxlat_pool_nl_policy, extack);
+ if (ret)
+ return ret;
+
+ if (!attrs_pool[IPXLAT_A_POOL_PREFIX] &&
+ !attrs_pool[IPXLAT_A_POOL_PREFIX_LEN]) {
+ NL_SET_ERR_MSG_MOD(extack, "xlat-prefix6 update is empty");
+ return -EINVAL;
+ }
+
+ if (attrs_pool[IPXLAT_A_POOL_PREFIX])
+ new_xlat_prefix6.addr =
+ nla_get_in6_addr(attrs_pool[IPXLAT_A_POOL_PREFIX]);
+ if (attrs_pool[IPXLAT_A_POOL_PREFIX_LEN])
+ new_xlat_prefix6.len =
+ nla_get_u8(attrs_pool[IPXLAT_A_POOL_PREFIX_LEN]);
+
+ ret = ipxlat_nl_validate_xlat_prefix6(&new_xlat_prefix6, extack);
+ if (ret) {
+ if (attrs_pool[IPXLAT_A_POOL_PREFIX_LEN])
+ NL_SET_BAD_ATTR(extack,
+ attrs_pool[IPXLAT_A_POOL_PREFIX_LEN]);
+ else
+ NL_SET_BAD_ATTR(extack,
+ attrs_pool[IPXLAT_A_POOL_PREFIX]);
+ return ret;
+ }
+
+ *xlat_prefix6 = new_xlat_prefix6;
+ return 0;
+}
+
+int ipxlat_nl_dev_set_doit(struct sk_buff *skb, struct genl_info *info)
+{
+ struct ipxlat_nl_info_ctx *ctx = (struct ipxlat_nl_info_ctx *)info->ctx;
+ struct nlattr *attrs[IPXLAT_A_CFG_MAX + 1];
+ struct nlattr *xlat_prefix6_attr;
+ struct ipv6_prefix xlat_prefix6;
+ u32 lowest_ipv6_mtu;
+ int ret = 0;
+
+ if (GENL_REQ_ATTR_CHECK(info, IPXLAT_A_DEV_CONFIG))
+ return -EINVAL;
+
+ ret = nla_parse_nested(attrs, IPXLAT_A_CFG_MAX,
+ info->attrs[IPXLAT_A_DEV_CONFIG],
+ ipxlat_cfg_nl_policy, info->extack);
+ if (ret)
+ return ret;
+
+ if (!attrs[IPXLAT_A_CFG_XLAT_PREFIX6] &&
+ !attrs[IPXLAT_A_CFG_LOWEST_IPV6_MTU]) {
+ NL_SET_ERR_MSG_MOD(info->extack, "config update is empty");
+ return -EINVAL;
+ }
+ xlat_prefix6_attr = attrs[IPXLAT_A_CFG_XLAT_PREFIX6];
+
+ mutex_lock(&ctx->ipxlat->cfg_lock);
+
+ /* Stage updates that can fail before writing device state.
+ * This keeps dev-set all-or-nothing and avoids partial commits when
+ * xlat-prefix parsing/validation fails.
+ */
+ if (xlat_prefix6_attr) {
+ xlat_prefix6 = ctx->ipxlat->xlat_prefix6;
+ ret = ipxlat_nl_parse_xlat_prefix6(xlat_prefix6_attr,
+ &xlat_prefix6,
+ info->extack);
+ if (ret)
+ goto out_unlock;
+ }
+
+ if (xlat_prefix6_attr)
+ ctx->ipxlat->xlat_prefix6 = xlat_prefix6;
+ if (attrs[IPXLAT_A_CFG_LOWEST_IPV6_MTU]) {
+ lowest_ipv6_mtu =
+ nla_get_u32(attrs[IPXLAT_A_CFG_LOWEST_IPV6_MTU]);
+ WRITE_ONCE(ctx->ipxlat->lowest_ipv6_mtu, lowest_ipv6_mtu);
+ }
+
+out_unlock:
+ mutex_unlock(&ctx->ipxlat->cfg_lock);
+ return ret;
+}
+
+/**
+ * ipxlat_nl_register - perform any needed registration in the netlink subsystem
+ *
+ * Return: 0 on success, a negative error code otherwise
+ */
+int __init ipxlat_nl_register(void)
+{
+ return genl_register_family(&ipxlat_nl_family);
+}
+
+/**
+ * ipxlat_nl_unregister - undo any module wide netlink registration
+ */
+void ipxlat_nl_unregister(void)
+{
+ genl_unregister_family(&ipxlat_nl_family);
+}
diff --git a/drivers/net/ipxlat/netlink.h b/drivers/net/ipxlat/netlink.h
new file mode 100644
index 000000000000..1ea292ad9964
--- /dev/null
+++ b/drivers/net/ipxlat/netlink.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* IPXLAT - Stateless IP/ICMP Translation (SIIT) virtual device driver
+ *
+ * Copyright (C) 2026- Mandelbit SRL
+ * Copyright (C) 2026- Daniel Gröber <dxld@darkboxed.org>
+ *
+ * Author: Antonio Quartulli <antonio@mandelbit.com>
+ * Daniel Gröber <dxld@darkboxed.org>
+ * Ralf Lici <ralf@mandelbit.com>
+ */
+
+#ifndef _NET_IPXLAT_NETLINK_H_
+#define _NET_IPXLAT_NETLINK_H_
+
+/**
+ * ipxlat_nl_register - register ipxlat generic-netlink family
+ *
+ * Return: 0 on success, negative errno on registration failure.
+ */
+int ipxlat_nl_register(void);
+
+/**
+ * ipxlat_nl_unregister - unregister ipxlat generic-netlink family
+ */
+void ipxlat_nl_unregister(void);
+
+#endif /* _NET_IPXLAT_NETLINK_H_ */
diff --git a/drivers/net/ipxlat/translate_46.c b/drivers/net/ipxlat/translate_46.c
index 0b79ca07c771..d625dc85576b 100644
--- a/drivers/net/ipxlat/translate_46.c
+++ b/drivers/net/ipxlat/translate_46.c
@@ -14,6 +14,7 @@
#include <net/ip6_route.h>
#include "address.h"
+#include "icmp.h"
#include "packet.h"
#include "transport.h"
#include "translate_46.h"
@@ -239,7 +240,7 @@ int ipxlat_46_translate(struct ipxlat_priv *ipxlat, struct sk_buff *skb)
err = ipxlat_46_outer_udp(skb, &outer4);
break;
case IPPROTO_ICMP:
- err = -EPROTONOSUPPORT;
+ err = ipxlat_46_icmp(ipxlat, skb);
break;
default:
err = 0;
diff --git a/include/uapi/linux/ipxlat.h b/include/uapi/linux/ipxlat.h
new file mode 100644
index 000000000000..f8db3df3f9e8
--- /dev/null
+++ b/include/uapi/linux/ipxlat.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+/* Do not edit directly, auto-generated from: */
+/* Documentation/netlink/specs/ipxlat.yaml */
+/* YNL-GEN uapi header */
+/* To regenerate run: tools/net/ynl/ynl-regen.sh */
+
+#ifndef _UAPI_LINUX_IPXLAT_H
+#define _UAPI_LINUX_IPXLAT_H
+
+#define IPXLAT_FAMILY_NAME "ipxlat"
+#define IPXLAT_FAMILY_VERSION 1
+
+#define IPXLAT_XLAT_PREFIX6_MAX_PREFIX_LEN 96
+
+enum {
+ IPXLAT_A_POOL_PREFIX = 1,
+ IPXLAT_A_POOL_PREFIX_LEN,
+
+ __IPXLAT_A_POOL_MAX,
+ IPXLAT_A_POOL_MAX = (__IPXLAT_A_POOL_MAX - 1)
+};
+
+enum {
+ IPXLAT_A_CFG_XLAT_PREFIX6 = 1,
+ IPXLAT_A_CFG_LOWEST_IPV6_MTU,
+
+ __IPXLAT_A_CFG_MAX,
+ IPXLAT_A_CFG_MAX = (__IPXLAT_A_CFG_MAX - 1)
+};
+
+enum {
+ IPXLAT_A_DEV_IFINDEX = 1,
+ IPXLAT_A_DEV_NETNSID,
+ IPXLAT_A_DEV_CONFIG,
+
+ __IPXLAT_A_DEV_MAX,
+ IPXLAT_A_DEV_MAX = (__IPXLAT_A_DEV_MAX - 1)
+};
+
+enum {
+ IPXLAT_CMD_DEV_GET = 1,
+ IPXLAT_CMD_DEV_SET,
+
+ __IPXLAT_CMD_MAX,
+ IPXLAT_CMD_MAX = (__IPXLAT_CMD_MAX - 1)
+};
+
+#endif /* _UAPI_LINUX_IPXLAT_H */
--
2.53.0
next prev parent reply other threads:[~2026-03-19 15:22 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-19 15:12 [RFC net-next 00/15] Introducing ipxlat: a stateless IPv4/IPv6 translation device Ralf Lici
2026-03-19 15:12 ` [RFC net-next 01/15] drivers/net: add ipxlat netdevice skeleton and build plumbing Ralf Lici
2026-03-19 15:12 ` [RFC net-next 02/15] ipxlat: add RFC 6052 address conversion helpers Ralf Lici
2026-03-19 15:12 ` [RFC net-next 03/15] ipxlat: add packet metadata control block helpers Ralf Lici
2026-03-19 15:12 ` [RFC net-next 04/15] ipxlat: add IPv4 packet validation path Ralf Lici
2026-03-19 15:12 ` [RFC net-next 05/15] ipxlat: add IPv6 " Ralf Lici
2026-03-19 15:12 ` [RFC net-next 06/15] ipxlat: add transport checksum and offload helpers Ralf Lici
2026-03-19 15:12 ` [RFC net-next 07/15] ipxlat: add 4to6 and 6to4 TCP/UDP translation helpers Ralf Lici
2026-03-19 15:12 ` [RFC net-next 08/15] ipxlat: add translation engine and dispatch core Ralf Lici
2026-03-19 15:12 ` [RFC net-next 09/15] ipxlat: emit translator-generated ICMP errors on drop Ralf Lici
2026-03-19 15:12 ` [RFC net-next 10/15] ipxlat: add 4to6 pre-fragmentation path Ralf Lici
2026-03-19 15:12 ` [RFC net-next 11/15] ipxlat: add ICMP informational translation paths Ralf Lici
2026-03-19 15:12 ` [RFC net-next 12/15] ipxlat: add ICMP error translation and quoted-inner handling Ralf Lici
2026-03-19 15:12 ` Ralf Lici [this message]
2026-03-19 15:12 ` [RFC net-next 14/15] selftests: net: add ipxlat coverage Ralf Lici
2026-03-19 15:12 ` [RFC net-next 15/15] Documentation: networking: add ipxlat translator guide Ralf Lici
2026-03-19 22:11 ` Jonathan Corbet
2026-03-24 9:55 ` Ralf Lici
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260319151230.655687-14-ralf@mandelbit.com \
--to=ralf@mandelbit.com \
--cc=andrew+netdev@lunn.ch \
--cc=antonio@mandelbit.com \
--cc=davem@davemloft.net \
--cc=donald.hunter@gmail.com \
--cc=dxld@darkboxed.org \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox