[RFC net-next v2 07/18] net: ipxlat: add packet metadata and header helpers
From: Ralf Lici
Date: Sat Oct 24 2026 - 05:31:48 EST
Define the skb control block used while a packet is prepared, together
with offset, length, fragment and pending-error helpers shared by
the two protocol families.
Signed-off-by: Ralf Lici <ralf@xxxxxxxxxxxxx>
---
Changes since RFC v1 https://lore.kernel.org/netdev/20260319151230.655687-4-ralf@xxxxxxxxxxxxx/
- Reshape the v1 control block for the prepare-translate engine contract,
remove netdevice actions and continuation policy from its metadata,
and split family-specific parsing into packet4.c and packet6.c.
- Make headers which expose IPv6 packet and Fragment Header types
include the declarations they use. (Sashiko)
net/ipxlat/packet.h | 243 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 243 insertions(+)
create mode 100644 net/ipxlat/packet.h
diff --git a/net/ipxlat/packet.h b/net/ipxlat/packet.h
new file mode 100644
index 000000000000..463f1ef027d0
--- /dev/null
+++ b/net/ipxlat/packet.h
@@ -0,0 +1,243 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* IPXLAT - Stateless IP/ICMP Translation (SIIT) engine
+ *
+ * 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_PACKET_H_
+#define _NET_IPXLAT_PACKET_H_
+
+#include <net/ip.h>
+#include <net/ipv6.h>
+
+#include <net/ipxlat.h>
+
+/**
+ * struct ipxlat_cb - per-skb parser and control metadata stored in skb->cb
+ * @l4_off: outer L4 header offset
+ * @payload_off: outer payload offset
+ * @fragh_off: outer IPv6 Fragment Header offset, or 0 if absent
+ * @inner_l3_offset: quoted inner L3 offset for ICMP errors
+ * @inner_l4_offset: quoted inner L4 offset for ICMP errors
+ * @inner_fragh_off: quoted inner IPv6 Fragment Header offset, or 0
+ * @l4_proto: outer L4 protocol (or nexthdr for IPv6)
+ * @inner_l4_proto: quoted inner L4 protocol
+ * @l3_hdr_len: outer L3 header length including extension headers
+ * @is_icmp_err: packet is ICMP error and carries quoted inner packet
+ * @emit_icmp_err: datapath must emit translator-generated ICMP on drop
+ * @saddr4: translated outer IPv4 source address for 6->4
+ * @daddr4: translated outer IPv4 destination address for 6->4
+ * @inner_saddr4: translated quoted IPv4 source address for ICMP 6->4
+ * @inner_daddr4: translated quoted IPv4 destination address for ICMP 6->4
+ * @pending_icmp_err: ICMP type/code/info cached for deferred emission
+ * @pending_icmp_err.type: ICMP type to emit
+ * @pending_icmp_err.code: ICMP code to emit
+ * @pending_icmp_err.info: ICMP auxiliary info (e.g. pointer/MTU)
+ */
+struct ipxlat_cb {
+ u16 l4_off;
+ u16 payload_off;
+ u16 fragh_off;
+ u16 inner_l3_offset;
+ u16 inner_l4_offset;
+ u16 inner_fragh_off;
+ u16 l3_hdr_len;
+ u8 l4_proto;
+ u8 inner_l4_proto;
+ bool is_icmp_err;
+ bool emit_icmp_err;
+ __be32 saddr4;
+ __be32 daddr4;
+ __be32 inner_saddr4;
+ __be32 inner_daddr4;
+ struct {
+ u8 type;
+ u8 code;
+ u32 info;
+ } pending_icmp_err;
+};
+
+static inline struct ipxlat_cb *ipxlat_skb_cb(const struct sk_buff *skb)
+{
+ BUILD_BUG_ON(sizeof(struct ipxlat_cb) > sizeof(skb->cb));
+ return (struct ipxlat_cb *)(skb->cb);
+}
+
+static inline unsigned int ipxlat_skb_l4_len(const struct sk_buff *skb)
+{
+ return skb->len - skb_transport_offset(skb);
+}
+
+/* return the L4 length of one eventual segment rather than the GSO aggregate */
+static inline unsigned int ipxlat_skb_l4_seg_len(const struct sk_buff *skb,
+ const struct ipxlat_cb *cb)
+{
+ const struct skb_shared_info *shinfo;
+
+ if (skb_is_gso(skb)) {
+ shinfo = skb_shinfo(skb);
+ /* legacy UFO's gso_size already includes the UDP header */
+ if (shinfo->gso_type & SKB_GSO_UDP)
+ return shinfo->gso_size;
+
+ /* TCP and UDP-L4 GSO sizes describe payload bytes only */
+ return cb->payload_off - cb->l4_off + shinfo->gso_size;
+ }
+ return skb->len - cb->l4_off;
+}
+
+static inline u8 ipxlat_get_ipv6_tclass(const struct ipv6hdr *hdr)
+{
+ return (hdr->priority << 4) | (hdr->flow_lbl[0] >> 4);
+}
+
+static inline u16 ipxlat_get_frag6_offset(const struct frag_hdr *hdr)
+{
+ return be16_to_cpu(hdr->frag_off) & IP6_OFFSET;
+}
+
+static inline u16 ipxlat_get_frag4_offset(const struct iphdr *hdr)
+{
+ return (be16_to_cpu(hdr->frag_off) & IP_OFFSET) << 3;
+}
+
+static inline bool ipxlat_is_first_frag6(const struct frag_hdr *hdr)
+{
+ return !hdr || ipxlat_get_frag6_offset(hdr) == 0;
+}
+
+static inline bool ipxlat_is_first_frag4(const struct iphdr *hdr)
+{
+ return !(hdr->frag_off & htons(IP_OFFSET));
+}
+
+/**
+ * ipxlat_replace_l3 - replace an outer or quoted L3 header in place
+ * @skb: packet containing the L3 header
+ * @old_l3_end: current offset immediately after the L3 header
+ * @new_l3_end: desired offset immediately after the L3 header
+ * @transport_off: transport header offset after replacement
+ *
+ * The caller must provide valid offsets and enough headroom. Bytes following
+ * @old_l3_end remain unchanged, and the network header is reset to @skb->data.
+ */
+static inline void ipxlat_replace_l3(struct sk_buff *skb,
+ unsigned int old_l3_end,
+ unsigned int new_l3_end,
+ unsigned int transport_off)
+{
+ skb_pull(skb, old_l3_end);
+ skb_push(skb, new_l3_end);
+ skb_reset_network_header(skb);
+ skb_set_transport_header(skb, transport_off);
+}
+
+/**
+ * ipxlat_cb_rebase_offsets - shift cached cb offsets after skb relayout
+ * @cb: parsed packet metadata
+ * @delta: signed byte delta applied to cached offsets
+ *
+ * All cached absolute offsets include the old outer L3 length. Apply the same
+ * delta to keep them pointing at the same logical fields after replacement.
+ */
+static inline void ipxlat_cb_rebase_offsets(struct ipxlat_cb *cb, int delta)
+{
+ cb->l4_off += delta;
+ cb->payload_off += delta;
+
+ if (unlikely(cb->is_icmp_err)) {
+ cb->inner_l3_offset += delta;
+ cb->inner_l4_offset += delta;
+
+ if (cb->inner_fragh_off)
+ cb->inner_fragh_off += delta;
+ }
+}
+
+#ifdef CONFIG_DEBUG_NET
+/**
+ * ipxlat_cb_offsets_valid - validate monotonicity and bounds of cb offsets
+ * @cb: parsed packet metadata
+ *
+ * Checks that L4 precedes payload, quoted L3 follows the outer payload and a
+ * quoted Fragment Header lies inside the quoted L3 area before quoted L4.
+ *
+ * Return: true if cached offsets are internally consistent.
+ */
+static inline bool ipxlat_cb_offsets_valid(const struct ipxlat_cb *cb)
+{
+ if (unlikely(cb->payload_off < cb->l4_off))
+ return false;
+
+ if (cb->is_icmp_err) {
+ if (unlikely(cb->inner_l3_offset < cb->payload_off))
+ return false;
+ if (unlikely(cb->inner_l4_offset < cb->inner_l3_offset))
+ return false;
+ if (unlikely(cb->inner_fragh_off &&
+ cb->inner_fragh_off < cb->inner_l3_offset))
+ return false;
+ if (unlikely(cb->inner_fragh_off &&
+ cb->inner_fragh_off >= cb->inner_l4_offset))
+ return false;
+ }
+
+ return true;
+}
+#else
+static inline bool ipxlat_cb_offsets_valid(const struct ipxlat_cb *cb)
+{
+ return true;
+}
+#endif
+
+/**
+ * ipxlat_mark_icmp_drop - record an ICMP error for a dropped packet
+ * @skb: packet carrying the translator metadata
+ * @type: ICMP or ICMPv6 type
+ * @code: ICMP or ICMPv6 code
+ * @info: ICMP or ICMPv6 auxiliary information
+ */
+static inline void ipxlat_mark_icmp_drop(struct sk_buff *skb, u8 type, u8 code,
+ u32 info)
+{
+ struct ipxlat_cb *cb = ipxlat_skb_cb(skb);
+
+ cb->emit_icmp_err = true;
+ cb->pending_icmp_err.type = type;
+ cb->pending_icmp_err.code = code;
+ cb->pending_icmp_err.info = info;
+}
+
+/**
+ * ipxlat_v4_validate_skb - validate and summarize IPv4 packet into skb->cb
+ * @skb: packet to validate
+ *
+ * Ensures required headers are present and consistent, populates
+ * &struct ipxlat_cb for subsequent 4->6 translation, and may mark a
+ * translator-generated ICMP action on failure paths.
+ *
+ * Return: 0 on success, negative errno on validation failure.
+ */
+int ipxlat_v4_validate_skb(struct sk_buff *skb);
+
+/**
+ * ipxlat_v6_validate_skb - validate and summarize IPv6 packet into skb->cb
+ * @skb: packet to validate
+ *
+ * Ensures required headers are present and consistent, populates
+ * &struct ipxlat_cb for subsequent 6->4 translation, and may mark a
+ * translator-generated ICMP action on failure paths.
+ *
+ * Return: 0 on success, negative errno on validation failure.
+ */
+int ipxlat_v6_validate_skb(struct sk_buff *skb);
+
+#endif /* _NET_IPXLAT_PACKET_H_ */
--
2.55.0