From: Anton Danilov <littlesmilingcloud@gmail.com>
To: netdev@vger.kernel.org
Cc: "David S . Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@google.com>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
David Ahern <dsahern@kernel.org>, Simon Horman <horms@kernel.org>,
Shuah Khan <shuah@kernel.org>,
linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org
Subject: [PATCH net-next 08/11] ip_tunnel: add drop reasons to the transmit path
Date: Tue, 1 Sep 2026 00:51:34 +0300 [thread overview]
Message-ID: <20260831215137.549324-9-littlesmilingcloud@gmail.com> (raw)
In-Reply-To: <20260831215137.549324-1-littlesmilingcloud@gmail.com>
ip_tunnel_xmit() and ip_md_tunnel_xmit() encapsulate packets that the
tunnel forwards, and every failure on that path ends in the same plain
kfree_skb(). The device counters separate them a little, but they are
too coarse to act on: tx_errors alone covers an encapsulation failure, a
routing failure, a lookup loop and a packet that is simply too big.
The last one deserves attention. tnl_update_pmtu() returns -E2BIG for a
packet larger than the path MTU that has the DF bit set, after it has
already sent an ICMP fragmentation needed back to the sender. That is
path MTU discovery working as intended, yet it lands in tx_errors next
to genuine failures, so a MTU black hole cannot be told from a broken
route by looking at the counters.
No new reason is needed for most of it:
- SKB_DROP_REASON_PKT_TOO_BIG for the case above,
- SKB_DROP_REASON_IP_OUTNOROUTES when no route is found,
- SKB_DROP_REASON_RECURSION_LIMIT when the route points back at the
tunnel device itself, which is the "dead loop on virtual device" that
reason describes,
- SKB_DROP_REASON_NOMEM when the headroom cannot be expanded,
- SKB_DROP_REASON_NEIGH_CREATEFAIL when the NBMA neighbour lookup
fails, SKB_DROP_REASON_NO_TX_TARGET when no destination can be
derived at all, and SKB_DROP_REASON_UNHANDLED_PROTO for a payload
that is neither IPv4 nor IPv6,
- SKB_DROP_REASON_TUNNEL_TXINFO, which already documents a packet
reaching an external mode device without metadata, for the
collect_md path.
Only the encapsulation failure has no fitting reason, so add
SKB_DROP_REASON_IP_TUNNEL_ENCAP for it.
Drop reasons on transmit are not new: vxlan already reports several of
them from its xmit path, and ip_tunnel_core.c reports
SKB_DROP_REASON_RECURSION_LIMIT.
Assisted-by: Claude-Code:claude-opus-5
Signed-off-by: Anton Danilov <littlesmilingcloud@gmail.com>
---
include/net/dropreason-core.h | 7 ++++++
net/ipv4/ip_tunnel.c | 41 ++++++++++++++++++++++++++++-------
2 files changed, 40 insertions(+), 8 deletions(-)
diff --git a/include/net/dropreason-core.h b/include/net/dropreason-core.h
index d1fb52c1b0cb..2a2ac8767d68 100644
--- a/include/net/dropreason-core.h
+++ b/include/net/dropreason-core.h
@@ -133,6 +133,7 @@
FN(GRE_INVALID_HDR) \
FN(GRE_CSUM) \
FN(GRE_TUNNEL_NOT_FOUND) \
+ FN(IP_TUNNEL_ENCAP) \
FNe(MAX)
/**
@@ -637,6 +638,12 @@ enum skb_drop_reason {
* endpoints and the key the packet carries.
*/
SKB_DROP_REASON_GRE_TUNNEL_NOT_FOUND,
+ /**
+ * @SKB_DROP_REASON_IP_TUNNEL_ENCAP: failed to build the
+ * encapsulation header of a tunnel, e.g. an unknown or
+ * unregistered encapsulation type.
+ */
+ SKB_DROP_REASON_IP_TUNNEL_ENCAP,
/**
* @SKB_DROP_REASON_MAX: the maximum of core drop reasons, which
* shouldn't be used as a real 'reason' - only for tracing code gen
diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
index ab8bae8ba781..12d45f69c4d8 100644
--- a/net/ipv4/ip_tunnel.c
+++ b/net/ipv4/ip_tunnel.c
@@ -582,6 +582,7 @@ static int tnl_update_pmtu(struct net_device *dev, struct sk_buff *skb,
void ip_md_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
u8 proto, int tunnel_hlen)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct ip_tunnel *tunnel = netdev_priv(dev);
u32 headroom = sizeof(struct iphdr);
struct ip_tunnel_info *tun_info;
@@ -595,8 +596,10 @@ void ip_md_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
tun_info = skb_tunnel_info(skb);
if (unlikely(!tun_info || !(tun_info->mode & IP_TUNNEL_INFO_TX) ||
- ip_tunnel_info_af(tun_info) != AF_INET))
+ ip_tunnel_info_af(tun_info) != AF_INET)) {
+ reason = SKB_DROP_REASON_TUNNEL_TXINFO;
goto tx_error;
+ }
key = &tun_info->key;
memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
inner_iph = (const struct iphdr *)skb_inner_network_header(skb);
@@ -615,8 +618,10 @@ void ip_md_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
if (!tunnel_hlen)
tunnel_hlen = ip_encap_hlen(&tun_info->encap);
- if (ip_tunnel_encap(skb, &tun_info->encap, &proto, &fl4) < 0)
+ if (ip_tunnel_encap(skb, &tun_info->encap, &proto, &fl4) < 0) {
+ reason = SKB_DROP_REASON_IP_TUNNEL_ENCAP;
goto tx_error;
+ }
use_cache = ip_tunnel_dst_cache_usable(skb, tun_info);
if (use_cache)
@@ -625,6 +630,7 @@ void ip_md_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
rt = ip_route_output_key(tunnel->net, &fl4);
if (IS_ERR(rt)) {
DEV_STATS_INC(dev, tx_carrier_errors);
+ reason = SKB_DROP_REASON_IP_OUTNOROUTES;
goto tx_error;
}
if (use_cache)
@@ -634,6 +640,7 @@ void ip_md_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
if (rt->dst.dev == dev) {
ip_rt_put(rt);
DEV_STATS_INC(dev, collisions);
+ reason = SKB_DROP_REASON_RECURSION_LIMIT;
goto tx_error;
}
@@ -642,6 +649,7 @@ void ip_md_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
if (tnl_update_pmtu(dev, skb, rt, df, inner_iph, tunnel_hlen,
key->u.ipv4.dst, true)) {
ip_rt_put(rt);
+ reason = SKB_DROP_REASON_PKT_TOO_BIG;
goto tx_error;
}
@@ -659,6 +667,7 @@ void ip_md_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
headroom += LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len;
if (skb_cow_head(skb, headroom)) {
ip_rt_put(rt);
+ reason = SKB_DROP_REASON_NOMEM;
goto tx_dropped;
}
@@ -673,13 +682,14 @@ void ip_md_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
tx_dropped:
DEV_STATS_INC(dev, tx_dropped);
kfree:
- kfree_skb(skb);
+ kfree_skb_reason(skb, reason);
}
EXPORT_SYMBOL_GPL(ip_md_tunnel_xmit);
void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
const struct iphdr *tnl_params, u8 protocol)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct ip_tunnel *tunnel = netdev_priv(dev);
struct ip_tunnel_info *tun_info = NULL;
const struct iphdr *inner_iph;
@@ -707,9 +717,15 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
if (!skb_dst(skb)) {
DEV_STATS_INC(dev, tx_fifo_errors);
+ reason = SKB_DROP_REASON_NO_TX_TARGET;
goto tx_error;
}
+ /* Only the branches below can derive a destination. If
+ * none of them matches, the payload protocol is not one
+ * this tunnel can carry.
+ */
+ reason = SKB_DROP_REASON_UNHANDLED_PROTO;
tun_info = skb_tunnel_info(skb);
if (tun_info && (tun_info->mode & IP_TUNNEL_INFO_TX) &&
ip_tunnel_info_af(tun_info) == AF_INET &&
@@ -730,8 +746,10 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
neigh = dst_neigh_lookup(skb_dst(skb),
&ipv6_hdr(skb)->daddr);
- if (!neigh)
+ if (!neigh) {
+ reason = SKB_DROP_REASON_NEIGH_CREATEFAIL;
goto tx_error;
+ }
addr6 = (const struct in6_addr *)&neigh->primary_key;
addr_type = ipv6_addr_type(addr6);
@@ -748,8 +766,10 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
dst = addr6->s6_addr32[3];
}
neigh_release(neigh);
- if (do_tx_error_icmp)
+ if (do_tx_error_icmp) {
+ reason = SKB_DROP_REASON_NO_TX_TARGET;
goto tx_error_icmp;
+ }
}
#endif
else
@@ -776,8 +796,10 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
tunnel->net, READ_ONCE(tunnel->parms.link),
tunnel->fwmark, skb_get_hash(skb), 0);
- if (ip_tunnel_encap(skb, &tunnel->encap, &protocol, &fl4) < 0)
+ if (ip_tunnel_encap(skb, &tunnel->encap, &protocol, &fl4) < 0) {
+ reason = SKB_DROP_REASON_IP_TUNNEL_ENCAP;
goto tx_error;
+ }
if (connected && md) {
use_cache = ip_tunnel_dst_cache_usable(skb, tun_info);
@@ -794,6 +816,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
if (IS_ERR(rt)) {
DEV_STATS_INC(dev, tx_carrier_errors);
+ reason = SKB_DROP_REASON_IP_OUTNOROUTES;
goto tx_error;
}
if (use_cache)
@@ -807,6 +830,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
if (rt->dst.dev == dev) {
ip_rt_put(rt);
DEV_STATS_INC(dev, collisions);
+ reason = SKB_DROP_REASON_RECURSION_LIMIT;
goto tx_error;
}
@@ -816,6 +840,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
if (tnl_update_pmtu(dev, skb, rt, df, inner_iph, 0, 0, false)) {
ip_rt_put(rt);
+ reason = SKB_DROP_REASON_PKT_TOO_BIG;
goto tx_error;
}
@@ -850,7 +875,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
if (skb_cow_head(skb, max_headroom)) {
ip_rt_put(rt);
DEV_STATS_INC(dev, tx_dropped);
- kfree_skb(skb);
+ kfree_skb_reason(skb, SKB_DROP_REASON_NOMEM);
return;
}
@@ -866,7 +891,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
#endif
tx_error:
DEV_STATS_INC(dev, tx_errors);
- kfree_skb(skb);
+ kfree_skb_reason(skb, reason);
}
EXPORT_SYMBOL_GPL(ip_tunnel_xmit);
--
2.47.3
next prev parent reply other threads:[~2026-08-31 21:52 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-31 21:51 [PATCH net-next 00/11] tunnels: add core and gre drop reasons Anton Danilov
2026-08-31 21:51 ` [PATCH net-next 01/11] ip_tunnel: add drop reasons to the generic RX path Anton Danilov
2026-09-03 1:47 ` Jakub Kicinski
2026-09-03 1:47 ` Jakub Kicinski
2026-08-31 21:51 ` [PATCH net-next 02/11] ip6_tunnel: " Anton Danilov
2026-08-31 21:51 ` [PATCH net-next 03/11] selftests: net: add a test for the tunnel RX drop reasons Anton Danilov
2026-09-03 1:45 ` Jakub Kicinski
2026-08-31 21:51 ` [PATCH net-next 04/11] gre: make gre_parse_header() report a drop reason Anton Danilov
2026-08-31 21:51 ` [PATCH net-next 05/11] ip_gre: add drop reasons to the RX path Anton Danilov
2026-08-31 21:51 ` [PATCH net-next 06/11] ip6_gre: " Anton Danilov
2026-08-31 21:51 ` [PATCH net-next 07/11] selftests: net: cover the GRE specific drop reasons Anton Danilov
2026-08-31 21:51 ` Anton Danilov [this message]
2026-08-31 21:51 ` [PATCH net-next 09/11] ip_gre: add drop reasons to the transmit path Anton Danilov
2026-08-31 21:51 ` [PATCH net-next 10/11] ip6_tunnel: " Anton Danilov
2026-09-03 1:43 ` Jakub Kicinski
2026-08-31 21:51 ` [PATCH net-next 11/11] selftests: net: cover the tunnel transmit drop reasons Anton Danilov
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=20260831215137.549324-9-littlesmilingcloud@gmail.com \
--to=littlesmilingcloud@gmail.com \
--cc=davem@davemloft.net \
--cc=dsahern@kernel.org \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=shuah@kernel.org \
/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