[RFC net-next 09/15] ipxlat: emit translator-generated ICMP errors on drop
From: Ralf Lici
Date: Thu Mar 19 2026 - 11:28:39 EST
When validation or policy requires dropping a packet and generating an
ICMP error, route that failure through explicit ICMP emission paths so
the sender can be notified where appropriate. This commit adds
translator-originated error generation for both directions and
integrates it into dispatch action handling without changing normal
forwarding behavior.
Signed-off-by: Ralf Lici <ralf@xxxxxxxxxxxxx>
---
drivers/net/ipxlat/dispatch.c | 66 ++++++++++++++++++++++++++++++++++-
drivers/net/ipxlat/dispatch.h | 7 ++++
drivers/net/ipxlat/packet.c | 25 ++++++++++---
3 files changed, 92 insertions(+), 6 deletions(-)
diff --git a/drivers/net/ipxlat/dispatch.c b/drivers/net/ipxlat/dispatch.c
index 133d30859f49..b8b9b930b04c 100644
--- a/drivers/net/ipxlat/dispatch.c
+++ b/drivers/net/ipxlat/dispatch.c
@@ -11,7 +11,12 @@
* Ralf Lici <ralf@xxxxxxxxxxxxx>
*/
+#include <linux/icmp.h>
+#include <linux/icmpv6.h>
+#include <net/icmp.h>
#include <net/ip.h>
+#include <net/route.h>
+#include <net/ipv6.h>
#include "dispatch.h"
#include "packet.h"
@@ -21,7 +26,8 @@
static enum ipxlat_action
ipxlat_resolve_failed_action(const struct sk_buff *skb)
{
- return IPXLAT_ACT_DROP;
+ return ipxlat_skb_cb(skb)->emit_icmp_err ? IPXLAT_ACT_ICMP_ERR :
+ IPXLAT_ACT_DROP;
}
enum ipxlat_action ipxlat_translate(struct ipxlat_priv *ipxlat,
@@ -61,6 +67,59 @@ void ipxlat_mark_icmp_drop(struct sk_buff *skb, u8 type, u8 code, u32 info)
cb->icmp_err.info = info;
}
+static void ipxlat_46_emit_icmp_err(struct ipxlat_priv *ipxlat,
+ struct sk_buff *inner)
+{
+ struct ipxlat_cb *cb = ipxlat_skb_cb(inner);
+ const struct iphdr *iph = ip_hdr(inner);
+ struct inet_skb_parm param = {};
+
+ /* build route metadata on demand when the packet has no dst */
+ if (unlikely(!skb_dst(inner))) {
+ const int reason = ip_route_input_noref(inner, iph->daddr,
+ iph->saddr,
+ ip4h_dscp(iph),
+ inner->dev);
+
+ if (unlikely(reason)) {
+ netdev_dbg(ipxlat->dev,
+ "icmp4 emit: route build failed reason=%d\n",
+ reason);
+ return;
+ }
+ }
+
+ /* emit the ICMPv4 error */
+ __icmp_send(inner, cb->icmp_err.type, cb->icmp_err.code,
+ htonl(cb->icmp_err.info), ¶m);
+}
+
+static void ipxlat_64_emit_icmp_err(struct sk_buff *inner)
+{
+ struct ipxlat_cb *cb = ipxlat_skb_cb(inner);
+ struct inet6_skb_parm param = {};
+
+ /* emit the ICMPv6 error */
+ icmp6_send(inner, cb->icmp_err.type, cb->icmp_err.code,
+ cb->icmp_err.info, NULL, ¶m);
+}
+
+/* emit translator-generated ICMP errors for packets rejected by RFC rules */
+void ipxlat_emit_icmp_error(struct ipxlat_priv *ipxlat, struct sk_buff *inner)
+{
+ switch (ntohs(inner->protocol)) {
+ case ETH_P_IPV6:
+ ipxlat_64_emit_icmp_err(inner);
+ return;
+ case ETH_P_IP:
+ ipxlat_46_emit_icmp_err(ipxlat, inner);
+ return;
+ default:
+ DEBUG_NET_WARN_ON_ONCE(1);
+ return;
+ }
+}
+
static void ipxlat_forward_pkt(struct ipxlat_priv *ipxlat, struct sk_buff *skb)
{
const unsigned int len = skb->len;
@@ -90,6 +149,11 @@ int ipxlat_process_skb(struct ipxlat_priv *ipxlat, struct sk_buff *skb,
dev_dstats_tx_add(ipxlat->dev, skb->len);
ipxlat_forward_pkt(ipxlat, skb);
return 0;
+ case IPXLAT_ACT_ICMP_ERR:
+ dev_dstats_tx_dropped(ipxlat->dev);
+ ipxlat_emit_icmp_error(ipxlat, skb);
+ consume_skb(skb);
+ return 0;
case IPXLAT_ACT_DROP:
goto drop_free;
default:
diff --git a/drivers/net/ipxlat/dispatch.h b/drivers/net/ipxlat/dispatch.h
index fa6fafea656b..73acd831b6cf 100644
--- a/drivers/net/ipxlat/dispatch.h
+++ b/drivers/net/ipxlat/dispatch.h
@@ -44,6 +44,13 @@ enum ipxlat_action {
*/
void ipxlat_mark_icmp_drop(struct sk_buff *skb, u8 type, u8 code, u32 info);
+/**
+ * ipxlat_emit_icmp_error - emit cached translator-generated ICMP error
+ * @ipxlat: translator private context
+ * @inner: offending packet used as quoted payload
+ */
+void ipxlat_emit_icmp_error(struct ipxlat_priv *ipxlat, struct sk_buff *inner);
+
/**
* ipxlat_translate - validate/translate one packet and return next action
* @ipxlat: translator private context
diff --git a/drivers/net/ipxlat/packet.c b/drivers/net/ipxlat/packet.c
index b37a3e55aff8..758b72bdc6f1 100644
--- a/drivers/net/ipxlat/packet.c
+++ b/drivers/net/ipxlat/packet.c
@@ -142,6 +142,8 @@ static int ipxlat_v4_srr_check(struct sk_buff *skb, const struct iphdr *hdr)
if (unlikely(ptr > len - 3))
return -EINVAL;
+ ipxlat_mark_icmp_drop(skb, ICMP_DEST_UNREACH,
+ ICMP_SR_FAILED, 0);
return -EINVAL;
}
@@ -272,8 +274,10 @@ static int ipxlat_v4_pull_hdrs(struct sk_buff *skb)
/* RFC 7915 Section 4.1 */
if (unlikely(ipxlat_v4_srr_check(skb, l3_hdr)))
return -EINVAL;
- if (unlikely(l3_hdr->ttl <= 1))
+ if (unlikely(l3_hdr->ttl <= 1)) {
+ ipxlat_mark_icmp_drop(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0);
return -EINVAL;
+ }
/* RFC 7915 Section 1.2:
* Fragmented ICMP/ICMPv6 packets will not be translated by IP/ICMP
@@ -390,8 +394,11 @@ int ipxlat_v4_validate_skb(struct ipxlat_priv *ipxlat, struct sk_buff *skb)
* Fragmented checksum-less IPv4 UDP is rejected because 4->6 cannot
* reliably translate it.
*/
- if (unlikely(ip_is_fragment(l3_hdr)))
+ if (unlikely(ip_is_fragment(l3_hdr))) {
+ ipxlat_mark_icmp_drop(skb, ICMP_DEST_UNREACH, ICMP_PKT_FILTERED,
+ 0);
return -EINVAL;
+ }
/* udph->len bounds the span used to compute replacement checksum */
if (unlikely(ntohs(udph->len) > skb->len - cb->l4_off))
@@ -520,7 +527,7 @@ static int ipxlat_v6_walk_hdrs(struct sk_buff *skb, unsigned int l3_offset,
*/
static int ipxlat_v6_check_rh(struct sk_buff *skb)
{
- unsigned int rh_off;
+ unsigned int rh_off, pointer;
int flags, nexthdr;
rh_off = 0;
@@ -531,6 +538,8 @@ static int ipxlat_v6_check_rh(struct sk_buff *skb)
if (likely(nexthdr != NEXTHDR_ROUTING))
return 0;
+ pointer = rh_off + offsetof(struct ipv6_rt_hdr, segments_left);
+ ipxlat_mark_icmp_drop(skb, ICMPV6_PARAMPROB, ICMPV6_HDR_FIELD, pointer);
return -EINVAL;
}
@@ -550,8 +559,11 @@ static int ipxlat_v6_pull_outer_l3(struct sk_buff *skb)
!ipxlat_v6_validate_saddr(&l3_hdr->saddr)))
return -EINVAL;
- if (unlikely(l3_hdr->hop_limit <= 1))
+ if (unlikely(l3_hdr->hop_limit <= 1)) {
+ ipxlat_mark_icmp_drop(skb, ICMPV6_TIME_EXCEED,
+ ICMPV6_EXC_HOPLIMIT, 0);
return -EINVAL;
+ }
return 0;
}
@@ -617,8 +629,11 @@ static int ipxlat_v6_pull_hdrs(struct sk_buff *skb)
/* -EPROTONOSUPPORT means packet layout is syntactically valid but
* unsupported by our RFC 7915 path
*/
- if (unlikely(err == -EPROTONOSUPPORT))
+ if (unlikely(err == -EPROTONOSUPPORT)) {
+ ipxlat_mark_icmp_drop(skb, ICMPV6_DEST_UNREACH,
+ ICMPV6_ADM_PROHIBITED, 0);
return -EINVAL;
+ }
if (unlikely(err))
return err;
--
2.53.0