public inbox for netdev@vger.kernel.org
 help / color / mirror / Atom feed
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


  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