[RFC net-next 09/15] ipxlat: emit translator-generated ICMP errors on drop

Ralf Lici posted 15 patches 2 weeks, 3 days ago
[RFC net-next 09/15] ipxlat: emit translator-generated ICMP errors on drop
Posted by Ralf Lici 2 weeks, 3 days ago
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@mandelbit.com>
---
 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@mandelbit.com>
  */
 
+#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), &param);
+}
+
+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, &param);
+}
+
+/* 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