[RFC net-next 02/15] ipxlat: add RFC 6052 address conversion helpers

From: Ralf Lici

Date: Thu Mar 19 2026 - 11:23:14 EST


Introduce IPv4/IPv6 stateless address mapping helpers used by the
translation pipeline. Add the core 4<->6 conversion routines, including
RFC 6052 prefix embedding/extraction and the RFC 6791 fallback source
selection logic used by ICMP translation paths.

Signed-off-by: Ralf Lici <ralf@xxxxxxxxxxxxx>
---
drivers/net/ipxlat/Makefile | 1 +
drivers/net/ipxlat/address.c | 132 +++++++++++++++++++++++++++++++++++
drivers/net/ipxlat/address.h | 59 ++++++++++++++++
3 files changed, 192 insertions(+)
create mode 100644 drivers/net/ipxlat/address.c
create mode 100644 drivers/net/ipxlat/address.h

diff --git a/drivers/net/ipxlat/Makefile b/drivers/net/ipxlat/Makefile
index bd48c2700bf5..b6367dedd78e 100644
--- a/drivers/net/ipxlat/Makefile
+++ b/drivers/net/ipxlat/Makefile
@@ -5,3 +5,4 @@
obj-$(CONFIG_IPXLAT) := ipxlat.o

ipxlat-objs += main.o
+ipxlat-objs += address.o
diff --git a/drivers/net/ipxlat/address.c b/drivers/net/ipxlat/address.c
new file mode 100644
index 000000000000..d1a2b7d1768f
--- /dev/null
+++ b/drivers/net/ipxlat/address.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0
+/* IPXLAT - Stateless IP/ICMP Translation (SIIT) virtual device driver
+ *
+ * Copyright (C) 2024- Alberto Leiva Popper <ydahhrk@xxxxxxxxx>
+ * Copyright (C) 2026- Mandelbit SRL
+ * Copyright (C) 2026- Daniel Gröber <dxld@xxxxxxxxxxxxx>
+ *
+ * Author: Alberto Leiva Popper <ydahhrk@xxxxxxxxx>
+ * Antonio Quartulli <antonio@xxxxxxxxxxxxx>
+ * Daniel Gröber <dxld@xxxxxxxxxxxxx>
+ * Ralf Lici <ralf@xxxxxxxxxxxxx>
+ */
+
+#include "address.h"
+
+static bool ipxlat_prefix6_contains(const struct ipv6_prefix *prefix,
+ const struct in6_addr *addr)
+{
+ return ipv6_prefix_equal(&prefix->addr, addr, prefix->len);
+}
+
+static __be32 ipxlat_64_extract_addr(const struct in6_addr *src,
+ unsigned int q1, unsigned int q2,
+ unsigned int q3, unsigned int q4)
+{
+ q1 = src->s6_addr[q1];
+ q2 = src->s6_addr[q2];
+ q3 = src->s6_addr[q3];
+ q4 = src->s6_addr[q4];
+ return htonl((q1 << 24) | (q2 << 16) | (q3 << 8) | q4);
+}
+
+static void ipxlat_46_embed_addr(__be32 __src, struct in6_addr *dst,
+ unsigned int q1, unsigned int q2,
+ unsigned int q3, unsigned int q4)
+{
+ u32 src = ntohl(__src);
+
+ dst->s6_addr[q1] = ((src >> 24) & 0xFF);
+ dst->s6_addr[q2] = ((src >> 16) & 0xFF);
+ dst->s6_addr[q3] = ((src >> 8) & 0xFF);
+ dst->s6_addr[q4] = ((src) & 0xFF);
+}
+
+void ipxlat_46_convert_addr(const struct ipv6_prefix *xlat_prefix6,
+ __be32 addr4, struct in6_addr *addr6)
+{
+ *addr6 = xlat_prefix6->addr;
+
+ switch (xlat_prefix6->len) {
+ case 96:
+ addr6->s6_addr32[3] = addr4;
+ return;
+ case 64:
+ ipxlat_46_embed_addr(addr4, addr6, 9, 10, 11, 12);
+ return;
+ case 56:
+ ipxlat_46_embed_addr(addr4, addr6, 7, 9, 10, 11);
+ return;
+ case 48:
+ ipxlat_46_embed_addr(addr4, addr6, 6, 7, 9, 10);
+ return;
+ case 40:
+ ipxlat_46_embed_addr(addr4, addr6, 5, 6, 7, 9);
+ return;
+ case 32:
+ addr6->s6_addr32[1] = addr4;
+ return;
+ }
+
+ DEBUG_NET_WARN_ON_ONCE(1);
+}
+
+int ipxlat_64_convert_addrs(const struct ipv6_prefix *xlat_prefix6,
+ const struct ipv6hdr *hdr6, bool icmp_err,
+ __be32 *src, __be32 *dst)
+{
+ bool src_ok;
+
+ src_ok = ipxlat_prefix6_contains(xlat_prefix6, &hdr6->saddr);
+ if (unlikely(!src_ok && !icmp_err))
+ return -EINVAL;
+ if (unlikely(!ipxlat_prefix6_contains(xlat_prefix6, &hdr6->daddr)))
+ return -EINVAL;
+
+ switch (xlat_prefix6->len) {
+ case 96:
+ if (likely(src_ok))
+ *src = hdr6->saddr.s6_addr32[3];
+ *dst = hdr6->daddr.s6_addr32[3];
+ break;
+ case 64:
+ if (likely(src_ok))
+ *src = ipxlat_64_extract_addr(&hdr6->saddr, 9, 10, 11,
+ 12);
+ *dst = ipxlat_64_extract_addr(&hdr6->daddr, 9, 10, 11, 12);
+ break;
+ case 56:
+ if (likely(src_ok))
+ *src = ipxlat_64_extract_addr(&hdr6->saddr, 7,
+ 9, 10, 11);
+ *dst = ipxlat_64_extract_addr(&hdr6->daddr, 7, 9, 10, 11);
+ break;
+ case 48:
+ if (likely(src_ok))
+ *src = ipxlat_64_extract_addr(&hdr6->saddr, 6,
+ 7, 9, 10);
+ *dst = ipxlat_64_extract_addr(&hdr6->daddr, 6, 7, 9, 10);
+ break;
+ case 40:
+ if (likely(src_ok))
+ *src = ipxlat_64_extract_addr(&hdr6->saddr, 5, 6, 7, 9);
+ *dst = ipxlat_64_extract_addr(&hdr6->daddr, 5, 6, 7, 9);
+ break;
+ case 32:
+ if (likely(src_ok))
+ *src = hdr6->saddr.s6_addr32[1];
+ *dst = hdr6->daddr.s6_addr32[1];
+ break;
+ default:
+ DEBUG_NET_WARN_ON_ONCE(1);
+ return -EINVAL;
+ }
+
+ /* keep 6->4 ICMP error translation functional even when the ICMPv6
+ * source is not xlat_prefix6-mapped (for example, stack-generated PTB)
+ */
+ if (unlikely(!src_ok))
+ *src = htonl(INADDR_DUMMY);
+
+ return 0;
+}
diff --git a/drivers/net/ipxlat/address.h b/drivers/net/ipxlat/address.h
new file mode 100644
index 000000000000..4283fdddac56
--- /dev/null
+++ b/drivers/net/ipxlat/address.h
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* IPXLAT - Stateless IP/ICMP Translation (SIIT) virtual device driver
+ *
+ * Copyright (C) 2024- Alberto Leiva Popper <ydahhrk@xxxxxxxxx>
+ * Copyright (C) 2026- Mandelbit SRL
+ * Copyright (C) 2026- Daniel Gröber <dxld@xxxxxxxxxxxxx>
+ *
+ * Author: Alberto Leiva Popper <ydahhrk@xxxxxxxxx>
+ * Antonio Quartulli <antonio@xxxxxxxxxxxxx>
+ * Daniel Gröber <dxld@xxxxxxxxxxxxx>
+ * Ralf Lici <ralf@xxxxxxxxxxxxx>
+ */
+
+#ifndef _NET_IPXLAT_ADDRESS_H_
+#define _NET_IPXLAT_ADDRESS_H_
+
+#include <linux/ip.h>
+#include <net/ipv6.h>
+
+#include "ipxlpriv.h"
+
+/**
+ * ipxlat_46_convert_addr - translate one IPv4 address into RFC 6052 IPv6 form
+ * @xlat_prefix6: configured RFC 6052 prefix
+ * @addr4: IPv4 address to convert
+ * @addr6: output IPv6 address
+ */
+void ipxlat_46_convert_addr(const struct ipv6_prefix *xlat_prefix6,
+ __be32 addr4, struct in6_addr *addr6);
+
+/**
+ * ipxlat_64_convert_addrs - translate outer IPv6 endpoints into IPv4 pair
+ * @xlat_prefix6: configured RFC 6052 prefix
+ * @hdr6: source IPv6 header
+ * @icmp_err: source packet is ICMPv6 error
+ * @src: output IPv4 source address
+ * @dst: output IPv4 destination address
+ *
+ * Return: 0 on success, negative errno on non-translatable addresses.
+ */
+int ipxlat_64_convert_addrs(const struct ipv6_prefix *xlat_prefix6,
+ const struct ipv6hdr *hdr6, bool icmp_err,
+ __be32 *src, __be32 *dst);
+
+/**
+ * ipxlat_46_convert_addrs - translate outer IPv4 endpoints into IPv6 pair
+ * @xlat_prefix6: configured RFC 6052 prefix
+ * @iph4: source IPv4 header
+ * @iph6: output IPv6 header (only saddr/daddr are updated)
+ */
+static inline void
+ipxlat_46_convert_addrs(const struct ipv6_prefix *xlat_prefix6,
+ const struct iphdr *iph4, struct ipv6hdr *iph6)
+{
+ ipxlat_46_convert_addr(xlat_prefix6, iph4->saddr, &iph6->saddr);
+ ipxlat_46_convert_addr(xlat_prefix6, iph4->daddr, &iph6->daddr);
+}
+
+#endif /* _NET_IPXLAT_ADDRESS_H_ */
--
2.53.0