From: Anton Danilov <littlesmilingcloud@gmail.com>
To: netdev@vger.kernel.org
Cc: "David S . Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@kernel.org>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
Simon Horman <horms@kernel.org>, David Ahern <dsahern@kernel.org>,
Ido Schimmel <idosch@nvidia.com>,
linux-kernel@vger.kernel.org
Subject: [PATCH net-next v6 7/8] ip_tunnel: add drop reasons to the transmit path
Date: Tue, 10 Nov 2026 23:20:20 +0300 [thread overview]
Message-ID: <20261110202021.821224-8-littlesmilingcloud@gmail.com> (raw)
In-Reply-To: <20261110202021.821224-1-littlesmilingcloud@gmail.com>
ip_tunnel_xmit() and ip_md_tunnel_xmit() encapsulate the packets sent
through the tunnel, and every failure these two functions detect ends in
a plain kfree_skb(). The device counters separate them a little, but
they are too coarse to act on: tx_errors counts an encapsulation
failure, a routing failure, a routing loop and a packet that is simply
too big alike.
The packet that is too big deserves attention. tnl_update_pmtu()
returns -E2BIG for a non-GSO packet larger than the MTU if it is IPv4
with the DF bit set, or IPv6 and the MTU is at least IPV6_MIN_MTU, after
it has already sent the ICMP error back to the sender. That is path MTU
discovery working as intended, yet among the device counters the drop
only bumps tx_errors, like a failed encapsulation and a few other
failures do. If the ICMP error never reaches the sender, the resulting
MTU black hole cannot be told from those by the device counters. A
failed route lookup and a routing loop do have counters of their own,
tx_carrier_errors and collisions, but in both functions all of these
packets end up in the same kfree_skb() call.
No new reason is needed:
- SKB_DROP_REASON_PKT_TOO_BIG for the case above,
- SKB_DROP_REASON_IP_OUTNOROUTES when the route lookup fails,
- SKB_DROP_REASON_RECURSION_LIMIT when the route points back at the
tunnel device itself, the case its description gives as an example,
- SKB_DROP_REASON_NOMEM when the headroom cannot be expanded,
- SKB_DROP_REASON_NEIGH_CREATEFAIL when the NBMA neighbour lookup
fails, and SKB_DROP_REASON_NO_TX_TARGET when no destination can be
derived at all, which includes 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,
- the reason ip_tunnel_encap() returns since the previous patch, when
the encapsulation header cannot be built.
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. They are most useful for forwarded
packets, which is what a tunnel gateway mostly transmits: the sender is
another host, which gets an ICMP error for only some of these failures,
so the drop has to be explained on the gateway.
Signed-off-by: Anton Danilov <littlesmilingcloud@gmail.com>
---
net/ipv4/ip_tunnel.c | 42 ++++++++++++++++++++++++++++++------------
1 file changed, 30 insertions(+), 12 deletions(-)
diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
index f3c5d8424925..924d02d0cc7b 100644
--- a/net/ipv4/ip_tunnel.c
+++ b/net/ipv4/ip_tunnel.c
@@ -586,6 +586,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;
@@ -599,8 +600,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);
@@ -619,7 +622,8 @@ 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))
+ reason = ip_tunnel_encap(skb, &tun_info->encap, &proto, &fl4);
+ if (reason)
goto tx_error;
use_cache = ip_tunnel_dst_cache_usable(skb, tun_info);
@@ -629,6 +633,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)
@@ -638,6 +643,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;
}
@@ -646,6 +652,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;
}
@@ -663,6 +670,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;
}
@@ -677,13 +685,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;
@@ -711,6 +720,7 @@ 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;
}
@@ -724,9 +734,8 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
} else if (payload_protocol == htons(ETH_P_IP)) {
rt = skb_rtable(skb);
dst = rt_nexthop(rt, inner_iph->daddr);
- }
#if IS_ENABLED(CONFIG_IPV6)
- else if (payload_protocol == htons(ETH_P_IPV6)) {
+ } else if (payload_protocol == htons(ETH_P_IPV6)) {
const struct in6_addr *addr6;
struct neighbour *neigh;
bool do_tx_error_icmp;
@@ -734,8 +743,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);
@@ -752,12 +763,15 @@ 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
+ } else {
+ reason = SKB_DROP_REASON_NO_TX_TARGET;
goto tx_error;
+ }
if (!md)
connected = false;
@@ -780,7 +794,8 @@ 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))
+ reason = ip_tunnel_encap(skb, &tunnel->encap, &protocol, &fl4);
+ if (reason)
goto tx_error;
if (connected && md) {
@@ -798,6 +813,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)
@@ -811,6 +827,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;
}
@@ -820,6 +837,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;
}
@@ -854,7 +872,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;
}
@@ -870,7 +888,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-11-10 20:20 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-10 20:20 [PATCH net-next v6 0/8] tunnels: add core and gre drop reasons Anton Danilov
2026-11-10 20:20 ` [PATCH net-next v6 1/8] ip_tunnel: add drop reasons to the generic RX path Anton Danilov
2026-11-10 20:20 ` [PATCH net-next v6 2/8] ip6_tunnel: add drop reasons to the receive path Anton Danilov
2026-11-10 20:20 ` [PATCH net-next v6 3/8] gre: make gre_parse_header() report a drop reason Anton Danilov
2026-11-10 20:20 ` [PATCH net-next v6 4/8] ip_gre: add drop reasons to the RX path Anton Danilov
2026-11-10 20:20 ` [PATCH net-next v6 5/8] ip6_gre: " Anton Danilov
2026-11-10 20:20 ` [PATCH net-next v6 6/8] ip_tunnel: make ip_tunnel_encap() return a drop reason Anton Danilov
2026-11-10 20:20 ` Anton Danilov [this message]
2026-11-10 20:20 ` [PATCH net-next v6 8/8] ip_gre: add drop reasons to the transmit path 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=20261110202021.821224-8-littlesmilingcloud@gmail.com \
--to=littlesmilingcloud@gmail.com \
--cc=davem@davemloft.net \
--cc=dsahern@kernel.org \
--cc=edumazet@kernel.org \
--cc=horms@kernel.org \
--cc=idosch@nvidia.com \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
/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
all inboxes | Powered by JetHome®