| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/09/18 04:45 | flow | patch-triage |
3mArgs:null Results: {
"EnableConfigs": [
"NET_IPGRE",
"NET_IPGRE_DEMUX",
"NET_IP_TUNNEL",
"IPV6_GRE",
"IPV6_TUNNEL"
],
"FocusSymbols": [
"gre_parse_header",
"ip_tunnel_rcv",
"ip_tunnel_xmit",
"ip_md_tunnel_xmit",
"ipgre_xmit",
"erspan_rcv",
"ip6_tnl_xmit",
"ip6_tnl_start_xmit",
"ip6gre_tunnel_xmit",
"ip6erspan_tunnel_xmit"
],
"KMSANReasoning": "The patch series updates the GRE and IPv4/IPv6 tunnel receive and transmit paths to record detailed sk_buff drop reasons by converting kfree_skb() invocations to kfree_skb_reason() and propagating `enum skb_drop_reason` pointers.\n\nAnalysis of uninitialized memory risks (KMSAN relevance):\n1. Scalar enum initialization: In all modified functions (`gre_rcv`, `ipgre_xmit`, `erspan_xmit`, `gre_tap_xmit`, `ip_tunnel_rcv`, `ip_md_tunnel_xmit`, `ip_tunnel_xmit`, `ip6gre_tunnel_xmit`, `ip6erspan_tunnel_xmit`, `__ip6_tnl_rcv`, `ip6_tnl_start_xmit`), local `reason` variables are explicitly initialized to `SKB_DROP_REASON_NOT_SPECIFIED` before any use or passing by reference.\n2. Pointer parameters: In functions taking `enum skb_drop_reason *reason` (such as `gre_parse_header`, `erspan_rcv`, `ipgre_rcv`, `ip6gre_rcv`, `ip6erspan_rcv`, `__gre6_xmit`, `ip6_tnl_xmit`), nullness is checked when necessary, and callers always provide pointers to previously initialized storage.\n3. Drop reason query functions: Calls converted to `pskb_may_pull_reason()`, `pskb_inet_may_pull_reason()`, and `skb_vlan_inet_prepare()` preserve identical buffer length and validity checks as the previous boolean wrappers, returning fully initialized enum values with no change to buffer parsing bounds.\n4. No kernel-to-user info-leaks: No uninitialized stack/heap structures or padding are exposed to user space via copy_to_user, netlink, or socket options.\n\nContrast with KASAN:\nAny potential errors in skb manipulation, buffer indexing, or reference counts in these networking paths are memory corruption or out-of-bounds access issues detectable by KASAN and DEBUG_NET. No uninitialized memory reads or information leaks are created, so a dedicated KMSAN fuzzing run is not warranted.",
"NeedsKMSAN": false,
"Reasoning": "The patch modifies IPv4/IPv6 GRE and IP tunneling drivers to track and propagate granular skb drop reasons (SKB_DROP_REASON_*) to kfree_skb_reason(). This alters function signatures, pointer propagation, and branching/validation logic across rx and tx paths in reachable virtual networking drivers. Fuzzing is warranted to verify that new drop paths, pointer handling, and refactored conditional checks behave correctly under malformed or edge-case tunnel packets.",
"WorthFuzzing": true
} |
| 1/1 | 2026/09/18 04:45 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 69b321fab7f696a20d12dd8898306fc1b8f69cd2\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Fri Sep 18 04:45:43 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/include/net/dropreason-core.h b/include/net/dropreason-core.h\nindex 12f9096515912..30378a0d22726 100644\n--- a/include/net/dropreason-core.h\n+++ b/include/net/dropreason-core.h\n@@ -129,6 +129,12 @@\n \tFN(PSP_INPUT)\t\t\t\\\n \tFN(PSP_OUTPUT)\t\t\t\\\n \tFN(RECURSION_LIMIT)\t\t\\\n+\tFN(TNL_OPT_MISMATCH)\t\t\\\n+\tFN(TNL_OLD_SEQ)\t\t\t\\\n+\tFN(GRE_INVALID_HDR)\t\t\\\n+\tFN(GRE_CSUM)\t\t\t\\\n+\tFN(GRE_TUNNEL_NOT_FOUND)\t\\\n+\tFN(TNL_ENCAP)\t\t\t\\\n \tFNe(MAX)\n \n /**\n@@ -612,6 +618,38 @@ enum skb_drop_reason {\n \tSKB_DROP_REASON_PSP_OUTPUT,\n \t/** @SKB_DROP_REASON_RECURSION_LIMIT: Dead loop on virtual device. */\n \tSKB_DROP_REASON_RECURSION_LIMIT,\n+\t/**\n+\t * @SKB_DROP_REASON_TNL_OPT_MISMATCH: the tunnel options\n+\t * carried by the packet do not match the tunnel configuration, e.g.\n+\t * a GRE tunnel configured with 'icsum' or 'iseq' received a packet\n+\t * with no checksum or no sequence number.\n+\t */\n+\tSKB_DROP_REASON_TNL_OPT_MISMATCH,\n+\t/**\n+\t * @SKB_DROP_REASON_TNL_OLD_SEQ: the sequence number carried\n+\t * by the packet is older than the one expected by the tunnel, e.g.\n+\t * after the remote endpoint restarted and reset its sequence\n+\t * numbering.\n+\t */\n+\tSKB_DROP_REASON_TNL_OLD_SEQ,\n+\t/**\n+\t * @SKB_DROP_REASON_GRE_INVALID_HDR: the GRE header is invalid, e.g.\n+\t * an unsupported version or the routing bit is set.\n+\t */\n+\tSKB_DROP_REASON_GRE_INVALID_HDR,\n+\t/** @SKB_DROP_REASON_GRE_CSUM: GRE checksum error */\n+\tSKB_DROP_REASON_GRE_CSUM,\n+\t/**\n+\t * @SKB_DROP_REASON_GRE_TUNNEL_NOT_FOUND: no GRE tunnel found for the\n+\t * endpoints and the key the packet carries.\n+\t */\n+\tSKB_DROP_REASON_GRE_TUNNEL_NOT_FOUND,\n+\t/**\n+\t * @SKB_DROP_REASON_TNL_ENCAP: failed to build the\n+\t * encapsulation header of a tunnel, e.g. an unknown or\n+\t * unregistered encapsulation type.\n+\t */\n+\tSKB_DROP_REASON_TNL_ENCAP,\n \t/**\n \t * @SKB_DROP_REASON_MAX: the maximum of core drop reasons, which\n \t * shouldn't be used as a real 'reason' - only for tracing code gen\ndiff --git a/include/net/gre.h b/include/net/gre.h\nindex b55f67ecd2fc4..a63f26c3f78ed 100644\n--- a/include/net/gre.h\n+++ b/include/net/gre.h\n@@ -33,7 +33,7 @@ int gre_add_protocol(const struct gre_protocol *proto, u8 version);\n int gre_del_protocol(const struct gre_protocol *proto, u8 version);\n \n int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,\n-\t\t bool *csum_err, __be16 proto, int nhs);\n+\t\t enum skb_drop_reason *reason, __be16 proto, int nhs);\n \n static inline bool netif_is_gretap(const struct net_device *dev)\n {\ndiff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h\nindex b99805ee2fd14..95f6d12254df6 100644\n--- a/include/net/ip6_tunnel.h\n+++ b/include/net/ip6_tunnel.h\n@@ -143,7 +143,8 @@ int ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb,\n int ip6_tnl_xmit_ctl(struct ip6_tnl *t, const struct in6_addr *laddr,\n \t\t const struct in6_addr *raddr);\n int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,\n-\t\t struct flowi6 *fl6, int encap_limit, __u32 *pmtu, __u8 proto);\n+\t\t struct flowi6 *fl6, int encap_limit, __u32 *pmtu, __u8 proto,\n+\t\t enum skb_drop_reason *reason);\n __u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 *raw);\n __u32 ip6_tnl_get_cap(struct ip6_tnl *t, const struct in6_addr *laddr,\n \t\t\t const struct in6_addr *raddr);\ndiff --git a/net/ipv4/gre_demux.c b/net/ipv4/gre_demux.c\nindex 96fd7dc6d82dd..c5d3847848efd 100644\n--- a/net/ipv4/gre_demux.c\n+++ b/net/ipv4/gre_demux.c\n@@ -58,26 +58,44 @@ EXPORT_SYMBOL_GPL(gre_del_protocol);\n \n /* Fills in tpi and returns header length to be pulled.\n * Note that caller must use pskb_may_pull() before pulling GRE header.\n+ *\n+ * @reason is only written when the header is rejected, so the caller has\n+ * to initialise it before the call.\n+ *\n+ * A NULL @reason means that the caller is not interested in the drop\n+ * reason, and also that a checksum failure must not be reported: the\n+ * checksum is still computed, the packet is just not rejected over it.\n+ * This is what the ICMP error handlers need, as they only get a part of\n+ * the original packet.\n */\n int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,\n-\t\t bool *csum_err, __be16 proto, int nhs)\n+\t\t enum skb_drop_reason *reason, __be16 proto, int nhs)\n {\n \tconst struct gre_base_hdr *greh;\n \t__be32 *options;\n \tint hdr_len;\n \n-\tif (unlikely(!pskb_may_pull(skb, nhs + sizeof(struct gre_base_hdr))))\n+\tif (unlikely(!pskb_may_pull(skb, nhs + sizeof(struct gre_base_hdr)))) {\n+\t\tif (reason)\n+\t\t\t*reason = SKB_DROP_REASON_HDR_TRUNC;\n \t\treturn -EINVAL;\n+\t}\n \n \tgreh = (struct gre_base_hdr *)(skb-\u003edata + nhs);\n-\tif (unlikely(greh-\u003eflags \u0026 (GRE_VERSION | GRE_ROUTING)))\n+\tif (unlikely(greh-\u003eflags \u0026 (GRE_VERSION | GRE_ROUTING))) {\n+\t\tif (reason)\n+\t\t\t*reason = SKB_DROP_REASON_GRE_INVALID_HDR;\n \t\treturn -EINVAL;\n+\t}\n \n \tgre_flags_to_tnl_flags(tpi-\u003eflags, greh-\u003eflags);\n \thdr_len = gre_calc_hlen(tpi-\u003eflags);\n \n-\tif (!pskb_may_pull(skb, nhs + hdr_len))\n+\tif (!pskb_may_pull(skb, nhs + hdr_len)) {\n+\t\tif (reason)\n+\t\t\t*reason = SKB_DROP_REASON_HDR_TRUNC;\n \t\treturn -EINVAL;\n+\t}\n \n \tgreh = (struct gre_base_hdr *)(skb-\u003edata + nhs);\n \ttpi-\u003eproto = greh-\u003eprotocol;\n@@ -87,8 +105,8 @@ int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,\n \t\tif (!skb_checksum_simple_validate(skb)) {\n \t\t\tskb_checksum_try_convert(skb, IPPROTO_GRE,\n \t\t\t\t\t\t null_compute_pseudo);\n-\t\t} else if (csum_err) {\n-\t\t\t*csum_err = true;\n+\t\t} else if (reason) {\n+\t\t\t*reason = SKB_DROP_REASON_GRE_CSUM;\n \t\t\treturn -EINVAL;\n \t\t}\n \n@@ -116,8 +134,11 @@ int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,\n \n \t\tval = skb_header_pointer(skb, nhs + hdr_len,\n \t\t\t\t\t sizeof(_val), \u0026_val);\n-\t\tif (!val)\n+\t\tif (!val) {\n+\t\t\tif (reason)\n+\t\t\t\t*reason = SKB_DROP_REASON_HDR_TRUNC;\n \t\t\treturn -EINVAL;\n+\t\t}\n \t\ttpi-\u003eproto = proto;\n \t\tif ((*val \u0026 0xF0) != 0x40)\n \t\t\thdr_len += 4;\n@@ -132,8 +153,11 @@ int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,\n \t greh-\u003eprotocol == htons(ETH_P_ERSPAN2)) {\n \t\tstruct erspan_base_hdr *ershdr;\n \n-\t\tif (!pskb_may_pull(skb, nhs + hdr_len + sizeof(*ershdr)))\n+\t\tif (!pskb_may_pull(skb, nhs + hdr_len + sizeof(*ershdr))) {\n+\t\t\tif (reason)\n+\t\t\t\t*reason = SKB_DROP_REASON_HDR_TRUNC;\n \t\t\treturn -EINVAL;\n+\t\t}\n \n \t\tershdr = (struct erspan_base_hdr *)(skb-\u003edata + nhs + hdr_len);\n \t\ttpi-\u003ekey = cpu_to_be32(get_session_id(ershdr));\n@@ -145,16 +169,20 @@ EXPORT_SYMBOL(gre_parse_header);\n \n static int gre_rcv(struct sk_buff *skb)\n {\n+\tenum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;\n \tconst struct gre_protocol *proto;\n \tu8 ver;\n \tint ret;\n \n-\tif (!pskb_may_pull(skb, 12))\n+\treason = pskb_may_pull_reason(skb, 12);\n+\tif (reason)\n \t\tgoto drop;\n \n \tver = skb-\u003edata[1]\u00260x7f;\n-\tif (ver \u003e= GREPROTO_MAX)\n+\tif (ver \u003e= GREPROTO_MAX) {\n+\t\treason = SKB_DROP_REASON_UNHANDLED_PROTO;\n \t\tgoto drop;\n+\t}\n \n \trcu_read_lock();\n \tproto = rcu_dereference(gre_proto[ver]);\n@@ -167,11 +195,11 @@ static int gre_rcv(struct sk_buff *skb)\n drop_nohandler:\n \trcu_read_unlock();\n \tdev_core_stats_rx_nohandler_inc(skb-\u003edev);\n-\tkfree_skb(skb);\n+\tkfree_skb_reason(skb, SKB_DROP_REASON_UNHANDLED_PROTO);\n \treturn NET_RX_DROP;\n drop:\n \tdev_core_stats_rx_dropped_inc(skb-\u003edev);\n-\tkfree_skb(skb);\n+\tkfree_skb_reason(skb, reason);\n \treturn NET_RX_DROP;\n }\n \ndiff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c\nindex 5e877018e006c..71360af429ea7 100644\n--- a/net/ipv4/ip_gre.c\n+++ b/net/ipv4/ip_gre.c\n@@ -265,7 +265,7 @@ static bool is_erspan_type1(int gre_hdr_len)\n }\n \n static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,\n-\t\t int gre_hdr_len)\n+\t\t int gre_hdr_len, enum skb_drop_reason *reason)\n {\n \tstruct net *net = dev_net(skb-\u003edev);\n \tstruct metadata_dst *tun_dst = NULL;\n@@ -289,8 +289,10 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,\n \t\t\t\t\t iph-\u003esaddr, iph-\u003edaddr, 0);\n \t} else {\n \t\tif (unlikely(!pskb_may_pull(skb,\n-\t\t\t\t\t gre_hdr_len + sizeof(*ershdr))))\n+\t\t\t\t\t gre_hdr_len + sizeof(*ershdr)))) {\n+\t\t\t*reason = SKB_DROP_REASON_HDR_TRUNC;\n \t\t\treturn PACKET_REJECT;\n+\t\t}\n \n \t\tershdr = (struct erspan_base_hdr *)(skb-\u003edata + gre_hdr_len);\n \t\tver = ershdr-\u003ever;\n@@ -306,8 +308,10 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,\n \t\telse\n \t\t\tlen = gre_hdr_len + erspan_hdr_len(ver);\n \n-\t\tif (unlikely(!pskb_may_pull(skb, len)))\n+\t\tif (unlikely(!pskb_may_pull(skb, len))) {\n+\t\t\t*reason = SKB_DROP_REASON_HDR_TRUNC;\n \t\t\treturn PACKET_REJECT;\n+\t\t}\n \n \t\tif (__iptunnel_pull_header(skb,\n \t\t\t\t\t len,\n@@ -327,8 +331,10 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,\n \n \t\t\ttun_dst = ip_tun_rx_dst(skb, flags,\n \t\t\t\t\t\ttun_id, sizeof(*md));\n-\t\t\tif (!tun_dst)\n+\t\t\tif (!tun_dst) {\n+\t\t\t\t*reason = SKB_DROP_REASON_NOMEM;\n \t\t\t\treturn PACKET_REJECT;\n+\t\t\t}\n \n \t\t\t/* MUST set options_len before referencing options */\n \t\t\tinfo = \u0026tun_dst-\u003eu.tun_info;\n@@ -356,15 +362,17 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,\n \t\tip_tunnel_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error);\n \t\treturn PACKET_RCVD;\n \t}\n+\t*reason = SKB_DROP_REASON_GRE_TUNNEL_NOT_FOUND;\n \treturn PACKET_REJECT;\n \n drop:\n-\tkfree_skb(skb);\n+\tkfree_skb_reason(skb, SKB_DROP_REASON_NOMEM);\n \treturn PACKET_RCVD;\n }\n \n static int __ipgre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi,\n-\t\t struct ip_tunnel_net *itn, int hdr_len, bool raw_proto)\n+\t\t struct ip_tunnel_net *itn, int hdr_len, bool raw_proto,\n+\t\t enum skb_drop_reason *reason)\n {\n \tstruct metadata_dst *tun_dst = NULL;\n \tconst struct iphdr *iph;\n@@ -400,22 +408,25 @@ static int __ipgre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi,\n \n \t\t\ttun_id = key32_to_tunnel_id(tpi-\u003ekey);\n \t\t\ttun_dst = ip_tun_rx_dst(skb, flags, tun_id, 0);\n-\t\t\tif (!tun_dst)\n+\t\t\tif (!tun_dst) {\n+\t\t\t\t*reason = SKB_DROP_REASON_NOMEM;\n \t\t\t\treturn PACKET_REJECT;\n+\t\t\t}\n \t\t}\n \n \t\tip_tunnel_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error);\n \t\treturn PACKET_RCVD;\n \t}\n+\t*reason = SKB_DROP_REASON_GRE_TUNNEL_NOT_FOUND;\n \treturn PACKET_NEXT;\n \n drop:\n-\tkfree_skb(skb);\n+\tkfree_skb_reason(skb, SKB_DROP_REASON_NOMEM);\n \treturn PACKET_RCVD;\n }\n \n static int ipgre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi,\n-\t\t int hdr_len)\n+\t\t int hdr_len, enum skb_drop_reason *reason)\n {\n \tstruct net *net = dev_net(skb-\u003edev);\n \tstruct ip_tunnel_net *itn;\n@@ -426,21 +437,21 @@ static int ipgre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi,\n \telse\n \t\titn = net_generic(net, ipgre_net_id);\n \n-\tres = __ipgre_rcv(skb, tpi, itn, hdr_len, false);\n+\tres = __ipgre_rcv(skb, tpi, itn, hdr_len, false, reason);\n \tif (res == PACKET_NEXT \u0026\u0026 tpi-\u003eproto == htons(ETH_P_TEB)) {\n \t\t/* ipgre tunnels in collect metadata mode should receive\n \t\t * also ETH_P_TEB traffic.\n \t\t */\n \t\titn = net_generic(net, ipgre_net_id);\n-\t\tres = __ipgre_rcv(skb, tpi, itn, hdr_len, true);\n+\t\tres = __ipgre_rcv(skb, tpi, itn, hdr_len, true, reason);\n \t}\n \treturn res;\n }\n \n static int gre_rcv(struct sk_buff *skb)\n {\n+\tenum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;\n \tstruct tnl_ptk_info tpi;\n-\tbool csum_err = false;\n \tint hdr_len;\n \n #ifdef CONFIG_NET_IPGRE_BROADCAST\n@@ -451,25 +462,25 @@ static int gre_rcv(struct sk_buff *skb)\n \t}\n #endif\n \n-\thdr_len = gre_parse_header(skb, \u0026tpi, \u0026csum_err, htons(ETH_P_IP), 0);\n+\thdr_len = gre_parse_header(skb, \u0026tpi, \u0026reason, htons(ETH_P_IP), 0);\n \tif (hdr_len \u003c 0)\n \t\tgoto drop;\n \n \tif (unlikely(tpi.proto == htons(ETH_P_ERSPAN) ||\n \t\t tpi.proto == htons(ETH_P_ERSPAN2))) {\n-\t\tif (erspan_rcv(skb, \u0026tpi, hdr_len) == PACKET_RCVD)\n+\t\tif (erspan_rcv(skb, \u0026tpi, hdr_len, \u0026reason) == PACKET_RCVD)\n \t\t\treturn 0;\n \t\tgoto out;\n \t}\n \n-\tif (ipgre_rcv(skb, \u0026tpi, hdr_len) == PACKET_RCVD)\n+\tif (ipgre_rcv(skb, \u0026tpi, hdr_len, \u0026reason) == PACKET_RCVD)\n \t\treturn 0;\n \n out:\n \ticmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);\n drop:\n \tdev_core_stats_rx_dropped_inc(skb-\u003edev);\n-\tkfree_skb(skb);\n+\tkfree_skb_reason(skb, reason);\n \treturn 0;\n }\n \n@@ -496,6 +507,7 @@ static int gre_handle_offloads(struct sk_buff *skb, bool csum)\n static void gre_fb_xmit(struct sk_buff *skb, struct net_device *dev,\n \t\t\t__be16 proto)\n {\n+\tenum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;\n \tstruct ip_tunnel *tunnel = netdev_priv(dev);\n \tIP_TUNNEL_DECLARE_FLAGS(flags) = { };\n \tstruct ip_tunnel_info *tun_info;\n@@ -504,19 +516,25 @@ static void gre_fb_xmit(struct sk_buff *skb, struct net_device *dev,\n \n \ttun_info = skb_tunnel_info(skb);\n \tif (unlikely(!tun_info || !(tun_info-\u003emode \u0026 IP_TUNNEL_INFO_TX) ||\n-\t\t ip_tunnel_info_af(tun_info) != AF_INET))\n+\t\t ip_tunnel_info_af(tun_info) != AF_INET)) {\n+\t\treason = SKB_DROP_REASON_TUNNEL_TXINFO;\n \t\tgoto err_free_skb;\n+\t}\n \n \tkey = \u0026tun_info-\u003ekey;\n \ttunnel_hlen = gre_calc_hlen(key-\u003etun_flags);\n \n-\tif (skb_cow_head(skb, dev-\u003eneeded_headroom))\n+\tif (skb_cow_head(skb, dev-\u003eneeded_headroom)) {\n+\t\treason = SKB_DROP_REASON_NOMEM;\n \t\tgoto err_free_skb;\n+\t}\n \n \t/* Push Tunnel header. */\n \tif (gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT,\n-\t\t\t\t\t tunnel-\u003eparms.o_flags)))\n+\t\t\t\t\t tunnel-\u003eparms.o_flags))) {\n+\t\treason = SKB_DROP_REASON_NOMEM;\n \t\tgoto err_free_skb;\n+\t}\n \n \t__set_bit(IP_TUNNEL_CSUM_BIT, flags);\n \t__set_bit(IP_TUNNEL_KEY_BIT, flags);\n@@ -533,12 +551,13 @@ static void gre_fb_xmit(struct sk_buff *skb, struct net_device *dev,\n \treturn;\n \n err_free_skb:\n-\tkfree_skb(skb);\n+\tkfree_skb_reason(skb, reason);\n \tDEV_STATS_INC(dev, tx_dropped);\n }\n \n static void erspan_fb_xmit(struct sk_buff *skb, struct net_device *dev)\n {\n+\tenum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;\n \tstruct ip_tunnel *tunnel = netdev_priv(dev);\n \tIP_TUNNEL_DECLARE_FLAGS(flags) = { };\n \tstruct ip_tunnel_info *tun_info;\n@@ -552,29 +571,41 @@ static void erspan_fb_xmit(struct sk_buff *skb, struct net_device *dev)\n \n \ttun_info = skb_tunnel_info(skb);\n \tif (unlikely(!tun_info || !(tun_info-\u003emode \u0026 IP_TUNNEL_INFO_TX) ||\n-\t\t ip_tunnel_info_af(tun_info) != AF_INET))\n+\t\t ip_tunnel_info_af(tun_info) != AF_INET)) {\n+\t\treason = SKB_DROP_REASON_TUNNEL_TXINFO;\n \t\tgoto err_free_skb;\n+\t}\n \n \tkey = \u0026tun_info-\u003ekey;\n-\tif (!test_bit(IP_TUNNEL_ERSPAN_OPT_BIT, tun_info-\u003ekey.tun_flags))\n+\tif (!test_bit(IP_TUNNEL_ERSPAN_OPT_BIT, tun_info-\u003ekey.tun_flags)) {\n+\t\treason = SKB_DROP_REASON_TUNNEL_TXINFO;\n \t\tgoto err_free_skb;\n-\tif (tun_info-\u003eoptions_len \u003c sizeof(*md))\n+\t}\n+\tif (tun_info-\u003eoptions_len \u003c sizeof(*md)) {\n+\t\treason = SKB_DROP_REASON_TUNNEL_TXINFO;\n \t\tgoto err_free_skb;\n+\t}\n \tmd = ip_tunnel_info_opts(tun_info);\n \n \t/* ERSPAN has fixed 8 byte GRE header */\n \tversion = md-\u003eversion;\n \ttunnel_hlen = 8 + erspan_hdr_len(version);\n \n-\tif (skb_cow_head(skb, dev-\u003eneeded_headroom))\n+\tif (skb_cow_head(skb, dev-\u003eneeded_headroom)) {\n+\t\treason = SKB_DROP_REASON_NOMEM;\n \t\tgoto err_free_skb;\n+\t}\n \n-\tif (gre_handle_offloads(skb, false))\n+\tif (gre_handle_offloads(skb, false)) {\n+\t\treason = SKB_DROP_REASON_NOMEM;\n \t\tgoto err_free_skb;\n+\t}\n \n \tif (skb-\u003elen \u003e dev-\u003emtu + dev-\u003ehard_header_len) {\n-\t\tif (pskb_trim(skb, dev-\u003emtu + dev-\u003ehard_header_len))\n+\t\tif (pskb_trim(skb, dev-\u003emtu + dev-\u003ehard_header_len)) {\n+\t\t\treason = SKB_DROP_REASON_NOMEM;\n \t\t\tgoto err_free_skb;\n+\t\t}\n \t\ttruncate = true;\n \t}\n \n@@ -606,6 +637,7 @@ static void erspan_fb_xmit(struct sk_buff *skb, struct net_device *dev)\n \t\t\t\t truncate, true);\n \t\tproto = htons(ETH_P_ERSPAN2);\n \t} else {\n+\t\treason = SKB_DROP_REASON_UNHANDLED_PROTO;\n \t\tgoto err_free_skb;\n \t}\n \n@@ -618,7 +650,7 @@ static void erspan_fb_xmit(struct sk_buff *skb, struct net_device *dev)\n \treturn;\n \n err_free_skb:\n-\tkfree_skb(skb);\n+\tkfree_skb_reason(skb, reason);\n \tDEV_STATS_INC(dev, tx_dropped);\n }\n \n@@ -649,11 +681,13 @@ static int gre_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)\n static netdev_tx_t ipgre_xmit(struct sk_buff *skb,\n \t\t\t struct net_device *dev)\n {\n+\tenum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;\n \tstruct ip_tunnel *tunnel = netdev_priv(dev);\n \tIP_TUNNEL_DECLARE_FLAGS(flags);\n \tconst struct iphdr *tnl_params;\n \n-\tif (!pskb_inet_may_pull(skb))\n+\treason = pskb_inet_may_pull_reason(skb);\n+\tif (reason)\n \t\tgoto free_skb;\n \n \tif (tunnel-\u003ecollect_md) {\n@@ -664,10 +698,13 @@ static netdev_tx_t ipgre_xmit(struct sk_buff *skb,\n \tif (dev-\u003eheader_ops) {\n \t\tint pull_len = tunnel-\u003ehlen + sizeof(struct iphdr);\n \n-\t\tif (skb_cow_head(skb, 0))\n+\t\tif (skb_cow_head(skb, 0)) {\n+\t\t\treason = SKB_DROP_REASON_NOMEM;\n \t\t\tgoto free_skb;\n+\t\t}\n \n-\t\tif (!pskb_may_pull(skb, pull_len))\n+\t\treason = pskb_may_pull_reason(skb, pull_len);\n+\t\tif (reason)\n \t\t\tgoto free_skb;\n \n \t\ttnl_params = (const struct iphdr *)skb-\u003edata;\n@@ -677,25 +714,31 @@ static netdev_tx_t ipgre_xmit(struct sk_buff *skb,\n \t\tskb_reset_mac_header(skb);\n \n \t\tif (skb-\u003eip_summed == CHECKSUM_PARTIAL \u0026\u0026\n-\t\t skb_checksum_start(skb) \u003c skb-\u003edata)\n+\t\t skb_checksum_start(skb) \u003c skb-\u003edata) {\n+\t\t\treason = SKB_DROP_REASON_SKB_CSUM;\n \t\t\tgoto free_skb;\n+\t\t}\n \t} else {\n-\t\tif (skb_cow_head(skb, dev-\u003eneeded_headroom))\n+\t\tif (skb_cow_head(skb, dev-\u003eneeded_headroom)) {\n+\t\t\treason = SKB_DROP_REASON_NOMEM;\n \t\t\tgoto free_skb;\n+\t\t}\n \n \t\ttnl_params = \u0026tunnel-\u003eparms.iph;\n \t}\n \n \tip_tunnel_flags_copy(flags, tunnel-\u003eparms.o_flags);\n \n-\tif (gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT, flags)))\n+\tif (gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT, flags))) {\n+\t\treason = SKB_DROP_REASON_NOMEM;\n \t\tgoto free_skb;\n+\t}\n \n \t__gre_xmit(skb, dev, tnl_params, skb-\u003eprotocol, flags);\n \treturn NETDEV_TX_OK;\n \n free_skb:\n-\tkfree_skb(skb);\n+\tkfree_skb_reason(skb, reason);\n \tDEV_STATS_INC(dev, tx_dropped);\n \treturn NETDEV_TX_OK;\n }\n@@ -703,12 +746,14 @@ static netdev_tx_t ipgre_xmit(struct sk_buff *skb,\n static netdev_tx_t erspan_xmit(struct sk_buff *skb,\n \t\t\t struct net_device *dev)\n {\n+\tenum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;\n \tstruct ip_tunnel *tunnel = netdev_priv(dev);\n \tIP_TUNNEL_DECLARE_FLAGS(flags);\n \tbool truncate = false;\n \t__be16 proto;\n \n-\tif (!pskb_inet_may_pull(skb))\n+\treason = pskb_inet_may_pull_reason(skb);\n+\tif (reason)\n \t\tgoto free_skb;\n \n \tif (tunnel-\u003ecollect_md) {\n@@ -716,15 +761,21 @@ static netdev_tx_t erspan_xmit(struct sk_buff *skb,\n \t\treturn NETDEV_TX_OK;\n \t}\n \n-\tif (gre_handle_offloads(skb, false))\n+\tif (gre_handle_offloads(skb, false)) {\n+\t\treason = SKB_DROP_REASON_NOMEM;\n \t\tgoto free_skb;\n+\t}\n \n-\tif (skb_cow_head(skb, dev-\u003eneeded_headroom))\n+\tif (skb_cow_head(skb, dev-\u003eneeded_headroom)) {\n+\t\treason = SKB_DROP_REASON_NOMEM;\n \t\tgoto free_skb;\n+\t}\n \n \tif (skb-\u003elen \u003e dev-\u003emtu + dev-\u003ehard_header_len) {\n-\t\tif (pskb_trim(skb, dev-\u003emtu + dev-\u003ehard_header_len))\n+\t\tif (pskb_trim(skb, dev-\u003emtu + dev-\u003ehard_header_len)) {\n+\t\t\treason = SKB_DROP_REASON_NOMEM;\n \t\t\tgoto free_skb;\n+\t\t}\n \t\ttruncate = true;\n \t}\n \n@@ -745,6 +796,7 @@ static netdev_tx_t erspan_xmit(struct sk_buff *skb,\n \t\t\t\t truncate, true);\n \t\tproto = htons(ETH_P_ERSPAN2);\n \t} else {\n+\t\treason = SKB_DROP_REASON_UNHANDLED_PROTO;\n \t\tgoto free_skb;\n \t}\n \n@@ -753,7 +805,7 @@ static netdev_tx_t erspan_xmit(struct sk_buff *skb,\n \treturn NETDEV_TX_OK;\n \n free_skb:\n-\tkfree_skb(skb);\n+\tkfree_skb_reason(skb, reason);\n \tDEV_STATS_INC(dev, tx_dropped);\n \treturn NETDEV_TX_OK;\n }\n@@ -761,10 +813,12 @@ static netdev_tx_t erspan_xmit(struct sk_buff *skb,\n static netdev_tx_t gre_tap_xmit(struct sk_buff *skb,\n \t\t\t\tstruct net_device *dev)\n {\n+\tenum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;\n \tstruct ip_tunnel *tunnel = netdev_priv(dev);\n \tIP_TUNNEL_DECLARE_FLAGS(flags);\n \n-\tif (!pskb_inet_may_pull(skb))\n+\treason = pskb_inet_may_pull_reason(skb);\n+\tif (reason)\n \t\tgoto free_skb;\n \n \tif (tunnel-\u003ecollect_md) {\n@@ -774,17 +828,21 @@ static netdev_tx_t gre_tap_xmit(struct sk_buff *skb,\n \n \tip_tunnel_flags_copy(flags, tunnel-\u003eparms.o_flags);\n \n-\tif (gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT, flags)))\n+\tif (gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT, flags))) {\n+\t\treason = SKB_DROP_REASON_NOMEM;\n \t\tgoto free_skb;\n+\t}\n \n-\tif (skb_cow_head(skb, dev-\u003eneeded_headroom))\n+\tif (skb_cow_head(skb, dev-\u003eneeded_headroom)) {\n+\t\treason = SKB_DROP_REASON_NOMEM;\n \t\tgoto free_skb;\n+\t}\n \n \t__gre_xmit(skb, dev, \u0026tunnel-\u003eparms.iph, htons(ETH_P_TEB), flags);\n \treturn NETDEV_TX_OK;\n \n free_skb:\n-\tkfree_skb(skb);\n+\tkfree_skb_reason(skb, reason);\n \tDEV_STATS_INC(dev, tx_dropped);\n \treturn NETDEV_TX_OK;\n }\ndiff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c\nindex 0875474a578a4..66cb0b86fa79a 100644\n--- a/net/ipv4/ip_tunnel.c\n+++ b/net/ipv4/ip_tunnel.c\n@@ -384,6 +384,7 @@ int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb,\n \t\t const struct tnl_ptk_info *tpi, struct metadata_dst *tun_dst,\n \t\t bool log_ecn_error)\n {\n+\tenum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;\n \tconst struct iphdr *iph = ip_hdr(skb);\n \tint nh, err;\n \n@@ -398,14 +399,22 @@ int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb,\n \t test_bit(IP_TUNNEL_CSUM_BIT, tpi-\u003eflags)) {\n \t\tDEV_STATS_INC(tunnel-\u003edev, rx_crc_errors);\n \t\tDEV_STATS_INC(tunnel-\u003edev, rx_errors);\n+\t\treason = SKB_DROP_REASON_TNL_OPT_MISMATCH;\n \t\tgoto drop;\n \t}\n \n \tif (test_bit(IP_TUNNEL_SEQ_BIT, tunnel-\u003eparms.i_flags)) {\n-\t\tif (!test_bit(IP_TUNNEL_SEQ_BIT, tpi-\u003eflags) ||\n-\t\t (tunnel-\u003ei_seqno \u0026\u0026 (s32)(ntohl(tpi-\u003eseq) - tunnel-\u003ei_seqno) \u003c 0)) {\n+\t\tif (!test_bit(IP_TUNNEL_SEQ_BIT, tpi-\u003eflags)) {\n \t\t\tDEV_STATS_INC(tunnel-\u003edev, rx_fifo_errors);\n \t\t\tDEV_STATS_INC(tunnel-\u003edev, rx_errors);\n+\t\t\treason = SKB_DROP_REASON_TNL_OPT_MISMATCH;\n+\t\t\tgoto drop;\n+\t\t}\n+\t\tif (tunnel-\u003ei_seqno \u0026\u0026\n+\t\t (s32)(ntohl(tpi-\u003eseq) - tunnel-\u003ei_seqno) \u003c 0) {\n+\t\t\tDEV_STATS_INC(tunnel-\u003edev, rx_fifo_errors);\n+\t\t\tDEV_STATS_INC(tunnel-\u003edev, rx_errors);\n+\t\t\treason = SKB_DROP_REASON_TNL_OLD_SEQ;\n \t\t\tgoto drop;\n \t\t}\n \t\ttunnel-\u003ei_seqno = ntohl(tpi-\u003eseq) + 1;\n@@ -419,7 +428,8 @@ int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb,\n \n \tskb_set_network_header(skb, (tunnel-\u003edev-\u003etype == ARPHRD_ETHER) ? ETH_HLEN : 0);\n \n-\tif (!pskb_inet_may_pull(skb)) {\n+\treason = pskb_inet_may_pull_reason(skb);\n+\tif (reason) {\n \t\tDEV_STATS_INC(tunnel-\u003edev, rx_length_errors);\n \t\tDEV_STATS_INC(tunnel-\u003edev, rx_errors);\n \t\tgoto drop;\n@@ -434,6 +444,7 @@ int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb,\n \t\tif (err \u003e 1) {\n \t\t\tDEV_STATS_INC(tunnel-\u003edev, rx_frame_errors);\n \t\t\tDEV_STATS_INC(tunnel-\u003edev, rx_errors);\n+\t\t\treason = SKB_DROP_REASON_IP_TUNNEL_ECN;\n \t\t\tgoto drop;\n \t\t}\n \t}\n@@ -457,7 +468,7 @@ int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb,\n drop:\n \tif (tun_dst)\n \t\tdst_release((struct dst_entry *)tun_dst);\n-\tkfree_skb(skb);\n+\tkfree_skb_reason(skb, reason);\n \treturn 0;\n }\n EXPORT_SYMBOL_GPL(ip_tunnel_rcv);\n@@ -575,6 +586,7 @@ static int tnl_update_pmtu(struct net_device *dev, struct sk_buff *skb,\n void ip_md_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,\n \t\t u8 proto, int tunnel_hlen)\n {\n+\tenum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;\n \tstruct ip_tunnel *tunnel = netdev_priv(dev);\n \tu32 headroom = sizeof(struct iphdr);\n \tstruct ip_tunnel_info *tun_info;\n@@ -588,8 +600,10 @@ void ip_md_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,\n \n \ttun_info = skb_tunnel_info(skb);\n \tif (unlikely(!tun_info || !(tun_info-\u003emode \u0026 IP_TUNNEL_INFO_TX) ||\n-\t\t ip_tunnel_info_af(tun_info) != AF_INET))\n+\t\t ip_tunnel_info_af(tun_info) != AF_INET)) {\n+\t\treason = SKB_DROP_REASON_TUNNEL_TXINFO;\n \t\tgoto tx_error;\n+\t}\n \tkey = \u0026tun_info-\u003ekey;\n \tmemset(\u0026(IPCB(skb)-\u003eopt), 0, sizeof(IPCB(skb)-\u003eopt));\n \tinner_iph = (const struct iphdr *)skb_inner_network_header(skb);\n@@ -608,8 +622,10 @@ void ip_md_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,\n \tif (!tunnel_hlen)\n \t\ttunnel_hlen = ip_encap_hlen(\u0026tun_info-\u003eencap);\n \n-\tif (ip_tunnel_encap(skb, \u0026tun_info-\u003eencap, \u0026proto, \u0026fl4) \u003c 0)\n+\tif (ip_tunnel_encap(skb, \u0026tun_info-\u003eencap, \u0026proto, \u0026fl4) \u003c 0) {\n+\t\treason = SKB_DROP_REASON_TNL_ENCAP;\n \t\tgoto tx_error;\n+\t}\n \n \tuse_cache = ip_tunnel_dst_cache_usable(skb, tun_info);\n \tif (use_cache)\n@@ -618,6 +634,7 @@ void ip_md_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,\n \t\trt = ip_route_output_key(tunnel-\u003enet, \u0026fl4);\n \t\tif (IS_ERR(rt)) {\n \t\t\tDEV_STATS_INC(dev, tx_carrier_errors);\n+\t\t\treason = SKB_DROP_REASON_IP_OUTNOROUTES;\n \t\t\tgoto tx_error;\n \t\t}\n \t\tif (use_cache)\n@@ -627,6 +644,7 @@ void ip_md_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,\n \tif (rt-\u003edst.dev == dev) {\n \t\tip_rt_put(rt);\n \t\tDEV_STATS_INC(dev, collisions);\n+\t\treason = SKB_DROP_REASON_RECURSION_LIMIT;\n \t\tgoto tx_error;\n \t}\n \n@@ -635,6 +653,7 @@ void ip_md_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,\n \tif (tnl_update_pmtu(dev, skb, rt, df, inner_iph, tunnel_hlen,\n \t\t\t key-\u003eu.ipv4.dst, true)) {\n \t\tip_rt_put(rt);\n+\t\treason = SKB_DROP_REASON_PKT_TOO_BIG;\n \t\tgoto tx_error;\n \t}\n \n@@ -652,6 +671,7 @@ void ip_md_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,\n \theadroom += LL_RESERVED_SPACE(rt-\u003edst.dev) + rt-\u003edst.header_len;\n \tif (skb_cow_head(skb, headroom)) {\n \t\tip_rt_put(rt);\n+\t\treason = SKB_DROP_REASON_NOMEM;\n \t\tgoto tx_dropped;\n \t}\n \n@@ -666,13 +686,14 @@ void ip_md_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,\n tx_dropped:\n \tDEV_STATS_INC(dev, tx_dropped);\n kfree:\n-\tkfree_skb(skb);\n+\tkfree_skb_reason(skb, reason);\n }\n EXPORT_SYMBOL_GPL(ip_md_tunnel_xmit);\n \n void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,\n \t\t const struct iphdr *tnl_params, u8 protocol)\n {\n+\tenum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;\n \tstruct ip_tunnel *tunnel = netdev_priv(dev);\n \tstruct ip_tunnel_info *tun_info = NULL;\n \tconst struct iphdr *inner_iph;\n@@ -700,9 +721,15 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,\n \n \t\tif (!skb_dst(skb)) {\n \t\t\tDEV_STATS_INC(dev, tx_fifo_errors);\n+\t\t\treason = SKB_DROP_REASON_NO_TX_TARGET;\n \t\t\tgoto tx_error;\n \t\t}\n \n+\t\t/* Only the branches below can derive a destination. If\n+\t\t * none of them matches, the payload protocol is not one\n+\t\t * this tunnel can carry.\n+\t\t */\n+\t\treason = SKB_DROP_REASON_UNHANDLED_PROTO;\n \t\ttun_info = skb_tunnel_info(skb);\n \t\tif (tun_info \u0026\u0026 (tun_info-\u003emode \u0026 IP_TUNNEL_INFO_TX) \u0026\u0026\n \t\t ip_tunnel_info_af(tun_info) == AF_INET \u0026\u0026\n@@ -723,8 +750,10 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,\n \n \t\t\tneigh = dst_neigh_lookup(skb_dst(skb),\n \t\t\t\t\t\t \u0026ipv6_hdr(skb)-\u003edaddr);\n-\t\t\tif (!neigh)\n+\t\t\tif (!neigh) {\n+\t\t\t\treason = SKB_DROP_REASON_NEIGH_CREATEFAIL;\n \t\t\t\tgoto tx_error;\n+\t\t\t}\n \n \t\t\taddr6 = (const struct in6_addr *)\u0026neigh-\u003eprimary_key;\n \t\t\taddr_type = ipv6_addr_type(addr6);\n@@ -741,8 +770,10 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,\n \t\t\t\tdst = addr6-\u003es6_addr32[3];\n \t\t\t}\n \t\t\tneigh_release(neigh);\n-\t\t\tif (do_tx_error_icmp)\n+\t\t\tif (do_tx_error_icmp) {\n+\t\t\t\treason = SKB_DROP_REASON_NO_TX_TARGET;\n \t\t\t\tgoto tx_error_icmp;\n+\t\t\t}\n \t\t}\n #endif\n \t\telse\n@@ -769,8 +800,10 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,\n \t\t\t tunnel-\u003enet, READ_ONCE(tunnel-\u003eparms.link),\n \t\t\t tunnel-\u003efwmark, skb_get_hash(skb), 0);\n \n-\tif (ip_tunnel_encap(skb, \u0026tunnel-\u003eencap, \u0026protocol, \u0026fl4) \u003c 0)\n+\tif (ip_tunnel_encap(skb, \u0026tunnel-\u003eencap, \u0026protocol, \u0026fl4) \u003c 0) {\n+\t\treason = SKB_DROP_REASON_TNL_ENCAP;\n \t\tgoto tx_error;\n+\t}\n \n \tif (connected \u0026\u0026 md) {\n \t\tuse_cache = ip_tunnel_dst_cache_usable(skb, tun_info);\n@@ -787,6 +820,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,\n \n \t\tif (IS_ERR(rt)) {\n \t\t\tDEV_STATS_INC(dev, tx_carrier_errors);\n+\t\t\treason = SKB_DROP_REASON_IP_OUTNOROUTES;\n \t\t\tgoto tx_error;\n \t\t}\n \t\tif (use_cache)\n@@ -800,6 +834,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,\n \tif (rt-\u003edst.dev == dev) {\n \t\tip_rt_put(rt);\n \t\tDEV_STATS_INC(dev, collisions);\n+\t\treason = SKB_DROP_REASON_RECURSION_LIMIT;\n \t\tgoto tx_error;\n \t}\n \n@@ -809,6 +844,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,\n \n \tif (tnl_update_pmtu(dev, skb, rt, df, inner_iph, 0, 0, false)) {\n \t\tip_rt_put(rt);\n+\t\treason = SKB_DROP_REASON_PKT_TOO_BIG;\n \t\tgoto tx_error;\n \t}\n \n@@ -843,7 +879,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,\n \tif (skb_cow_head(skb, max_headroom)) {\n \t\tip_rt_put(rt);\n \t\tDEV_STATS_INC(dev, tx_dropped);\n-\t\tkfree_skb(skb);\n+\t\tkfree_skb_reason(skb, SKB_DROP_REASON_NOMEM);\n \t\treturn;\n \t}\n \n@@ -859,7 +895,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,\n #endif\n tx_error:\n \tDEV_STATS_INC(dev, tx_errors);\n-\tkfree_skb(skb);\n+\tkfree_skb_reason(skb, reason);\n }\n EXPORT_SYMBOL_GPL(ip_tunnel_xmit);\n \ndiff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c\nindex 8ebda0b6a78b2..5d1e55813fce2 100644\n--- a/net/ipv6/ip6_gre.c\n+++ b/net/ipv6/ip6_gre.c\n@@ -454,7 +454,8 @@ static int ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt,\n \treturn 0;\n }\n \n-static int ip6gre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi)\n+static int ip6gre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi,\n+\t\t enum skb_drop_reason *reason)\n {\n \tconst struct ipv6hdr *ipv6h;\n \tstruct ip6_tnl *tunnel;\n@@ -473,8 +474,10 @@ static int ip6gre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi)\n \t\t\ttun_id = key32_to_tunnel_id(tpi-\u003ekey);\n \n \t\t\ttun_dst = ipv6_tun_rx_dst(skb, flags, tun_id, 0);\n-\t\t\tif (!tun_dst)\n+\t\t\tif (!tun_dst) {\n+\t\t\t\t*reason = SKB_DROP_REASON_NOMEM;\n \t\t\t\treturn PACKET_REJECT;\n+\t\t\t}\n \n \t\t\tip6_tnl_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error);\n \t\t} else {\n@@ -484,12 +487,14 @@ static int ip6gre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi)\n \t\treturn PACKET_RCVD;\n \t}\n \n+\t*reason = SKB_DROP_REASON_GRE_TUNNEL_NOT_FOUND;\n \treturn PACKET_REJECT;\n }\n \n static int ip6erspan_rcv(struct sk_buff *skb,\n \t\t\t struct tnl_ptk_info *tpi,\n-\t\t\t int gre_hdr_len)\n+\t\t\t int gre_hdr_len,\n+\t\t\t enum skb_drop_reason *reason)\n {\n \tstruct erspan_base_hdr *ershdr;\n \tconst struct ipv6hdr *ipv6h;\n@@ -497,8 +502,10 @@ static int ip6erspan_rcv(struct sk_buff *skb,\n \tstruct ip6_tnl *tunnel;\n \tu8 ver;\n \n-\tif (unlikely(!pskb_may_pull(skb, sizeof(*ershdr))))\n+\tif (unlikely(!pskb_may_pull(skb, sizeof(*ershdr)))) {\n+\t\t*reason = SKB_DROP_REASON_HDR_TRUNC;\n \t\treturn PACKET_REJECT;\n+\t}\n \n \tipv6h = ipv6_hdr(skb);\n \tershdr = (struct erspan_base_hdr *)skb-\u003edata;\n@@ -510,13 +517,17 @@ static int ip6erspan_rcv(struct sk_buff *skb,\n \tif (tunnel) {\n \t\tint len = erspan_hdr_len(ver);\n \n-\t\tif (unlikely(!pskb_may_pull(skb, len)))\n+\t\tif (unlikely(!pskb_may_pull(skb, len))) {\n+\t\t\t*reason = SKB_DROP_REASON_HDR_TRUNC;\n \t\t\treturn PACKET_REJECT;\n+\t\t}\n \n \t\tif (__iptunnel_pull_header(skb, len,\n \t\t\t\t\t htons(ETH_P_TEB),\n-\t\t\t\t\t false, false) \u003c 0)\n+\t\t\t\t\t false, false) \u003c 0) {\n+\t\t\t*reason = SKB_DROP_REASON_NOMEM;\n \t\t\treturn PACKET_REJECT;\n+\t\t}\n \n \t\tif (tunnel-\u003eparms.collect_md) {\n \t\t\tstruct erspan_metadata *pkt_md, *md;\n@@ -532,8 +543,10 @@ static int ip6erspan_rcv(struct sk_buff *skb,\n \n \t\t\ttun_dst = ipv6_tun_rx_dst(skb, flags, tun_id,\n \t\t\t\t\t\t sizeof(*md));\n-\t\t\tif (!tun_dst)\n+\t\t\tif (!tun_dst) {\n+\t\t\t\t*reason = SKB_DROP_REASON_NOMEM;\n \t\t\t\treturn PACKET_REJECT;\n+\t\t\t}\n \n \t\t\t/* MUST set options_len before referencing options */\n \t\t\tinfo = \u0026tun_dst-\u003eu.tun_info;\n@@ -564,37 +577,40 @@ static int ip6erspan_rcv(struct sk_buff *skb,\n \t\treturn PACKET_RCVD;\n \t}\n \n+\t*reason = SKB_DROP_REASON_GRE_TUNNEL_NOT_FOUND;\n \treturn PACKET_REJECT;\n }\n \n static int gre_rcv(struct sk_buff *skb)\n {\n+\tenum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;\n \tstruct tnl_ptk_info tpi;\n-\tbool csum_err = false;\n \tint hdr_len;\n \n-\thdr_len = gre_parse_header(skb, \u0026tpi, \u0026csum_err, htons(ETH_P_IPV6), 0);\n+\thdr_len = gre_parse_header(skb, \u0026tpi, \u0026reason, htons(ETH_P_IPV6), 0);\n \tif (hdr_len \u003c 0)\n \t\tgoto drop;\n \n-\tif (iptunnel_pull_header(skb, hdr_len, tpi.proto, false))\n+\tif (iptunnel_pull_header(skb, hdr_len, tpi.proto, false)) {\n+\t\treason = SKB_DROP_REASON_NOMEM;\n \t\tgoto drop;\n+\t}\n \n \tif (unlikely(tpi.proto == htons(ETH_P_ERSPAN) ||\n \t\t tpi.proto == htons(ETH_P_ERSPAN2))) {\n-\t\tif (ip6erspan_rcv(skb, \u0026tpi, hdr_len) == PACKET_RCVD)\n+\t\tif (ip6erspan_rcv(skb, \u0026tpi, hdr_len, \u0026reason) == PACKET_RCVD)\n \t\t\treturn 0;\n \t\tgoto out;\n \t}\n \n-\tif (ip6gre_rcv(skb, \u0026tpi) == PACKET_RCVD)\n+\tif (ip6gre_rcv(skb, \u0026tpi, \u0026reason) == PACKET_RCVD)\n \t\treturn 0;\n \n out:\n \ticmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);\n drop:\n \tdev_core_stats_rx_dropped_inc(skb-\u003edev);\n-\tkfree_skb(skb);\n+\tkfree_skb_reason(skb, reason);\n \treturn 0;\n }\n \n@@ -677,10 +693,10 @@ static int prepare_ip6gre_xmit_ipv6(struct sk_buff *skb,\n \treturn 0;\n }\n \n-static int prepare_ip6gre_xmit_other(struct sk_buff *skb,\n-\t\t\t\t struct net_device *dev,\n-\t\t\t\t struct flowi6 *fl6, __u8 *dsfield,\n-\t\t\t\t int *encap_limit)\n+static void prepare_ip6gre_xmit_other(struct sk_buff *skb,\n+\t\t\t\t struct net_device *dev,\n+\t\t\t\t struct flowi6 *fl6, __u8 *dsfield,\n+\t\t\t\t int *encap_limit)\n {\n \tstruct ip6_tnl *t = netdev_priv(dev);\n \n@@ -700,8 +716,6 @@ static int prepare_ip6gre_xmit_other(struct sk_buff *skb,\n \t\tfl6-\u003eflowi6_mark = t-\u003eparms.fwmark;\n \n \tfl6-\u003eflowi6_uid = sock_net_uid(dev_net(dev), NULL);\n-\n-\treturn 0;\n }\n \n static struct ip_tunnel_info *skb_tunnel_info_txcheck(struct sk_buff *skb)\n@@ -718,7 +732,8 @@ static struct ip_tunnel_info *skb_tunnel_info_txcheck(struct sk_buff *skb)\n static netdev_tx_t __gre6_xmit(struct sk_buff *skb,\n \t\t\t struct net_device *dev, __u8 dsfield,\n \t\t\t struct flowi6 *fl6, int encap_limit,\n-\t\t\t __u32 *pmtu, __be16 proto)\n+\t\t\t __u32 *pmtu, __be16 proto,\n+\t\t\t enum skb_drop_reason *reason)\n {\n \tstruct ip6_tnl *tunnel = netdev_priv(dev);\n \tIP_TUNNEL_DECLARE_FLAGS(flags);\n@@ -742,8 +757,10 @@ static netdev_tx_t __gre6_xmit(struct sk_buff *skb,\n \n \t\ttun_info = skb_tunnel_info_txcheck(skb);\n \t\tif (IS_ERR(tun_info) ||\n-\t\t unlikely(ip_tunnel_info_af(tun_info) != AF_INET6))\n+\t\t unlikely(ip_tunnel_info_af(tun_info) != AF_INET6)) {\n+\t\t\t*reason = SKB_DROP_REASON_TUNNEL_TXINFO;\n \t\t\treturn -EINVAL;\n+\t\t}\n \n \t\tkey = \u0026tun_info-\u003ekey;\n \t\tmemset(fl6, 0, sizeof(*fl6));\n@@ -761,8 +778,11 @@ static netdev_tx_t __gre6_xmit(struct sk_buff *skb,\n \t\tip_tunnel_flags_and(flags, flags, key-\u003etun_flags);\n \t\ttun_hlen = gre_calc_hlen(flags);\n \n-\t\tif (skb_cow_head(skb, dev-\u003eneeded_headroom ?: tun_hlen + tunnel-\u003eencap_hlen))\n+\t\tif (skb_cow_head(skb, dev-\u003eneeded_headroom ?:\n+\t\t\t\t tun_hlen + tunnel-\u003eencap_hlen)) {\n+\t\t\t*reason = SKB_DROP_REASON_NOMEM;\n \t\t\treturn -ENOMEM;\n+\t\t}\n \n \t\tgre_build_header(skb, tun_hlen,\n \t\t\t\t flags, protocol,\n@@ -772,8 +792,10 @@ static netdev_tx_t __gre6_xmit(struct sk_buff *skb,\n \t\t\t\t 0);\n \n \t} else {\n-\t\tif (skb_cow_head(skb, dev-\u003eneeded_headroom ?: tunnel-\u003ehlen))\n+\t\tif (skb_cow_head(skb, dev-\u003eneeded_headroom ?: tunnel-\u003ehlen)) {\n+\t\t\t*reason = SKB_DROP_REASON_NOMEM;\n \t\t\treturn -ENOMEM;\n+\t\t}\n \n \t\tip_tunnel_flags_copy(flags, tunnel-\u003eparms.o_flags);\n \n@@ -785,10 +807,11 @@ static netdev_tx_t __gre6_xmit(struct sk_buff *skb,\n \t}\n \n \treturn ip6_tnl_xmit(skb, dev, dsfield, fl6, encap_limit, pmtu,\n-\t\t\t NEXTHDR_GRE);\n+\t\t\t NEXTHDR_GRE, reason);\n }\n \n-static inline int ip6gre_xmit_ipv4(struct sk_buff *skb, struct net_device *dev)\n+static inline int ip6gre_xmit_ipv4(struct sk_buff *skb, struct net_device *dev,\n+\t\t\t\t enum skb_drop_reason *reason)\n {\n \tstruct ip6_tnl *t = netdev_priv(dev);\n \tint encap_limit = -1;\n@@ -805,11 +828,13 @@ static inline int ip6gre_xmit_ipv4(struct sk_buff *skb, struct net_device *dev)\n \n \terr = gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT,\n \t\t\t\t\t\tt-\u003eparms.o_flags));\n-\tif (err)\n+\tif (err) {\n+\t\t*reason = SKB_DROP_REASON_NOMEM;\n \t\treturn -1;\n+\t}\n \n \terr = __gre6_xmit(skb, dev, dsfield, \u0026fl6, encap_limit, \u0026mtu,\n-\t\t\t skb-\u003eprotocol);\n+\t\t\t skb-\u003eprotocol, reason);\n \tif (err != 0) {\n \t\t/* XXX: send ICMP error even if DF is not set. */\n \t\tif (err == -EMSGSIZE)\n@@ -821,7 +846,8 @@ static inline int ip6gre_xmit_ipv4(struct sk_buff *skb, struct net_device *dev)\n \treturn 0;\n }\n \n-static inline int ip6gre_xmit_ipv6(struct sk_buff *skb, struct net_device *dev)\n+static inline int ip6gre_xmit_ipv6(struct sk_buff *skb, struct net_device *dev,\n+\t\t\t\t enum skb_drop_reason *reason)\n {\n \tstruct ip6_tnl *t = netdev_priv(dev);\n \tstruct ipv6hdr *ipv6h = ipv6_hdr(skb);\n@@ -831,19 +857,25 @@ static inline int ip6gre_xmit_ipv6(struct sk_buff *skb, struct net_device *dev)\n \t__u32 mtu;\n \tint err;\n \n-\tif (ipv6_addr_equal(\u0026t-\u003eparms.raddr, \u0026ipv6h-\u003esaddr))\n+\tif (ipv6_addr_equal(\u0026t-\u003eparms.raddr, \u0026ipv6h-\u003esaddr)) {\n+\t\t*reason = SKB_DROP_REASON_RECURSION_LIMIT;\n \t\treturn -1;\n+\t}\n \n \tif (!t-\u003eparms.collect_md \u0026\u0026\n-\t prepare_ip6gre_xmit_ipv6(skb, dev, \u0026fl6, \u0026dsfield, \u0026encap_limit))\n+\t prepare_ip6gre_xmit_ipv6(skb, dev, \u0026fl6, \u0026dsfield, \u0026encap_limit)) {\n+\t\t*reason = SKB_DROP_REASON_IPV6_BAD_EXTHDR;\n \t\treturn -1;\n+\t}\n \n \tif (gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT,\n-\t\t\t\t\t t-\u003eparms.o_flags)))\n+\t\t\t\t\t t-\u003eparms.o_flags))) {\n+\t\t*reason = SKB_DROP_REASON_NOMEM;\n \t\treturn -1;\n+\t}\n \n \terr = __gre6_xmit(skb, dev, dsfield, \u0026fl6, encap_limit,\n-\t\t\t \u0026mtu, skb-\u003eprotocol);\n+\t\t\t \u0026mtu, skb-\u003eprotocol, reason);\n \tif (err != 0) {\n \t\tif (err == -EMSGSIZE)\n \t\t\ticmpv6_ndo_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);\n@@ -853,7 +885,8 @@ static inline int ip6gre_xmit_ipv6(struct sk_buff *skb, struct net_device *dev)\n \treturn 0;\n }\n \n-static int ip6gre_xmit_other(struct sk_buff *skb, struct net_device *dev)\n+static int ip6gre_xmit_other(struct sk_buff *skb, struct net_device *dev,\n+\t\t\t enum skb_drop_reason *reason)\n {\n \tstruct ip6_tnl *t = netdev_priv(dev);\n \tint encap_limit = -1;\n@@ -862,15 +895,18 @@ static int ip6gre_xmit_other(struct sk_buff *skb, struct net_device *dev)\n \t__u32 mtu;\n \tint err;\n \n-\tif (!t-\u003eparms.collect_md \u0026\u0026\n-\t prepare_ip6gre_xmit_other(skb, dev, \u0026fl6, \u0026dsfield, \u0026encap_limit))\n-\t\treturn -1;\n+\tif (!t-\u003eparms.collect_md)\n+\t\tprepare_ip6gre_xmit_other(skb, dev, \u0026fl6,\n+\t\t\t\t\t \u0026dsfield, \u0026encap_limit);\n \n \terr = gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT,\n \t\t\t\t\t\tt-\u003eparms.o_flags));\n-\tif (err)\n+\tif (err) {\n+\t\t*reason = SKB_DROP_REASON_NOMEM;\n \t\treturn err;\n-\terr = __gre6_xmit(skb, dev, dsfield, \u0026fl6, encap_limit, \u0026mtu, skb-\u003eprotocol);\n+\t}\n+\terr = __gre6_xmit(skb, dev, dsfield, \u0026fl6, encap_limit, \u0026mtu,\n+\t\t\t skb-\u003eprotocol, reason);\n \n \treturn err;\n }\n@@ -878,16 +914,20 @@ static int ip6gre_xmit_other(struct sk_buff *skb, struct net_device *dev)\n static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,\n \tstruct net_device *dev)\n {\n+\tenum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;\n \tstruct ip_tunnel_info *tun_info = NULL;\n \tstruct ip6_tnl *t = netdev_priv(dev);\n \t__be16 payload_protocol;\n \tint ret;\n \n-\tif (!pskb_inet_may_pull(skb))\n+\treason = pskb_inet_may_pull_reason(skb);\n+\tif (reason)\n \t\tgoto tx_err;\n \n-\tif (!ip6_tnl_xmit_ctl(t, \u0026t-\u003eparms.laddr, \u0026t-\u003eparms.raddr))\n+\tif (!ip6_tnl_xmit_ctl(t, \u0026t-\u003eparms.laddr, \u0026t-\u003eparms.raddr)) {\n+\t\treason = SKB_DROP_REASON_DEV_READY;\n \t\tgoto tx_err;\n+\t}\n \n \tif (t-\u003eparms.collect_md)\n \t\ttun_info = skb_tunnel_info_txcheck(skb);\n@@ -895,13 +935,13 @@ static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,\n \tpayload_protocol = skb_protocol(skb, true);\n \tswitch (payload_protocol) {\n \tcase htons(ETH_P_IP):\n-\t\tret = ip6gre_xmit_ipv4(skb, dev);\n+\t\tret = ip6gre_xmit_ipv4(skb, dev, \u0026reason);\n \t\tbreak;\n \tcase htons(ETH_P_IPV6):\n-\t\tret = ip6gre_xmit_ipv6(skb, dev);\n+\t\tret = ip6gre_xmit_ipv6(skb, dev, \u0026reason);\n \t\tbreak;\n \tdefault:\n-\t\tret = ip6gre_xmit_other(skb, dev);\n+\t\tret = ip6gre_xmit_other(skb, dev, \u0026reason);\n \t\tbreak;\n \t}\n \n@@ -914,13 +954,14 @@ static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,\n \tif (!IS_ERR(tun_info))\n \t\tDEV_STATS_INC(dev, tx_errors);\n \tDEV_STATS_INC(dev, tx_dropped);\n-\tkfree_skb(skb);\n+\tkfree_skb_reason(skb, reason);\n \treturn NETDEV_TX_OK;\n }\n \n static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,\n \t\t\t\t\t struct net_device *dev)\n {\n+\tenum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;\n \tstruct ip_tunnel_info *tun_info = NULL;\n \tstruct ip6_tnl *t = netdev_priv(dev);\n \tstruct dst_entry *dst = skb_dst(skb);\n@@ -934,18 +975,25 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,\n \t__u32 mtu;\n \tint nhoff;\n \n-\tif (!pskb_inet_may_pull(skb))\n+\treason = pskb_inet_may_pull_reason(skb);\n+\tif (reason)\n \t\tgoto tx_err;\n \n-\tif (!ip6_tnl_xmit_ctl(t, \u0026t-\u003eparms.laddr, \u0026t-\u003eparms.raddr))\n+\tif (!ip6_tnl_xmit_ctl(t, \u0026t-\u003eparms.laddr, \u0026t-\u003eparms.raddr)) {\n+\t\treason = SKB_DROP_REASON_DEV_READY;\n \t\tgoto tx_err;\n+\t}\n \n-\tif (gre_handle_offloads(skb, false))\n+\tif (gre_handle_offloads(skb, false)) {\n+\t\treason = SKB_DROP_REASON_NOMEM;\n \t\tgoto tx_err;\n+\t}\n \n \tif (skb-\u003elen \u003e dev-\u003emtu + dev-\u003ehard_header_len) {\n-\t\tif (pskb_trim(skb, dev-\u003emtu + dev-\u003ehard_header_len))\n+\t\tif (pskb_trim(skb, dev-\u003emtu + dev-\u003ehard_header_len)) {\n+\t\t\treason = SKB_DROP_REASON_NOMEM;\n \t\t\tgoto tx_err;\n+\t\t}\n \t\ttruncate = true;\n \t}\n \n@@ -965,8 +1013,10 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,\n \t\t\ttruncate = true;\n \t}\n \n-\tif (skb_cow_head(skb, dev-\u003eneeded_headroom ?: t-\u003ehlen))\n+\tif (skb_cow_head(skb, dev-\u003eneeded_headroom ?: t-\u003ehlen)) {\n+\t\treason = SKB_DROP_REASON_NOMEM;\n \t\tgoto tx_err;\n+\t}\n \n \tIPCB(skb)-\u003eflags = 0;\n \n@@ -980,8 +1030,10 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,\n \n \t\ttun_info = skb_tunnel_info_txcheck(skb);\n \t\tif (IS_ERR(tun_info) ||\n-\t\t unlikely(ip_tunnel_info_af(tun_info) != AF_INET6))\n+\t\t unlikely(ip_tunnel_info_af(tun_info) != AF_INET6)) {\n+\t\t\treason = SKB_DROP_REASON_TUNNEL_TXINFO;\n \t\t\tgoto tx_err;\n+\t\t}\n \n \t\tkey = \u0026tun_info-\u003ekey;\n \t\tmemset(\u0026fl6, 0, sizeof(fl6));\n@@ -993,10 +1045,14 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,\n \n \t\tdsfield = key-\u003etos;\n \t\tif (!test_bit(IP_TUNNEL_ERSPAN_OPT_BIT,\n-\t\t\t tun_info-\u003ekey.tun_flags))\n+\t\t\t tun_info-\u003ekey.tun_flags)) {\n+\t\t\treason = SKB_DROP_REASON_TUNNEL_TXINFO;\n \t\t\tgoto tx_err;\n-\t\tif (tun_info-\u003eoptions_len \u003c sizeof(*md))\n+\t\t}\n+\t\tif (tun_info-\u003eoptions_len \u003c sizeof(*md)) {\n+\t\t\treason = SKB_DROP_REASON_TUNNEL_TXINFO;\n \t\t\tgoto tx_err;\n+\t\t}\n \t\tmd = ip_tunnel_info_opts(tun_info);\n \n \t\ttun_id = tunnel_id_to_key32(key-\u003etun_id);\n@@ -1014,6 +1070,7 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,\n \t\t\t\t\t truncate, false);\n \t\t\tproto = htons(ETH_P_ERSPAN2);\n \t\t} else {\n+\t\t\treason = SKB_DROP_REASON_UNHANDLED_PROTO;\n \t\t\tgoto tx_err;\n \t\t}\n \t} else {\n@@ -1024,11 +1081,16 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,\n \t\t\t\t\t\t \u0026dsfield, \u0026encap_limit);\n \t\t\tbreak;\n \t\tcase htons(ETH_P_IPV6):\n-\t\t\tif (ipv6_addr_equal(\u0026t-\u003eparms.raddr, \u0026ipv6_hdr(skb)-\u003esaddr))\n+\t\t\tif (ipv6_addr_equal(\u0026t-\u003eparms.raddr,\n+\t\t\t\t\t \u0026ipv6_hdr(skb)-\u003esaddr)) {\n+\t\t\t\treason = SKB_DROP_REASON_RECURSION_LIMIT;\n \t\t\t\tgoto tx_err;\n+\t\t\t}\n \t\t\tif (prepare_ip6gre_xmit_ipv6(skb, dev, \u0026fl6,\n-\t\t\t\t\t\t \u0026dsfield, \u0026encap_limit))\n+\t\t\t\t\t\t \u0026dsfield, \u0026encap_limit)) {\n+\t\t\t\treason = SKB_DROP_REASON_IPV6_BAD_EXTHDR;\n \t\t\t\tgoto tx_err;\n+\t\t\t}\n \t\t\tbreak;\n \t\tdefault:\n \t\t\tmemcpy(\u0026fl6, \u0026t-\u003efl.u.ip6, sizeof(fl6));\n@@ -1047,6 +1109,7 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,\n \t\t\t\t\t truncate, false);\n \t\t\tproto = htons(ETH_P_ERSPAN2);\n \t\t} else {\n+\t\t\treason = SKB_DROP_REASON_UNHANDLED_PROTO;\n \t\t\tgoto tx_err;\n \t\t}\n \n@@ -1065,7 +1128,7 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,\n \t\t\tdst-\u003eops-\u003eupdate_pmtu(dst, NULL, skb, mtu, false);\n \t}\n \terr = ip6_tnl_xmit(skb, dev, dsfield, \u0026fl6, encap_limit, \u0026mtu,\n-\t\t\t NEXTHDR_GRE);\n+\t\t\t NEXTHDR_GRE, \u0026reason);\n \tif (err != 0) {\n \t\t/* XXX: send ICMP error even if DF is not set. */\n \t\tif (err == -EMSGSIZE) {\n@@ -1084,7 +1147,7 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,\n \tif (!IS_ERR(tun_info))\n \t\tDEV_STATS_INC(dev, tx_errors);\n \tDEV_STATS_INC(dev, tx_dropped);\n-\tkfree_skb(skb);\n+\tkfree_skb_reason(skb, reason);\n \treturn NETDEV_TX_OK;\n }\n \ndiff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c\nindex d5ff50a2ac017..138151ed5797e 100644\n--- a/net/ipv6/ip6_tunnel.c\n+++ b/net/ipv6/ip6_tunnel.c\n@@ -813,6 +813,7 @@ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb,\n \t\t\t\t\t\tstruct sk_buff *skb),\n \t\t\t bool log_ecn_err)\n {\n+\tenum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;\n \tconst struct ipv6hdr *ipv6h;\n \tint nh, err;\n \n@@ -820,15 +821,22 @@ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb,\n \t test_bit(IP_TUNNEL_CSUM_BIT, tpi-\u003eflags)) {\n \t\tDEV_STATS_INC(tunnel-\u003edev, rx_crc_errors);\n \t\tDEV_STATS_INC(tunnel-\u003edev, rx_errors);\n+\t\treason = SKB_DROP_REASON_TNL_OPT_MISMATCH;\n \t\tgoto drop;\n \t}\n \n \tif (test_bit(IP_TUNNEL_SEQ_BIT, tunnel-\u003eparms.i_flags)) {\n-\t\tif (!test_bit(IP_TUNNEL_SEQ_BIT, tpi-\u003eflags) ||\n-\t\t (tunnel-\u003ei_seqno \u0026\u0026\n-\t\t (s32)(ntohl(tpi-\u003eseq) - tunnel-\u003ei_seqno) \u003c 0)) {\n+\t\tif (!test_bit(IP_TUNNEL_SEQ_BIT, tpi-\u003eflags)) {\n \t\t\tDEV_STATS_INC(tunnel-\u003edev, rx_fifo_errors);\n \t\t\tDEV_STATS_INC(tunnel-\u003edev, rx_errors);\n+\t\t\treason = SKB_DROP_REASON_TNL_OPT_MISMATCH;\n+\t\t\tgoto drop;\n+\t\t}\n+\t\tif (tunnel-\u003ei_seqno \u0026\u0026\n+\t\t (s32)(ntohl(tpi-\u003eseq) - tunnel-\u003ei_seqno) \u003c 0) {\n+\t\t\tDEV_STATS_INC(tunnel-\u003edev, rx_fifo_errors);\n+\t\t\tDEV_STATS_INC(tunnel-\u003edev, rx_errors);\n+\t\t\treason = SKB_DROP_REASON_TNL_OLD_SEQ;\n \t\t\tgoto drop;\n \t\t}\n \t\ttunnel-\u003ei_seqno = ntohl(tpi-\u003eseq) + 1;\n@@ -838,7 +846,8 @@ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb,\n \n \t/* Warning: All skb pointers will be invalidated! */\n \tif (tunnel-\u003edev-\u003etype == ARPHRD_ETHER) {\n-\t\tif (!pskb_may_pull(skb, ETH_HLEN)) {\n+\t\treason = pskb_may_pull_reason(skb, ETH_HLEN);\n+\t\tif (reason) {\n \t\t\tDEV_STATS_INC(tunnel-\u003edev, rx_length_errors);\n \t\t\tDEV_STATS_INC(tunnel-\u003edev, rx_errors);\n \t\t\tgoto drop;\n@@ -859,7 +868,8 @@ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb,\n \n \tskb_reset_network_header(skb);\n \n-\tif (skb_vlan_inet_prepare(skb, true)) {\n+\treason = skb_vlan_inet_prepare(skb, true);\n+\tif (reason) {\n \t\tDEV_STATS_INC(tunnel-\u003edev, rx_length_errors);\n \t\tDEV_STATS_INC(tunnel-\u003edev, rx_errors);\n \t\tgoto drop;\n@@ -881,6 +891,7 @@ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb,\n \t\tif (err \u003e 1) {\n \t\t\tDEV_STATS_INC(tunnel-\u003edev, rx_frame_errors);\n \t\t\tDEV_STATS_INC(tunnel-\u003edev, rx_errors);\n+\t\t\treason = SKB_DROP_REASON_IP_TUNNEL_ECN;\n \t\t\tgoto drop;\n \t\t}\n \t}\n@@ -898,7 +909,7 @@ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb,\n drop:\n \tif (tun_dst)\n \t\tdst_release((struct dst_entry *)tun_dst);\n-\tkfree_skb(skb);\n+\tkfree_skb_reason(skb, reason);\n \treturn 0;\n }\n \n@@ -1086,6 +1097,7 @@ EXPORT_SYMBOL_GPL(ip6_tnl_xmit_ctl);\n * @encap_limit: encapsulation limit\n * @pmtu: Path MTU is stored if packet is too big\n * @proto: next header value\n+ * @reason: drop reason, only written when the packet is dropped\n *\n * Description:\n * Build new header and do some sanity checks on the packet before sending\n@@ -1099,7 +1111,7 @@ EXPORT_SYMBOL_GPL(ip6_tnl_xmit_ctl);\n \n int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,\n \t\t struct flowi6 *fl6, int encap_limit, __u32 *pmtu,\n-\t\t __u8 proto)\n+\t\t __u8 proto, enum skb_drop_reason *reason)\n {\n \tstruct ip6_tnl *t = netdev_priv(dev);\n \tstruct net *net = t-\u003enet;\n@@ -1132,13 +1144,17 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,\n \t\t\tstruct neighbour *neigh;\n \t\t\tint addr_type;\n \n-\t\t\tif (!skb_dst(skb))\n+\t\t\tif (!skb_dst(skb)) {\n+\t\t\t\t*reason = SKB_DROP_REASON_NO_TX_TARGET;\n \t\t\t\tgoto tx_err_link_failure;\n+\t\t\t}\n \n \t\t\tneigh = dst_neigh_lookup(skb_dst(skb),\n \t\t\t\t\t\t \u0026ipv6_hdr(skb)-\u003edaddr);\n-\t\t\tif (!neigh)\n+\t\t\tif (!neigh) {\n+\t\t\t\t*reason = SKB_DROP_REASON_NEIGH_CREATEFAIL;\n \t\t\t\tgoto tx_err_link_failure;\n+\t\t\t}\n \n \t\t\taddr6 = (struct in6_addr *)\u0026neigh-\u003eprimary_key;\n \t\t\taddr_type = ipv6_addr_type(addr6);\n@@ -1151,8 +1167,10 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,\n \t\t} else if (payload_protocol == htons(ETH_P_IP)) {\n \t\t\tconst struct rtable *rt = skb_rtable(skb);\n \n-\t\t\tif (!rt)\n+\t\t\tif (!rt) {\n+\t\t\t\t*reason = SKB_DROP_REASON_NO_TX_TARGET;\n \t\t\t\tgoto tx_err_link_failure;\n+\t\t\t}\n \n \t\t\tif (rt-\u003ert_gw_family == AF_INET6)\n \t\t\t\tmemcpy(\u0026fl6-\u003edaddr, \u0026rt-\u003ert_gw6, sizeof(fl6-\u003edaddr));\n@@ -1169,8 +1187,10 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,\n \tif (use_cache)\n \t\tdst = dst_cache_get(\u0026t-\u003edst_cache);\n \n-\tif (!ip6_tnl_xmit_ctl(t, \u0026fl6-\u003esaddr, \u0026fl6-\u003edaddr))\n+\tif (!ip6_tnl_xmit_ctl(t, \u0026fl6-\u003esaddr, \u0026fl6-\u003edaddr)) {\n+\t\t*reason = SKB_DROP_REASON_DEV_READY;\n \t\tgoto tx_err_link_failure;\n+\t}\n \n \tif (!dst) {\n route_lookup:\n@@ -1179,18 +1199,23 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,\n \n \t\tdst = ip6_route_output(net, NULL, fl6);\n \n-\t\tif (dst-\u003eerror)\n+\t\tif (dst-\u003eerror) {\n+\t\t\t*reason = SKB_DROP_REASON_IP_OUTNOROUTES;\n \t\t\tgoto tx_err_link_failure;\n+\t\t}\n \t\tdst = xfrm_lookup(net, dst, flowi6_to_flowi(fl6), NULL, 0);\n \t\tif (IS_ERR(dst)) {\n \t\t\terr = PTR_ERR(dst);\n \t\t\tdst = NULL;\n+\t\t\t*reason = SKB_DROP_REASON_IP_OUTNOROUTES;\n \t\t\tgoto tx_err_link_failure;\n \t\t}\n \t\tif (t-\u003eparms.collect_md \u0026\u0026 ipv6_addr_any(\u0026fl6-\u003esaddr) \u0026\u0026\n \t\t ipv6_dev_get_saddr(net, ip6_dst_idev(dst)-\u003edev,\n-\t\t\t\t \u0026fl6-\u003edaddr, 0, \u0026fl6-\u003esaddr))\n+\t\t\t\t \u0026fl6-\u003edaddr, 0, \u0026fl6-\u003esaddr)) {\n+\t\t\t*reason = SKB_DROP_REASON_IP_OUTNOROUTES;\n \t\t\tgoto tx_err_link_failure;\n+\t\t}\n \t\tndst = dst;\n \t}\n \n@@ -1200,6 +1225,7 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,\n \t\tDEV_STATS_INC(dev, collisions);\n \t\tnet_warn_ratelimited(\"%s: Local routing loop detected!\\n\",\n \t\t\t\t t-\u003eparms.name);\n+\t\t*reason = SKB_DROP_REASON_RECURSION_LIMIT;\n \t\tgoto tx_err_dst_release;\n \t}\n \tmtu = dst6_mtu(dst) - eth_hlen - psh_hlen - t-\u003etun_hlen;\n@@ -1214,6 +1240,7 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,\n \tif (skb-\u003elen - t-\u003etun_hlen - eth_hlen \u003e mtu \u0026\u0026 !skb_is_gso(skb)) {\n \t\t*pmtu = mtu;\n \t\terr = -EMSGSIZE;\n+\t\t*reason = SKB_DROP_REASON_PKT_TOO_BIG;\n \t\tgoto tx_err_dst_release;\n \t}\n \n@@ -1236,12 +1263,16 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,\n \t */\n \tmax_headroom += LL_RESERVED_SPACE(tdev);\n \n-\tif (skb_cow_head(skb, max_headroom))\n+\tif (skb_cow_head(skb, max_headroom)) {\n+\t\t*reason = SKB_DROP_REASON_NOMEM;\n \t\tgoto tx_err_dst_release;\n+\t}\n \n \tif (t-\u003eparms.collect_md) {\n-\t\tif (t-\u003eencap.type != TUNNEL_ENCAP_NONE)\n+\t\tif (t-\u003eencap.type != TUNNEL_ENCAP_NONE) {\n+\t\t\t*reason = SKB_DROP_REASON_TNL_ENCAP;\n \t\t\tgoto tx_err_dst_release;\n+\t\t}\n \t} else {\n \t\tif (use_cache \u0026\u0026 ndst)\n \t\t\tdst_cache_set_ip6(\u0026t-\u003edst_cache, ndst, \u0026fl6-\u003esaddr);\n@@ -1265,8 +1296,10 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,\n \tip_tunnel_adj_headroom(dev, max_headroom);\n \n \terr = ip6_tnl_encap(skb, t, \u0026proto, fl6);\n-\tif (err)\n+\tif (err) {\n+\t\t*reason = SKB_DROP_REASON_TNL_ENCAP;\n \t\treturn err;\n+\t}\n \n \tif (encap_limit \u003e= 0) {\n \t\tinit_tel_txopt(\u0026opt, encap_limit);\n@@ -1295,7 +1328,7 @@ EXPORT_SYMBOL(ip6_tnl_xmit);\n \n static inline int\n ipxip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev,\n-\t\tu8 protocol)\n+\t\tu8 protocol, enum skb_drop_reason *reason)\n {\n \tstruct ip6_tnl *t = netdev_priv(dev);\n \tstruct ipv6hdr *ipv6h;\n@@ -1309,8 +1342,10 @@ ipxip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev,\n \tint err;\n \n \ttproto = READ_ONCE(t-\u003eparms.proto);\n-\tif (tproto != protocol \u0026\u0026 tproto != 0)\n+\tif (tproto != protocol \u0026\u0026 tproto != 0) {\n+\t\t*reason = SKB_DROP_REASON_UNHANDLED_PROTO;\n \t\treturn -1;\n+\t}\n \n \tif (t-\u003eparms.collect_md) {\n \t\tstruct ip_tunnel_info *tun_info;\n@@ -1318,8 +1353,10 @@ ipxip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev,\n \n \t\ttun_info = skb_tunnel_info(skb);\n \t\tif (unlikely(!tun_info || !(tun_info-\u003emode \u0026 IP_TUNNEL_INFO_TX) ||\n-\t\t\t ip_tunnel_info_af(tun_info) != AF_INET6))\n+\t\t\t ip_tunnel_info_af(tun_info) != AF_INET6)) {\n+\t\t\t*reason = SKB_DROP_REASON_TUNNEL_TXINFO;\n \t\t\treturn -1;\n+\t\t}\n \t\tkey = \u0026tun_info-\u003ekey;\n \t\tmemset(\u0026fl6, 0, sizeof(fl6));\n \t\tfl6.flowi6_proto = protocol;\n@@ -1356,6 +1393,7 @@ ipxip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev,\n \t\t\t\tif (tel-\u003eencap_limit == 0) {\n \t\t\t\t\ticmpv6_ndo_send(skb, ICMPV6_PARAMPROB,\n \t\t\t\t\t\t\tICMPV6_HDR_FIELD, offset + 2);\n+\t\t\t\t\t*reason = SKB_DROP_REASON_IPV6_BAD_EXTHDR;\n \t\t\t\t\treturn -1;\n \t\t\t\t}\n \t\t\t\tencap_limit = tel-\u003eencap_limit - 1;\n@@ -1397,13 +1435,15 @@ ipxip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev,\n \tfl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL);\n \tdsfield = INET_ECN_encapsulate(dsfield, orig_dsfield);\n \n-\tif (iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6))\n+\tif (iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6)) {\n+\t\t*reason = SKB_DROP_REASON_NOMEM;\n \t\treturn -1;\n+\t}\n \n \tskb_set_inner_ipproto(skb, protocol);\n \n \terr = ip6_tnl_xmit(skb, dev, dsfield, \u0026fl6, encap_limit, \u0026mtu,\n-\t\t\t protocol);\n+\t\t\t protocol, reason);\n \tif (err != 0) {\n \t\t/* XXX: send ICMP error even if DF is not set. */\n \t\tif (err == -EMSGSIZE)\n@@ -1427,11 +1467,13 @@ ipxip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev,\n static netdev_tx_t\n ip6_tnl_start_xmit(struct sk_buff *skb, struct net_device *dev)\n {\n+\tenum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;\n \tstruct ip6_tnl *t = netdev_priv(dev);\n \tu8 ipproto;\n \tint ret;\n \n-\tif (!pskb_inet_may_pull(skb))\n+\treason = pskb_inet_may_pull_reason(skb);\n+\tif (reason)\n \t\tgoto tx_err;\n \n \tswitch (skb-\u003eprotocol) {\n@@ -1439,18 +1481,21 @@ ip6_tnl_start_xmit(struct sk_buff *skb, struct net_device *dev)\n \t\tipproto = IPPROTO_IPIP;\n \t\tbreak;\n \tcase htons(ETH_P_IPV6):\n-\t\tif (ip6_tnl_addr_conflict(t, ipv6_hdr(skb)))\n+\t\tif (ip6_tnl_addr_conflict(t, ipv6_hdr(skb))) {\n+\t\t\treason = SKB_DROP_REASON_RECURSION_LIMIT;\n \t\t\tgoto tx_err;\n+\t\t}\n \t\tipproto = IPPROTO_IPV6;\n \t\tbreak;\n \tcase htons(ETH_P_MPLS_UC):\n \t\tipproto = IPPROTO_MPLS;\n \t\tbreak;\n \tdefault:\n+\t\treason = SKB_DROP_REASON_UNHANDLED_PROTO;\n \t\tgoto tx_err;\n \t}\n \n-\tret = ipxip6_tnl_xmit(skb, dev, ipproto);\n+\tret = ipxip6_tnl_xmit(skb, dev, ipproto, \u0026reason);\n \tif (ret \u003c 0)\n \t\tgoto tx_err;\n \n@@ -1459,7 +1504,7 @@ ip6_tnl_start_xmit(struct sk_buff *skb, struct net_device *dev)\n tx_err:\n \tDEV_STATS_INC(dev, tx_errors);\n \tDEV_STATS_INC(dev, tx_dropped);\n-\tkfree_skb(skb);\n+\tkfree_skb_reason(skb, reason);\n \treturn NETDEV_TX_OK;\n }\n \n"
} |
| 2/1 | 2026/09/18 04:45 | agent | patch-evaluator |
1mModel:core Args: null Results: {
"EnableConfigs": [
"NET_IPGRE",
"NET_IPGRE_DEMUX",
"NET_IP_TUNNEL",
"IPV6_GRE",
"IPV6_TUNNEL"
],
"FocusSymbols": [
"gre_parse_header",
"ip_tunnel_rcv",
"ip_tunnel_xmit",
"ip_md_tunnel_xmit",
"ipgre_xmit",
"erspan_rcv",
"ip6_tnl_xmit",
"ip6_tnl_start_xmit",
"ip6gre_tunnel_xmit",
"ip6erspan_tunnel_xmit"
],
"Reasoning": "The patch modifies IPv4/IPv6 GRE and IP tunneling drivers to track and propagate granular skb drop reasons (SKB_DROP_REASON_*) to kfree_skb_reason(). This alters function signatures, pointer propagation, and branching/validation logic across rx and tx paths in reachable virtual networking drivers. Fuzzing is warranted to verify that new drop paths, pointer handling, and refactored conditional checks behave correctly under malformed or edge-case tunnel packets.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.
IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.
================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.
- REACHABILITY IS THE PRIMARY GATE:
Fuzzing can only discover bugs in code that can actually execute in standard virtualized
environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
regardless of whether it adds assertions or complex logic.
- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
Patch authors routinely label changes as "cleanups", "refactorings", or state
"No functional change intended". Do NOT take these claims at face value.
Code refactorings that rearrange logic, introduce helper functions, or alter state management
in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
If reachable executable code is modified or refactored, it MUST be fuzzed.
- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
Even if the author believes the invariant always holds, fuzzing is essential to verify whether
an unusual sequence of operations can violate it.
================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:
- Non-kernel and non-executable changes:
* Modifications to Documentation/, comments, or spelling fixes.
* User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
that do not affect the compiled kernel image (vmlinux) or kernel modules.
* Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
that do not alter control flow or data structures.
* Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
* Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
* Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
physical PCI hot-unplug or manual sysfs driver unbinding.
* Code paths exclusive to architectures other than the target architecture.
================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
* Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
* Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
* Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
* Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).
================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:
- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
Do NOT list generic, ubiquitous functions called by almost every program in the corpus
(including, but not limited to: general memory allocators and deallocators, page fault
and trap handlers, or core synchronization primitives; this is not an exhaustive list).
Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
which severely dilutes fuzzing effort away from the actual changes.
- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
List functions that specifically implement the logic being added or altered, or direct API entrypoints
for the subsystem feature under review.
- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).
================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit 69b321fab7f696a20d12dd8898306fc1b8f69cd2
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Sep 18 04:45:43 2026 +0000
syz-cluster: applied patch under review
diff --git a/include/net/dropreason-core.h b/include/net/dropreason-core.h
index 12f9096515912..30378a0d22726 100644
--- a/include/net/dropreason-core.h
+++ b/include/net/dropreason-core.h
@@ -129,6 +129,12 @@
FN(PSP_INPUT) \
FN(PSP_OUTPUT) \
FN(RECURSION_LIMIT) \
+ FN(TNL_OPT_MISMATCH) \
+ FN(TNL_OLD_SEQ) \
+ FN(GRE_INVALID_HDR) \
+ FN(GRE_CSUM) \
+ FN(GRE_TUNNEL_NOT_FOUND) \
+ FN(TNL_ENCAP) \
FNe(MAX)
/**
@@ -612,6 +618,38 @@ enum skb_drop_reason {
SKB_DROP_REASON_PSP_OUTPUT,
/** @SKB_DROP_REASON_RECURSION_LIMIT: Dead loop on virtual device. */
SKB_DROP_REASON_RECURSION_LIMIT,
+ /**
+ * @SKB_DROP_REASON_TNL_OPT_MISMATCH: the tunnel options
+ * carried by the packet do not match the tunnel configuration, e.g.
+ * a GRE tunnel configured with 'icsum' or 'iseq' received a packet
+ * with no checksum or no sequence number.
+ */
+ SKB_DROP_REASON_TNL_OPT_MISMATCH,
+ /**
+ * @SKB_DROP_REASON_TNL_OLD_SEQ: the sequence number carried
+ * by the packet is older than the one expected by the tunnel, e.g.
+ * after the remote endpoint restarted and reset its sequence
+ * numbering.
+ */
+ SKB_DROP_REASON_TNL_OLD_SEQ,
+ /**
+ * @SKB_DROP_REASON_GRE_INVALID_HDR: the GRE header is invalid, e.g.
+ * an unsupported version or the routing bit is set.
+ */
+ SKB_DROP_REASON_GRE_INVALID_HDR,
+ /** @SKB_DROP_REASON_GRE_CSUM: GRE checksum error */
+ SKB_DROP_REASON_GRE_CSUM,
+ /**
+ * @SKB_DROP_REASON_GRE_TUNNEL_NOT_FOUND: no GRE tunnel found for the
+ * endpoints and the key the packet carries.
+ */
+ SKB_DROP_REASON_GRE_TUNNEL_NOT_FOUND,
+ /**
+ * @SKB_DROP_REASON_TNL_ENCAP: failed to build the
+ * encapsulation header of a tunnel, e.g. an unknown or
+ * unregistered encapsulation type.
+ */
+ SKB_DROP_REASON_TNL_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/include/net/gre.h b/include/net/gre.h
index b55f67ecd2fc4..a63f26c3f78ed 100644
--- a/include/net/gre.h
+++ b/include/net/gre.h
@@ -33,7 +33,7 @@ int gre_add_protocol(const struct gre_protocol *proto, u8 version);
int gre_del_protocol(const struct gre_protocol *proto, u8 version);
int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
- bool *csum_err, __be16 proto, int nhs);
+ enum skb_drop_reason *reason, __be16 proto, int nhs);
static inline bool netif_is_gretap(const struct net_device *dev)
{
diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h
index b99805ee2fd14..95f6d12254df6 100644
--- a/include/net/ip6_tunnel.h
+++ b/include/net/ip6_tunnel.h
@@ -143,7 +143,8 @@ int ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb,
int ip6_tnl_xmit_ctl(struct ip6_tnl *t, const struct in6_addr *laddr,
const struct in6_addr *raddr);
int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,
- struct flowi6 *fl6, int encap_limit, __u32 *pmtu, __u8 proto);
+ struct flowi6 *fl6, int encap_limit, __u32 *pmtu, __u8 proto,
+ enum skb_drop_reason *reason);
__u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 *raw);
__u32 ip6_tnl_get_cap(struct ip6_tnl *t, const struct in6_addr *laddr,
const struct in6_addr *raddr);
diff --git a/net/ipv4/gre_demux.c b/net/ipv4/gre_demux.c
index 96fd7dc6d82dd..c5d3847848efd 100644
--- a/net/ipv4/gre_demux.c
+++ b/net/ipv4/gre_demux.c
@@ -58,26 +58,44 @@ EXPORT_SYMBOL_GPL(gre_del_protocol);
/* Fills in tpi and returns header length to be pulled.
* Note that caller must use pskb_may_pull() before pulling GRE header.
+ *
+ * @reason is only written when the header is rejected, so the caller has
+ * to initialise it before the call.
+ *
+ * A NULL @reason means that the caller is not interested in the drop
+ * reason, and also that a checksum failure must not be reported: the
+ * checksum is still computed, the packet is just not rejected over it.
+ * This is what the ICMP error handlers need, as they only get a part of
+ * the original packet.
*/
int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
- bool *csum_err, __be16 proto, int nhs)
+ enum skb_drop_reason *reason, __be16 proto, int nhs)
{
const struct gre_base_hdr *greh;
__be32 *options;
int hdr_len;
- if (unlikely(!pskb_may_pull(skb, nhs + sizeof(struct gre_base_hdr))))
+ if (unlikely(!pskb_may_pull(skb, nhs + sizeof(struct gre_base_hdr)))) {
+ if (reason)
+ *reason = SKB_DROP_REASON_HDR_TRUNC;
return -EINVAL;
+ }
greh = (struct gre_base_hdr *)(skb->data + nhs);
- if (unlikely(greh->flags & (GRE_VERSION | GRE_ROUTING)))
+ if (unlikely(greh->flags & (GRE_VERSION | GRE_ROUTING))) {
+ if (reason)
+ *reason = SKB_DROP_REASON_GRE_INVALID_HDR;
return -EINVAL;
+ }
gre_flags_to_tnl_flags(tpi->flags, greh->flags);
hdr_len = gre_calc_hlen(tpi->flags);
- if (!pskb_may_pull(skb, nhs + hdr_len))
+ if (!pskb_may_pull(skb, nhs + hdr_len)) {
+ if (reason)
+ *reason = SKB_DROP_REASON_HDR_TRUNC;
return -EINVAL;
+ }
greh = (struct gre_base_hdr *)(skb->data + nhs);
tpi->proto = greh->protocol;
@@ -87,8 +105,8 @@ int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
if (!skb_checksum_simple_validate(skb)) {
skb_checksum_try_convert(skb, IPPROTO_GRE,
null_compute_pseudo);
- } else if (csum_err) {
- *csum_err = true;
+ } else if (reason) {
+ *reason = SKB_DROP_REASON_GRE_CSUM;
return -EINVAL;
}
@@ -116,8 +134,11 @@ int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
val = skb_header_pointer(skb, nhs + hdr_len,
sizeof(_val), &_val);
- if (!val)
+ if (!val) {
+ if (reason)
+ *reason = SKB_DROP_REASON_HDR_TRUNC;
return -EINVAL;
+ }
tpi->proto = proto;
if ((*val & 0xF0) != 0x40)
hdr_len += 4;
@@ -132,8 +153,11 @@ int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
greh->protocol == htons(ETH_P_ERSPAN2)) {
struct erspan_base_hdr *ershdr;
- if (!pskb_may_pull(skb, nhs + hdr_len + sizeof(*ershdr)))
+ if (!pskb_may_pull(skb, nhs + hdr_len + sizeof(*ershdr))) {
+ if (reason)
+ *reason = SKB_DROP_REASON_HDR_TRUNC;
return -EINVAL;
+ }
ershdr = (struct erspan_base_hdr *)(skb->data + nhs + hdr_len);
tpi->key = cpu_to_be32(get_session_id(ershdr));
@@ -145,16 +169,20 @@ EXPORT_SYMBOL(gre_parse_header);
static int gre_rcv(struct sk_buff *skb)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
const struct gre_protocol *proto;
u8 ver;
int ret;
- if (!pskb_may_pull(skb, 12))
+ reason = pskb_may_pull_reason(skb, 12);
+ if (reason)
goto drop;
ver = skb->data[1]&0x7f;
- if (ver >= GREPROTO_MAX)
+ if (ver >= GREPROTO_MAX) {
+ reason = SKB_DROP_REASON_UNHANDLED_PROTO;
goto drop;
+ }
rcu_read_lock();
proto = rcu_dereference(gre_proto[ver]);
@@ -167,11 +195,11 @@ static int gre_rcv(struct sk_buff *skb)
drop_nohandler:
rcu_read_unlock();
dev_core_stats_rx_nohandler_inc(skb->dev);
- kfree_skb(skb);
+ kfree_skb_reason(skb, SKB_DROP_REASON_UNHANDLED_PROTO);
return NET_RX_DROP;
drop:
dev_core_stats_rx_dropped_inc(skb->dev);
- kfree_skb(skb);
+ kfree_skb_reason(skb, reason);
return NET_RX_DROP;
}
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 5e877018e006c..71360af429ea7 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -265,7 +265,7 @@ static bool is_erspan_type1(int gre_hdr_len)
}
static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
- int gre_hdr_len)
+ int gre_hdr_len, enum skb_drop_reason *reason)
{
struct net *net = dev_net(skb->dev);
struct metadata_dst *tun_dst = NULL;
@@ -289,8 +289,10 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
iph->saddr, iph->daddr, 0);
} else {
if (unlikely(!pskb_may_pull(skb,
- gre_hdr_len + sizeof(*ershdr))))
+ gre_hdr_len + sizeof(*ershdr)))) {
+ *reason = SKB_DROP_REASON_HDR_TRUNC;
return PACKET_REJECT;
+ }
ershdr = (struct erspan_base_hdr *)(skb->data + gre_hdr_len);
ver = ershdr->ver;
@@ -306,8 +308,10 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
else
len = gre_hdr_len + erspan_hdr_len(ver);
- if (unlikely(!pskb_may_pull(skb, len)))
+ if (unlikely(!pskb_may_pull(skb, len))) {
+ *reason = SKB_DROP_REASON_HDR_TRUNC;
return PACKET_REJECT;
+ }
if (__iptunnel_pull_header(skb,
len,
@@ -327,8 +331,10 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
tun_dst = ip_tun_rx_dst(skb, flags,
tun_id, sizeof(*md));
- if (!tun_dst)
+ if (!tun_dst) {
+ *reason = SKB_DROP_REASON_NOMEM;
return PACKET_REJECT;
+ }
/* MUST set options_len before referencing options */
info = &tun_dst->u.tun_info;
@@ -356,15 +362,17 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
ip_tunnel_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error);
return PACKET_RCVD;
}
+ *reason = SKB_DROP_REASON_GRE_TUNNEL_NOT_FOUND;
return PACKET_REJECT;
drop:
- kfree_skb(skb);
+ kfree_skb_reason(skb, SKB_DROP_REASON_NOMEM);
return PACKET_RCVD;
}
static int __ipgre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
- struct ip_tunnel_net *itn, int hdr_len, bool raw_proto)
+ struct ip_tunnel_net *itn, int hdr_len, bool raw_proto,
+ enum skb_drop_reason *reason)
{
struct metadata_dst *tun_dst = NULL;
const struct iphdr *iph;
@@ -400,22 +408,25 @@ static int __ipgre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
tun_id = key32_to_tunnel_id(tpi->key);
tun_dst = ip_tun_rx_dst(skb, flags, tun_id, 0);
- if (!tun_dst)
+ if (!tun_dst) {
+ *reason = SKB_DROP_REASON_NOMEM;
return PACKET_REJECT;
+ }
}
ip_tunnel_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error);
return PACKET_RCVD;
}
+ *reason = SKB_DROP_REASON_GRE_TUNNEL_NOT_FOUND;
return PACKET_NEXT;
drop:
- kfree_skb(skb);
+ kfree_skb_reason(skb, SKB_DROP_REASON_NOMEM);
return PACKET_RCVD;
}
static int ipgre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
- int hdr_len)
+ int hdr_len, enum skb_drop_reason *reason)
{
struct net *net = dev_net(skb->dev);
struct ip_tunnel_net *itn;
@@ -426,21 +437,21 @@ static int ipgre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
else
itn = net_generic(net, ipgre_net_id);
- res = __ipgre_rcv(skb, tpi, itn, hdr_len, false);
+ res = __ipgre_rcv(skb, tpi, itn, hdr_len, false, reason);
if (res == PACKET_NEXT && tpi->proto == htons(ETH_P_TEB)) {
/* ipgre tunnels in collect metadata mode should receive
* also ETH_P_TEB traffic.
*/
itn = net_generic(net, ipgre_net_id);
- res = __ipgre_rcv(skb, tpi, itn, hdr_len, true);
+ res = __ipgre_rcv(skb, tpi, itn, hdr_len, true, reason);
}
return res;
}
static int gre_rcv(struct sk_buff *skb)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct tnl_ptk_info tpi;
- bool csum_err = false;
int hdr_len;
#ifdef CONFIG_NET_IPGRE_BROADCAST
@@ -451,25 +462,25 @@ static int gre_rcv(struct sk_buff *skb)
}
#endif
- hdr_len = gre_parse_header(skb, &tpi, &csum_err, htons(ETH_P_IP), 0);
+ hdr_len = gre_parse_header(skb, &tpi, &reason, htons(ETH_P_IP), 0);
if (hdr_len < 0)
goto drop;
if (unlikely(tpi.proto == htons(ETH_P_ERSPAN) ||
tpi.proto == htons(ETH_P_ERSPAN2))) {
- if (erspan_rcv(skb, &tpi, hdr_len) == PACKET_RCVD)
+ if (erspan_rcv(skb, &tpi, hdr_len, &reason) == PACKET_RCVD)
return 0;
goto out;
}
- if (ipgre_rcv(skb, &tpi, hdr_len) == PACKET_RCVD)
+ if (ipgre_rcv(skb, &tpi, hdr_len, &reason) == PACKET_RCVD)
return 0;
out:
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
drop:
dev_core_stats_rx_dropped_inc(skb->dev);
- kfree_skb(skb);
+ kfree_skb_reason(skb, reason);
return 0;
}
@@ -496,6 +507,7 @@ static int gre_handle_offloads(struct sk_buff *skb, bool csum)
static void gre_fb_xmit(struct sk_buff *skb, struct net_device *dev,
__be16 proto)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct ip_tunnel *tunnel = netdev_priv(dev);
IP_TUNNEL_DECLARE_FLAGS(flags) = { };
struct ip_tunnel_info *tun_info;
@@ -504,19 +516,25 @@ static void gre_fb_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 err_free_skb;
+ }
key = &tun_info->key;
tunnel_hlen = gre_calc_hlen(key->tun_flags);
- if (skb_cow_head(skb, dev->needed_headroom))
+ if (skb_cow_head(skb, dev->needed_headroom)) {
+ reason = SKB_DROP_REASON_NOMEM;
goto err_free_skb;
+ }
/* Push Tunnel header. */
if (gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT,
- tunnel->parms.o_flags)))
+ tunnel->parms.o_flags))) {
+ reason = SKB_DROP_REASON_NOMEM;
goto err_free_skb;
+ }
__set_bit(IP_TUNNEL_CSUM_BIT, flags);
__set_bit(IP_TUNNEL_KEY_BIT, flags);
@@ -533,12 +551,13 @@ static void gre_fb_xmit(struct sk_buff *skb, struct net_device *dev,
return;
err_free_skb:
- kfree_skb(skb);
+ kfree_skb_reason(skb, reason);
DEV_STATS_INC(dev, tx_dropped);
}
static void erspan_fb_xmit(struct sk_buff *skb, struct net_device *dev)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct ip_tunnel *tunnel = netdev_priv(dev);
IP_TUNNEL_DECLARE_FLAGS(flags) = { };
struct ip_tunnel_info *tun_info;
@@ -552,29 +571,41 @@ static void erspan_fb_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 err_free_skb;
+ }
key = &tun_info->key;
- if (!test_bit(IP_TUNNEL_ERSPAN_OPT_BIT, tun_info->key.tun_flags))
+ if (!test_bit(IP_TUNNEL_ERSPAN_OPT_BIT, tun_info->key.tun_flags)) {
+ reason = SKB_DROP_REASON_TUNNEL_TXINFO;
goto err_free_skb;
- if (tun_info->options_len < sizeof(*md))
+ }
+ if (tun_info->options_len < sizeof(*md)) {
+ reason = SKB_DROP_REASON_TUNNEL_TXINFO;
goto err_free_skb;
+ }
md = ip_tunnel_info_opts(tun_info);
/* ERSPAN has fixed 8 byte GRE header */
version = md->version;
tunnel_hlen = 8 + erspan_hdr_len(version);
- if (skb_cow_head(skb, dev->needed_headroom))
+ if (skb_cow_head(skb, dev->needed_headroom)) {
+ reason = SKB_DROP_REASON_NOMEM;
goto err_free_skb;
+ }
- if (gre_handle_offloads(skb, false))
+ if (gre_handle_offloads(skb, false)) {
+ reason = SKB_DROP_REASON_NOMEM;
goto err_free_skb;
+ }
if (skb->len > dev->mtu + dev->hard_header_len) {
- if (pskb_trim(skb, dev->mtu + dev->hard_header_len))
+ if (pskb_trim(skb, dev->mtu + dev->hard_header_len)) {
+ reason = SKB_DROP_REASON_NOMEM;
goto err_free_skb;
+ }
truncate = true;
}
@@ -606,6 +637,7 @@ static void erspan_fb_xmit(struct sk_buff *skb, struct net_device *dev)
truncate, true);
proto = htons(ETH_P_ERSPAN2);
} else {
+ reason = SKB_DROP_REASON_UNHANDLED_PROTO;
goto err_free_skb;
}
@@ -618,7 +650,7 @@ static void erspan_fb_xmit(struct sk_buff *skb, struct net_device *dev)
return;
err_free_skb:
- kfree_skb(skb);
+ kfree_skb_reason(skb, reason);
DEV_STATS_INC(dev, tx_dropped);
}
@@ -649,11 +681,13 @@ static int gre_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
static netdev_tx_t ipgre_xmit(struct sk_buff *skb,
struct net_device *dev)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct ip_tunnel *tunnel = netdev_priv(dev);
IP_TUNNEL_DECLARE_FLAGS(flags);
const struct iphdr *tnl_params;
- if (!pskb_inet_may_pull(skb))
+ reason = pskb_inet_may_pull_reason(skb);
+ if (reason)
goto free_skb;
if (tunnel->collect_md) {
@@ -664,10 +698,13 @@ static netdev_tx_t ipgre_xmit(struct sk_buff *skb,
if (dev->header_ops) {
int pull_len = tunnel->hlen + sizeof(struct iphdr);
- if (skb_cow_head(skb, 0))
+ if (skb_cow_head(skb, 0)) {
+ reason = SKB_DROP_REASON_NOMEM;
goto free_skb;
+ }
- if (!pskb_may_pull(skb, pull_len))
+ reason = pskb_may_pull_reason(skb, pull_len);
+ if (reason)
goto free_skb;
tnl_params = (const struct iphdr *)skb->data;
@@ -677,25 +714,31 @@ static netdev_tx_t ipgre_xmit(struct sk_buff *skb,
skb_reset_mac_header(skb);
if (skb->ip_summed == CHECKSUM_PARTIAL &&
- skb_checksum_start(skb) < skb->data)
+ skb_checksum_start(skb) < skb->data) {
+ reason = SKB_DROP_REASON_SKB_CSUM;
goto free_skb;
+ }
} else {
- if (skb_cow_head(skb, dev->needed_headroom))
+ if (skb_cow_head(skb, dev->needed_headroom)) {
+ reason = SKB_DROP_REASON_NOMEM;
goto free_skb;
+ }
tnl_params = &tunnel->parms.iph;
}
ip_tunnel_flags_copy(flags, tunnel->parms.o_flags);
- if (gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT, flags)))
+ if (gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT, flags))) {
+ reason = SKB_DROP_REASON_NOMEM;
goto free_skb;
+ }
__gre_xmit(skb, dev, tnl_params, skb->protocol, flags);
return NETDEV_TX_OK;
free_skb:
- kfree_skb(skb);
+ kfree_skb_reason(skb, reason);
DEV_STATS_INC(dev, tx_dropped);
return NETDEV_TX_OK;
}
@@ -703,12 +746,14 @@ static netdev_tx_t ipgre_xmit(struct sk_buff *skb,
static netdev_tx_t erspan_xmit(struct sk_buff *skb,
struct net_device *dev)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct ip_tunnel *tunnel = netdev_priv(dev);
IP_TUNNEL_DECLARE_FLAGS(flags);
bool truncate = false;
__be16 proto;
- if (!pskb_inet_may_pull(skb))
+ reason = pskb_inet_may_pull_reason(skb);
+ if (reason)
goto free_skb;
if (tunnel->collect_md) {
@@ -716,15 +761,21 @@ static netdev_tx_t erspan_xmit(struct sk_buff *skb,
return NETDEV_TX_OK;
}
- if (gre_handle_offloads(skb, false))
+ if (gre_handle_offloads(skb, false)) {
+ reason = SKB_DROP_REASON_NOMEM;
goto free_skb;
+ }
- if (skb_cow_head(skb, dev->needed_headroom))
+ if (skb_cow_head(skb, dev->needed_headroom)) {
+ reason = SKB_DROP_REASON_NOMEM;
goto free_skb;
+ }
if (skb->len > dev->mtu + dev->hard_header_len) {
- if (pskb_trim(skb, dev->mtu + dev->hard_header_len))
+ if (pskb_trim(skb, dev->mtu + dev->hard_header_len)) {
+ reason = SKB_DROP_REASON_NOMEM;
goto free_skb;
+ }
truncate = true;
}
@@ -745,6 +796,7 @@ static netdev_tx_t erspan_xmit(struct sk_buff *skb,
truncate, true);
proto = htons(ETH_P_ERSPAN2);
} else {
+ reason = SKB_DROP_REASON_UNHANDLED_PROTO;
goto free_skb;
}
@@ -753,7 +805,7 @@ static netdev_tx_t erspan_xmit(struct sk_buff *skb,
return NETDEV_TX_OK;
free_skb:
- kfree_skb(skb);
+ kfree_skb_reason(skb, reason);
DEV_STATS_INC(dev, tx_dropped);
return NETDEV_TX_OK;
}
@@ -761,10 +813,12 @@ static netdev_tx_t erspan_xmit(struct sk_buff *skb,
static netdev_tx_t gre_tap_xmit(struct sk_buff *skb,
struct net_device *dev)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct ip_tunnel *tunnel = netdev_priv(dev);
IP_TUNNEL_DECLARE_FLAGS(flags);
- if (!pskb_inet_may_pull(skb))
+ reason = pskb_inet_may_pull_reason(skb);
+ if (reason)
goto free_skb;
if (tunnel->collect_md) {
@@ -774,17 +828,21 @@ static netdev_tx_t gre_tap_xmit(struct sk_buff *skb,
ip_tunnel_flags_copy(flags, tunnel->parms.o_flags);
- if (gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT, flags)))
+ if (gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT, flags))) {
+ reason = SKB_DROP_REASON_NOMEM;
goto free_skb;
+ }
- if (skb_cow_head(skb, dev->needed_headroom))
+ if (skb_cow_head(skb, dev->needed_headroom)) {
+ reason = SKB_DROP_REASON_NOMEM;
goto free_skb;
+ }
__gre_xmit(skb, dev, &tunnel->parms.iph, htons(ETH_P_TEB), flags);
return NETDEV_TX_OK;
free_skb:
- kfree_skb(skb);
+ kfree_skb_reason(skb, reason);
DEV_STATS_INC(dev, tx_dropped);
return NETDEV_TX_OK;
}
diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
index 0875474a578a4..66cb0b86fa79a 100644
--- a/net/ipv4/ip_tunnel.c
+++ b/net/ipv4/ip_tunnel.c
@@ -384,6 +384,7 @@ int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb,
const struct tnl_ptk_info *tpi, struct metadata_dst *tun_dst,
bool log_ecn_error)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
const struct iphdr *iph = ip_hdr(skb);
int nh, err;
@@ -398,14 +399,22 @@ int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb,
test_bit(IP_TUNNEL_CSUM_BIT, tpi->flags)) {
DEV_STATS_INC(tunnel->dev, rx_crc_errors);
DEV_STATS_INC(tunnel->dev, rx_errors);
+ reason = SKB_DROP_REASON_TNL_OPT_MISMATCH;
goto drop;
}
if (test_bit(IP_TUNNEL_SEQ_BIT, tunnel->parms.i_flags)) {
- if (!test_bit(IP_TUNNEL_SEQ_BIT, tpi->flags) ||
- (tunnel->i_seqno && (s32)(ntohl(tpi->seq) - tunnel->i_seqno) < 0)) {
+ if (!test_bit(IP_TUNNEL_SEQ_BIT, tpi->flags)) {
DEV_STATS_INC(tunnel->dev, rx_fifo_errors);
DEV_STATS_INC(tunnel->dev, rx_errors);
+ reason = SKB_DROP_REASON_TNL_OPT_MISMATCH;
+ goto drop;
+ }
+ if (tunnel->i_seqno &&
+ (s32)(ntohl(tpi->seq) - tunnel->i_seqno) < 0) {
+ DEV_STATS_INC(tunnel->dev, rx_fifo_errors);
+ DEV_STATS_INC(tunnel->dev, rx_errors);
+ reason = SKB_DROP_REASON_TNL_OLD_SEQ;
goto drop;
}
tunnel->i_seqno = ntohl(tpi->seq) + 1;
@@ -419,7 +428,8 @@ int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb,
skb_set_network_header(skb, (tunnel->dev->type == ARPHRD_ETHER) ? ETH_HLEN : 0);
- if (!pskb_inet_may_pull(skb)) {
+ reason = pskb_inet_may_pull_reason(skb);
+ if (reason) {
DEV_STATS_INC(tunnel->dev, rx_length_errors);
DEV_STATS_INC(tunnel->dev, rx_errors);
goto drop;
@@ -434,6 +444,7 @@ int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb,
if (err > 1) {
DEV_STATS_INC(tunnel->dev, rx_frame_errors);
DEV_STATS_INC(tunnel->dev, rx_errors);
+ reason = SKB_DROP_REASON_IP_TUNNEL_ECN;
goto drop;
}
}
@@ -457,7 +468,7 @@ int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb,
drop:
if (tun_dst)
dst_release((struct dst_entry *)tun_dst);
- kfree_skb(skb);
+ kfree_skb_reason(skb, reason);
return 0;
}
EXPORT_SYMBOL_GPL(ip_tunnel_rcv);
@@ -575,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;
@@ -588,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);
@@ -608,8 +622,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_TNL_ENCAP;
goto tx_error;
+ }
use_cache = ip_tunnel_dst_cache_usable(skb, tun_info);
if (use_cache)
@@ -618,6 +634,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)
@@ -627,6 +644,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;
}
@@ -635,6 +653,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;
}
@@ -652,6 +671,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;
}
@@ -666,13 +686,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;
@@ -700,9 +721,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 &&
@@ -723,8 +750,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);
@@ -741,8 +770,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
@@ -769,8 +800,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_TNL_ENCAP;
goto tx_error;
+ }
if (connected && md) {
use_cache = ip_tunnel_dst_cache_usable(skb, tun_info);
@@ -787,6 +820,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)
@@ -800,6 +834,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;
}
@@ -809,6 +844,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;
}
@@ -843,7 +879,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;
}
@@ -859,7 +895,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);
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index 8ebda0b6a78b2..5d1e55813fce2 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -454,7 +454,8 @@ static int ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
return 0;
}
-static int ip6gre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi)
+static int ip6gre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
+ enum skb_drop_reason *reason)
{
const struct ipv6hdr *ipv6h;
struct ip6_tnl *tunnel;
@@ -473,8 +474,10 @@ static int ip6gre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi)
tun_id = key32_to_tunnel_id(tpi->key);
tun_dst = ipv6_tun_rx_dst(skb, flags, tun_id, 0);
- if (!tun_dst)
+ if (!tun_dst) {
+ *reason = SKB_DROP_REASON_NOMEM;
return PACKET_REJECT;
+ }
ip6_tnl_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error);
} else {
@@ -484,12 +487,14 @@ static int ip6gre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi)
return PACKET_RCVD;
}
+ *reason = SKB_DROP_REASON_GRE_TUNNEL_NOT_FOUND;
return PACKET_REJECT;
}
static int ip6erspan_rcv(struct sk_buff *skb,
struct tnl_ptk_info *tpi,
- int gre_hdr_len)
+ int gre_hdr_len,
+ enum skb_drop_reason *reason)
{
struct erspan_base_hdr *ershdr;
const struct ipv6hdr *ipv6h;
@@ -497,8 +502,10 @@ static int ip6erspan_rcv(struct sk_buff *skb,
struct ip6_tnl *tunnel;
u8 ver;
- if (unlikely(!pskb_may_pull(skb, sizeof(*ershdr))))
+ if (unlikely(!pskb_may_pull(skb, sizeof(*ershdr)))) {
+ *reason = SKB_DROP_REASON_HDR_TRUNC;
return PACKET_REJECT;
+ }
ipv6h = ipv6_hdr(skb);
ershdr = (struct erspan_base_hdr *)skb->data;
@@ -510,13 +517,17 @@ static int ip6erspan_rcv(struct sk_buff *skb,
if (tunnel) {
int len = erspan_hdr_len(ver);
- if (unlikely(!pskb_may_pull(skb, len)))
+ if (unlikely(!pskb_may_pull(skb, len))) {
+ *reason = SKB_DROP_REASON_HDR_TRUNC;
return PACKET_REJECT;
+ }
if (__iptunnel_pull_header(skb, len,
htons(ETH_P_TEB),
- false, false) < 0)
+ false, false) < 0) {
+ *reason = SKB_DROP_REASON_NOMEM;
return PACKET_REJECT;
+ }
if (tunnel->parms.collect_md) {
struct erspan_metadata *pkt_md, *md;
@@ -532,8 +543,10 @@ static int ip6erspan_rcv(struct sk_buff *skb,
tun_dst = ipv6_tun_rx_dst(skb, flags, tun_id,
sizeof(*md));
- if (!tun_dst)
+ if (!tun_dst) {
+ *reason = SKB_DROP_REASON_NOMEM;
return PACKET_REJECT;
+ }
/* MUST set options_len before referencing options */
info = &tun_dst->u.tun_info;
@@ -564,37 +577,40 @@ static int ip6erspan_rcv(struct sk_buff *skb,
return PACKET_RCVD;
}
+ *reason = SKB_DROP_REASON_GRE_TUNNEL_NOT_FOUND;
return PACKET_REJECT;
}
static int gre_rcv(struct sk_buff *skb)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct tnl_ptk_info tpi;
- bool csum_err = false;
int hdr_len;
- hdr_len = gre_parse_header(skb, &tpi, &csum_err, htons(ETH_P_IPV6), 0);
+ hdr_len = gre_parse_header(skb, &tpi, &reason, htons(ETH_P_IPV6), 0);
if (hdr_len < 0)
goto drop;
- if (iptunnel_pull_header(skb, hdr_len, tpi.proto, false))
+ if (iptunnel_pull_header(skb, hdr_len, tpi.proto, false)) {
+ reason = SKB_DROP_REASON_NOMEM;
goto drop;
+ }
if (unlikely(tpi.proto == htons(ETH_P_ERSPAN) ||
tpi.proto == htons(ETH_P_ERSPAN2))) {
- if (ip6erspan_rcv(skb, &tpi, hdr_len) == PACKET_RCVD)
+ if (ip6erspan_rcv(skb, &tpi, hdr_len, &reason) == PACKET_RCVD)
return 0;
goto out;
}
- if (ip6gre_rcv(skb, &tpi) == PACKET_RCVD)
+ if (ip6gre_rcv(skb, &tpi, &reason) == PACKET_RCVD)
return 0;
out:
icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
drop:
dev_core_stats_rx_dropped_inc(skb->dev);
- kfree_skb(skb);
+ kfree_skb_reason(skb, reason);
return 0;
}
@@ -677,10 +693,10 @@ static int prepare_ip6gre_xmit_ipv6(struct sk_buff *skb,
return 0;
}
-static int prepare_ip6gre_xmit_other(struct sk_buff *skb,
- struct net_device *dev,
- struct flowi6 *fl6, __u8 *dsfield,
- int *encap_limit)
+static void prepare_ip6gre_xmit_other(struct sk_buff *skb,
+ struct net_device *dev,
+ struct flowi6 *fl6, __u8 *dsfield,
+ int *encap_limit)
{
struct ip6_tnl *t = netdev_priv(dev);
@@ -700,8 +716,6 @@ static int prepare_ip6gre_xmit_other(struct sk_buff *skb,
fl6->flowi6_mark = t->parms.fwmark;
fl6->flowi6_uid = sock_net_uid(dev_net(dev), NULL);
-
- return 0;
}
static struct ip_tunnel_info *skb_tunnel_info_txcheck(struct sk_buff *skb)
@@ -718,7 +732,8 @@ static struct ip_tunnel_info *skb_tunnel_info_txcheck(struct sk_buff *skb)
static netdev_tx_t __gre6_xmit(struct sk_buff *skb,
struct net_device *dev, __u8 dsfield,
struct flowi6 *fl6, int encap_limit,
- __u32 *pmtu, __be16 proto)
+ __u32 *pmtu, __be16 proto,
+ enum skb_drop_reason *reason)
{
struct ip6_tnl *tunnel = netdev_priv(dev);
IP_TUNNEL_DECLARE_FLAGS(flags);
@@ -742,8 +757,10 @@ static netdev_tx_t __gre6_xmit(struct sk_buff *skb,
tun_info = skb_tunnel_info_txcheck(skb);
if (IS_ERR(tun_info) ||
- unlikely(ip_tunnel_info_af(tun_info) != AF_INET6))
+ unlikely(ip_tunnel_info_af(tun_info) != AF_INET6)) {
+ *reason = SKB_DROP_REASON_TUNNEL_TXINFO;
return -EINVAL;
+ }
key = &tun_info->key;
memset(fl6, 0, sizeof(*fl6));
@@ -761,8 +778,11 @@ static netdev_tx_t __gre6_xmit(struct sk_buff *skb,
ip_tunnel_flags_and(flags, flags, key->tun_flags);
tun_hlen = gre_calc_hlen(flags);
- if (skb_cow_head(skb, dev->needed_headroom ?: tun_hlen + tunnel->encap_hlen))
+ if (skb_cow_head(skb, dev->needed_headroom ?:
+ tun_hlen + tunnel->encap_hlen)) {
+ *reason = SKB_DROP_REASON_NOMEM;
return -ENOMEM;
+ }
gre_build_header(skb, tun_hlen,
flags, protocol,
@@ -772,8 +792,10 @@ static netdev_tx_t __gre6_xmit(struct sk_buff *skb,
0);
} else {
- if (skb_cow_head(skb, dev->needed_headroom ?: tunnel->hlen))
+ if (skb_cow_head(skb, dev->needed_headroom ?: tunnel->hlen)) {
+ *reason = SKB_DROP_REASON_NOMEM;
return -ENOMEM;
+ }
ip_tunnel_flags_copy(flags, tunnel->parms.o_flags);
@@ -785,10 +807,11 @@ static netdev_tx_t __gre6_xmit(struct sk_buff *skb,
}
return ip6_tnl_xmit(skb, dev, dsfield, fl6, encap_limit, pmtu,
- NEXTHDR_GRE);
+ NEXTHDR_GRE, reason);
}
-static inline int ip6gre_xmit_ipv4(struct sk_buff *skb, struct net_device *dev)
+static inline int ip6gre_xmit_ipv4(struct sk_buff *skb, struct net_device *dev,
+ enum skb_drop_reason *reason)
{
struct ip6_tnl *t = netdev_priv(dev);
int encap_limit = -1;
@@ -805,11 +828,13 @@ static inline int ip6gre_xmit_ipv4(struct sk_buff *skb, struct net_device *dev)
err = gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT,
t->parms.o_flags));
- if (err)
+ if (err) {
+ *reason = SKB_DROP_REASON_NOMEM;
return -1;
+ }
err = __gre6_xmit(skb, dev, dsfield, &fl6, encap_limit, &mtu,
- skb->protocol);
+ skb->protocol, reason);
if (err != 0) {
/* XXX: send ICMP error even if DF is not set. */
if (err == -EMSGSIZE)
@@ -821,7 +846,8 @@ static inline int ip6gre_xmit_ipv4(struct sk_buff *skb, struct net_device *dev)
return 0;
}
-static inline int ip6gre_xmit_ipv6(struct sk_buff *skb, struct net_device *dev)
+static inline int ip6gre_xmit_ipv6(struct sk_buff *skb, struct net_device *dev,
+ enum skb_drop_reason *reason)
{
struct ip6_tnl *t = netdev_priv(dev);
struct ipv6hdr *ipv6h = ipv6_hdr(skb);
@@ -831,19 +857,25 @@ static inline int ip6gre_xmit_ipv6(struct sk_buff *skb, struct net_device *dev)
__u32 mtu;
int err;
- if (ipv6_addr_equal(&t->parms.raddr, &ipv6h->saddr))
+ if (ipv6_addr_equal(&t->parms.raddr, &ipv6h->saddr)) {
+ *reason = SKB_DROP_REASON_RECURSION_LIMIT;
return -1;
+ }
if (!t->parms.collect_md &&
- prepare_ip6gre_xmit_ipv6(skb, dev, &fl6, &dsfield, &encap_limit))
+ prepare_ip6gre_xmit_ipv6(skb, dev, &fl6, &dsfield, &encap_limit)) {
+ *reason = SKB_DROP_REASON_IPV6_BAD_EXTHDR;
return -1;
+ }
if (gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT,
- t->parms.o_flags)))
+ t->parms.o_flags))) {
+ *reason = SKB_DROP_REASON_NOMEM;
return -1;
+ }
err = __gre6_xmit(skb, dev, dsfield, &fl6, encap_limit,
- &mtu, skb->protocol);
+ &mtu, skb->protocol, reason);
if (err != 0) {
if (err == -EMSGSIZE)
icmpv6_ndo_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
@@ -853,7 +885,8 @@ static inline int ip6gre_xmit_ipv6(struct sk_buff *skb, struct net_device *dev)
return 0;
}
-static int ip6gre_xmit_other(struct sk_buff *skb, struct net_device *dev)
+static int ip6gre_xmit_other(struct sk_buff *skb, struct net_device *dev,
+ enum skb_drop_reason *reason)
{
struct ip6_tnl *t = netdev_priv(dev);
int encap_limit = -1;
@@ -862,15 +895,18 @@ static int ip6gre_xmit_other(struct sk_buff *skb, struct net_device *dev)
__u32 mtu;
int err;
- if (!t->parms.collect_md &&
- prepare_ip6gre_xmit_other(skb, dev, &fl6, &dsfield, &encap_limit))
- return -1;
+ if (!t->parms.collect_md)
+ prepare_ip6gre_xmit_other(skb, dev, &fl6,
+ &dsfield, &encap_limit);
err = gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT,
t->parms.o_flags));
- if (err)
+ if (err) {
+ *reason = SKB_DROP_REASON_NOMEM;
return err;
- err = __gre6_xmit(skb, dev, dsfield, &fl6, encap_limit, &mtu, skb->protocol);
+ }
+ err = __gre6_xmit(skb, dev, dsfield, &fl6, encap_limit, &mtu,
+ skb->protocol, reason);
return err;
}
@@ -878,16 +914,20 @@ static int ip6gre_xmit_other(struct sk_buff *skb, struct net_device *dev)
static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,
struct net_device *dev)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct ip_tunnel_info *tun_info = NULL;
struct ip6_tnl *t = netdev_priv(dev);
__be16 payload_protocol;
int ret;
- if (!pskb_inet_may_pull(skb))
+ reason = pskb_inet_may_pull_reason(skb);
+ if (reason)
goto tx_err;
- if (!ip6_tnl_xmit_ctl(t, &t->parms.laddr, &t->parms.raddr))
+ if (!ip6_tnl_xmit_ctl(t, &t->parms.laddr, &t->parms.raddr)) {
+ reason = SKB_DROP_REASON_DEV_READY;
goto tx_err;
+ }
if (t->parms.collect_md)
tun_info = skb_tunnel_info_txcheck(skb);
@@ -895,13 +935,13 @@ static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,
payload_protocol = skb_protocol(skb, true);
switch (payload_protocol) {
case htons(ETH_P_IP):
- ret = ip6gre_xmit_ipv4(skb, dev);
+ ret = ip6gre_xmit_ipv4(skb, dev, &reason);
break;
case htons(ETH_P_IPV6):
- ret = ip6gre_xmit_ipv6(skb, dev);
+ ret = ip6gre_xmit_ipv6(skb, dev, &reason);
break;
default:
- ret = ip6gre_xmit_other(skb, dev);
+ ret = ip6gre_xmit_other(skb, dev, &reason);
break;
}
@@ -914,13 +954,14 @@ static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,
if (!IS_ERR(tun_info))
DEV_STATS_INC(dev, tx_errors);
DEV_STATS_INC(dev, tx_dropped);
- kfree_skb(skb);
+ kfree_skb_reason(skb, reason);
return NETDEV_TX_OK;
}
static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
struct net_device *dev)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct ip_tunnel_info *tun_info = NULL;
struct ip6_tnl *t = netdev_priv(dev);
struct dst_entry *dst = skb_dst(skb);
@@ -934,18 +975,25 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
__u32 mtu;
int nhoff;
- if (!pskb_inet_may_pull(skb))
+ reason = pskb_inet_may_pull_reason(skb);
+ if (reason)
goto tx_err;
- if (!ip6_tnl_xmit_ctl(t, &t->parms.laddr, &t->parms.raddr))
+ if (!ip6_tnl_xmit_ctl(t, &t->parms.laddr, &t->parms.raddr)) {
+ reason = SKB_DROP_REASON_DEV_READY;
goto tx_err;
+ }
- if (gre_handle_offloads(skb, false))
+ if (gre_handle_offloads(skb, false)) {
+ reason = SKB_DROP_REASON_NOMEM;
goto tx_err;
+ }
if (skb->len > dev->mtu + dev->hard_header_len) {
- if (pskb_trim(skb, dev->mtu + dev->hard_header_len))
+ if (pskb_trim(skb, dev->mtu + dev->hard_header_len)) {
+ reason = SKB_DROP_REASON_NOMEM;
goto tx_err;
+ }
truncate = true;
}
@@ -965,8 +1013,10 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
truncate = true;
}
- if (skb_cow_head(skb, dev->needed_headroom ?: t->hlen))
+ if (skb_cow_head(skb, dev->needed_headroom ?: t->hlen)) {
+ reason = SKB_DROP_REASON_NOMEM;
goto tx_err;
+ }
IPCB(skb)->flags = 0;
@@ -980,8 +1030,10 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
tun_info = skb_tunnel_info_txcheck(skb);
if (IS_ERR(tun_info) ||
- unlikely(ip_tunnel_info_af(tun_info) != AF_INET6))
+ unlikely(ip_tunnel_info_af(tun_info) != AF_INET6)) {
+ reason = SKB_DROP_REASON_TUNNEL_TXINFO;
goto tx_err;
+ }
key = &tun_info->key;
memset(&fl6, 0, sizeof(fl6));
@@ -993,10 +1045,14 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
dsfield = key->tos;
if (!test_bit(IP_TUNNEL_ERSPAN_OPT_BIT,
- tun_info->key.tun_flags))
+ tun_info->key.tun_flags)) {
+ reason = SKB_DROP_REASON_TUNNEL_TXINFO;
goto tx_err;
- if (tun_info->options_len < sizeof(*md))
+ }
+ if (tun_info->options_len < sizeof(*md)) {
+ reason = SKB_DROP_REASON_TUNNEL_TXINFO;
goto tx_err;
+ }
md = ip_tunnel_info_opts(tun_info);
tun_id = tunnel_id_to_key32(key->tun_id);
@@ -1014,6 +1070,7 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
truncate, false);
proto = htons(ETH_P_ERSPAN2);
} else {
+ reason = SKB_DROP_REASON_UNHANDLED_PROTO;
goto tx_err;
}
} else {
@@ -1024,11 +1081,16 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
&dsfield, &encap_limit);
break;
case htons(ETH_P_IPV6):
- if (ipv6_addr_equal(&t->parms.raddr, &ipv6_hdr(skb)->saddr))
+ if (ipv6_addr_equal(&t->parms.raddr,
+ &ipv6_hdr(skb)->saddr)) {
+ reason = SKB_DROP_REASON_RECURSION_LIMIT;
goto tx_err;
+ }
if (prepare_ip6gre_xmit_ipv6(skb, dev, &fl6,
- &dsfield, &encap_limit))
+ &dsfield, &encap_limit)) {
+ reason = SKB_DROP_REASON_IPV6_BAD_EXTHDR;
goto tx_err;
+ }
break;
default:
memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6));
@@ -1047,6 +1109,7 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
truncate, false);
proto = htons(ETH_P_ERSPAN2);
} else {
+ reason = SKB_DROP_REASON_UNHANDLED_PROTO;
goto tx_err;
}
@@ -1065,7 +1128,7 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
dst->ops->update_pmtu(dst, NULL, skb, mtu, false);
}
err = ip6_tnl_xmit(skb, dev, dsfield, &fl6, encap_limit, &mtu,
- NEXTHDR_GRE);
+ NEXTHDR_GRE, &reason);
if (err != 0) {
/* XXX: send ICMP error even if DF is not set. */
if (err == -EMSGSIZE) {
@@ -1084,7 +1147,7 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
if (!IS_ERR(tun_info))
DEV_STATS_INC(dev, tx_errors);
DEV_STATS_INC(dev, tx_dropped);
- kfree_skb(skb);
+ kfree_skb_reason(skb, reason);
return NETDEV_TX_OK;
}
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index d5ff50a2ac017..138151ed5797e 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -813,6 +813,7 @@ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb,
struct sk_buff *skb),
bool log_ecn_err)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
const struct ipv6hdr *ipv6h;
int nh, err;
@@ -820,15 +821,22 @@ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb,
test_bit(IP_TUNNEL_CSUM_BIT, tpi->flags)) {
DEV_STATS_INC(tunnel->dev, rx_crc_errors);
DEV_STATS_INC(tunnel->dev, rx_errors);
+ reason = SKB_DROP_REASON_TNL_OPT_MISMATCH;
goto drop;
}
if (test_bit(IP_TUNNEL_SEQ_BIT, tunnel->parms.i_flags)) {
- if (!test_bit(IP_TUNNEL_SEQ_BIT, tpi->flags) ||
- (tunnel->i_seqno &&
- (s32)(ntohl(tpi->seq) - tunnel->i_seqno) < 0)) {
+ if (!test_bit(IP_TUNNEL_SEQ_BIT, tpi->flags)) {
DEV_STATS_INC(tunnel->dev, rx_fifo_errors);
DEV_STATS_INC(tunnel->dev, rx_errors);
+ reason = SKB_DROP_REASON_TNL_OPT_MISMATCH;
+ goto drop;
+ }
+ if (tunnel->i_seqno &&
+ (s32)(ntohl(tpi->seq) - tunnel->i_seqno) < 0) {
+ DEV_STATS_INC(tunnel->dev, rx_fifo_errors);
+ DEV_STATS_INC(tunnel->dev, rx_errors);
+ reason = SKB_DROP_REASON_TNL_OLD_SEQ;
goto drop;
}
tunnel->i_seqno = ntohl(tpi->seq) + 1;
@@ -838,7 +846,8 @@ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb,
/* Warning: All skb pointers will be invalidated! */
if (tunnel->dev->type == ARPHRD_ETHER) {
- if (!pskb_may_pull(skb, ETH_HLEN)) {
+ reason = pskb_may_pull_reason(skb, ETH_HLEN);
+ if (reason) {
DEV_STATS_INC(tunnel->dev, rx_length_errors);
DEV_STATS_INC(tunnel->dev, rx_errors);
goto drop;
@@ -859,7 +868,8 @@ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb,
skb_reset_network_header(skb);
- if (skb_vlan_inet_prepare(skb, true)) {
+ reason = skb_vlan_inet_prepare(skb, true);
+ if (reason) {
DEV_STATS_INC(tunnel->dev, rx_length_errors);
DEV_STATS_INC(tunnel->dev, rx_errors);
goto drop;
@@ -881,6 +891,7 @@ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb,
if (err > 1) {
DEV_STATS_INC(tunnel->dev, rx_frame_errors);
DEV_STATS_INC(tunnel->dev, rx_errors);
+ reason = SKB_DROP_REASON_IP_TUNNEL_ECN;
goto drop;
}
}
@@ -898,7 +909,7 @@ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb,
drop:
if (tun_dst)
dst_release((struct dst_entry *)tun_dst);
- kfree_skb(skb);
+ kfree_skb_reason(skb, reason);
return 0;
}
@@ -1086,6 +1097,7 @@ EXPORT_SYMBOL_GPL(ip6_tnl_xmit_ctl);
* @encap_limit: encapsulation limit
* @pmtu: Path MTU is stored if packet is too big
* @proto: next header value
+ * @reason: drop reason, only written when the packet is dropped
*
* Description:
* Build new header and do some sanity checks on the packet before sending
@@ -1099,7 +1111,7 @@ EXPORT_SYMBOL_GPL(ip6_tnl_xmit_ctl);
int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,
struct flowi6 *fl6, int encap_limit, __u32 *pmtu,
- __u8 proto)
+ __u8 proto, enum skb_drop_reason *reason)
{
struct ip6_tnl *t = netdev_priv(dev);
struct net *net = t->net;
@@ -1132,13 +1144,17 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,
struct neighbour *neigh;
int addr_type;
- if (!skb_dst(skb))
+ if (!skb_dst(skb)) {
+ *reason = SKB_DROP_REASON_NO_TX_TARGET;
goto tx_err_link_failure;
+ }
neigh = dst_neigh_lookup(skb_dst(skb),
&ipv6_hdr(skb)->daddr);
- if (!neigh)
+ if (!neigh) {
+ *reason = SKB_DROP_REASON_NEIGH_CREATEFAIL;
goto tx_err_link_failure;
+ }
addr6 = (struct in6_addr *)&neigh->primary_key;
addr_type = ipv6_addr_type(addr6);
@@ -1151,8 +1167,10 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,
} else if (payload_protocol == htons(ETH_P_IP)) {
const struct rtable *rt = skb_rtable(skb);
- if (!rt)
+ if (!rt) {
+ *reason = SKB_DROP_REASON_NO_TX_TARGET;
goto tx_err_link_failure;
+ }
if (rt->rt_gw_family == AF_INET6)
memcpy(&fl6->daddr, &rt->rt_gw6, sizeof(fl6->daddr));
@@ -1169,8 +1187,10 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,
if (use_cache)
dst = dst_cache_get(&t->dst_cache);
- if (!ip6_tnl_xmit_ctl(t, &fl6->saddr, &fl6->daddr))
+ if (!ip6_tnl_xmit_ctl(t, &fl6->saddr, &fl6->daddr)) {
+ *reason = SKB_DROP_REASON_DEV_READY;
goto tx_err_link_failure;
+ }
if (!dst) {
route_lookup:
@@ -1179,18 +1199,23 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,
dst = ip6_route_output(net, NULL, fl6);
- if (dst->error)
+ if (dst->error) {
+ *reason = SKB_DROP_REASON_IP_OUTNOROUTES;
goto tx_err_link_failure;
+ }
dst = xfrm_lookup(net, dst, flowi6_to_flowi(fl6), NULL, 0);
if (IS_ERR(dst)) {
err = PTR_ERR(dst);
dst = NULL;
+ *reason = SKB_DROP_REASON_IP_OUTNOROUTES;
goto tx_err_link_failure;
}
if (t->parms.collect_md && ipv6_addr_any(&fl6->saddr) &&
ipv6_dev_get_saddr(net, ip6_dst_idev(dst)->dev,
- &fl6->daddr, 0, &fl6->saddr))
+ &fl6->daddr, 0, &fl6->saddr)) {
+ *reason = SKB_DROP_REASON_IP_OUTNOROUTES;
goto tx_err_link_failure;
+ }
ndst = dst;
}
@@ -1200,6 +1225,7 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,
DEV_STATS_INC(dev, collisions);
net_warn_ratelimited("%s: Local routing loop detected!\n",
t->parms.name);
+ *reason = SKB_DROP_REASON_RECURSION_LIMIT;
goto tx_err_dst_release;
}
mtu = dst6_mtu(dst) - eth_hlen - psh_hlen - t->tun_hlen;
@@ -1214,6 +1240,7 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,
if (skb->len - t->tun_hlen - eth_hlen > mtu && !skb_is_gso(skb)) {
*pmtu = mtu;
err = -EMSGSIZE;
+ *reason = SKB_DROP_REASON_PKT_TOO_BIG;
goto tx_err_dst_release;
}
@@ -1236,12 +1263,16 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,
*/
max_headroom += LL_RESERVED_SPACE(tdev);
- if (skb_cow_head(skb, max_headroom))
+ if (skb_cow_head(skb, max_headroom)) {
+ *reason = SKB_DROP_REASON_NOMEM;
goto tx_err_dst_release;
+ }
if (t->parms.collect_md) {
- if (t->encap.type != TUNNEL_ENCAP_NONE)
+ if (t->encap.type != TUNNEL_ENCAP_NONE) {
+ *reason = SKB_DROP_REASON_TNL_ENCAP;
goto tx_err_dst_release;
+ }
} else {
if (use_cache && ndst)
dst_cache_set_ip6(&t->dst_cache, ndst, &fl6->saddr);
@@ -1265,8 +1296,10 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,
ip_tunnel_adj_headroom(dev, max_headroom);
err = ip6_tnl_encap(skb, t, &proto, fl6);
- if (err)
+ if (err) {
+ *reason = SKB_DROP_REASON_TNL_ENCAP;
return err;
+ }
if (encap_limit >= 0) {
init_tel_txopt(&opt, encap_limit);
@@ -1295,7 +1328,7 @@ EXPORT_SYMBOL(ip6_tnl_xmit);
static inline int
ipxip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev,
- u8 protocol)
+ u8 protocol, enum skb_drop_reason *reason)
{
struct ip6_tnl *t = netdev_priv(dev);
struct ipv6hdr *ipv6h;
@@ -1309,8 +1342,10 @@ ipxip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev,
int err;
tproto = READ_ONCE(t->parms.proto);
- if (tproto != protocol && tproto != 0)
+ if (tproto != protocol && tproto != 0) {
+ *reason = SKB_DROP_REASON_UNHANDLED_PROTO;
return -1;
+ }
if (t->parms.collect_md) {
struct ip_tunnel_info *tun_info;
@@ -1318,8 +1353,10 @@ ipxip6_tnl_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_INET6))
+ ip_tunnel_info_af(tun_info) != AF_INET6)) {
+ *reason = SKB_DROP_REASON_TUNNEL_TXINFO;
return -1;
+ }
key = &tun_info->key;
memset(&fl6, 0, sizeof(fl6));
fl6.flowi6_proto = protocol;
@@ -1356,6 +1393,7 @@ ipxip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev,
if (tel->encap_limit == 0) {
icmpv6_ndo_send(skb, ICMPV6_PARAMPROB,
ICMPV6_HDR_FIELD, offset + 2);
+ *reason = SKB_DROP_REASON_IPV6_BAD_EXTHDR;
return -1;
}
encap_limit = tel->encap_limit - 1;
@@ -1397,13 +1435,15 @@ ipxip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev,
fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL);
dsfield = INET_ECN_encapsulate(dsfield, orig_dsfield);
- if (iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6))
+ if (iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6)) {
+ *reason = SKB_DROP_REASON_NOMEM;
return -1;
+ }
skb_set_inner_ipproto(skb, protocol);
err = ip6_tnl_xmit(skb, dev, dsfield, &fl6, encap_limit, &mtu,
- protocol);
+ protocol, reason);
if (err != 0) {
/* XXX: send ICMP error even if DF is not set. */
if (err == -EMSGSIZE)
@@ -1427,11 +1467,13 @@ ipxip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev,
static netdev_tx_t
ip6_tnl_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct ip6_tnl *t = netdev_priv(dev);
u8 ipproto;
int ret;
- if (!pskb_inet_may_pull(skb))
+ reason = pskb_inet_may_pull_reason(skb);
+ if (reason)
goto tx_err;
switch (skb->protocol) {
@@ -1439,18 +1481,21 @@ ip6_tnl_start_xmit(struct sk_buff *skb, struct net_device *dev)
ipproto = IPPROTO_IPIP;
break;
case htons(ETH_P_IPV6):
- if (ip6_tnl_addr_conflict(t, ipv6_hdr(skb)))
+ if (ip6_tnl_addr_conflict(t, ipv6_hdr(skb))) {
+ reason = SKB_DROP_REASON_RECURSION_LIMIT;
goto tx_err;
+ }
ipproto = IPPROTO_IPV6;
break;
case htons(ETH_P_MPLS_UC):
ipproto = IPPROTO_MPLS;
break;
default:
+ reason = SKB_DROP_REASON_UNHANDLED_PROTO;
goto tx_err;
}
- ret = ipxip6_tnl_xmit(skb, dev, ipproto);
+ ret = ipxip6_tnl_xmit(skb, dev, ipproto, &reason);
if (ret < 0)
goto tx_err;
@@ -1459,7 +1504,7 @@ ip6_tnl_start_xmit(struct sk_buff *skb, struct net_device *dev)
tx_err:
DEV_STATS_INC(dev, tx_errors);
DEV_STATS_INC(dev, tx_dropped);
- kfree_skb(skb);
+ kfree_skb_reason(skb, reason);
return NETDEV_TX_OK;
}
|
| 3/2 | 2026/09/18 04:45 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 27521 output: 19 thoughts: 721 Thoughts: **Analyzing the GRE/IP Tunnel Drop Reason Patch** Okay, so this patch is touching a fair number of files related to GRE and IP tunnels. Specifically, I see it's modifying several header files and core functions: `include/net/dropreason-core.h`, `include/net/gre.h`, `include/net/ip6_tunnel.h`, and a handful of `.c` files within `net/ipv4` and `net/ipv6`. Right off the bat, I can tell this is about refining error handling within these tunnel protocols. It looks like the primary goal is to introduce *detailed* drop reasons. I'm seeing additions to `include/net/dropreason-core.h` with new `SKB_DROP_REASON_*` defines, targeting cases like `TNL_OPT_MISMATCH`, `TNL_OLD_SEQ`, `GRE_INVALID_HDR`, and other potential issues during encapsulation/decapsulation. Very sensible. These additions will be invaluable for debugging and performance analysis. The changes to function signatures in `gre_parse_header` and `ip6_tnl_xmit` are immediately noticeable. They're adding a `enum skb_drop_reason *reason` argument. This is, of course, how the drop reasons will be communicated and propagated. It also means I need to double-check every call site of these functions to ensure the calling context is correctly updated. I'm seeing that the patch meticulously updates many receive (`rcv`) and transmit (`xmit`) functions like `gre_rcv`, `ip_tunnel_rcv`, and so on, to properly track and pass these drop reasons to `kfree_skb_reason`. This is key; it ensures that the kernel is aware of *why* packets are being dropped, which is the whole point of this exercise. Now, looking at the code, I see some logic changes in `gre_parse_header`. The snippet caught my eye: it's checking `pskb_may_pull` and, if it fails, setting `*reason = SKB_DROP_REASON_HDR_TRUNC` before returning. And then a checksum error is handled. Very good. Wait, what was that comment about a `NULL @reason`? It says that a null `reason` means the caller *doesn't* want the drop reason, and crucially, that checksum failures should be masked. This is likely for ICMP error handling – makes sense. The ICMP path likely only gets a truncated packet, so the drop reason wouldn't be accurate, and it's best not to report checksum errors in that context. This is something to be aware of; callers may behave differently. Alright, I need to understand *which* callers pass this `reason` argument and which don't. Time to use `grepper` and see all the call sites of `gre_parse_header` in the source tree. Knowing the context will really help me understand if this is safe. After that, I will see if any of the callers need adjustments. |
| 4/2 | 2026/09/18 04:45 | tool | grepper |
0mArgs:{
"Expression": "gre_parse_header"
}Results: {
"Output": "include/net/gre.h=33=int gre_del_protocol(const struct gre_protocol *proto, u8 version);\ninclude/net/gre.h-34-\ninclude/net/gre.h:35:int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,\ninclude/net/gre.h-36-\t\t enum skb_drop_reason *reason, __be16 proto, int nhs);\n--\nnet/ipv4/gre_demux.c=57=EXPORT_SYMBOL_GPL(gre_del_protocol);\n--\nnet/ipv4/gre_demux.c-70- */\nnet/ipv4/gre_demux.c:71:int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,\nnet/ipv4/gre_demux.c-72-\t\t enum skb_drop_reason *reason, __be16 proto, int nhs)\n--\nnet/ipv4/gre_demux.c-167-}\nnet/ipv4/gre_demux.c:168:EXPORT_SYMBOL(gre_parse_header);\nnet/ipv4/gre_demux.c-169-\n--\nnet/ipv4/ip_gre.c=219=static void gre_err(struct sk_buff *skb, u32 info)\n--\nnet/ipv4/ip_gre.c-239-\nnet/ipv4/ip_gre.c:240:\tif (gre_parse_header(skb, \u0026tpi, NULL, htons(ETH_P_IP),\nnet/ipv4/ip_gre.c-241-\t\t\t iph-\u003eihl * 4) \u003c 0)\n--\nnet/ipv4/ip_gre.c=451=static int gre_rcv(struct sk_buff *skb)\n--\nnet/ipv4/ip_gre.c-464-\nnet/ipv4/ip_gre.c:465:\thdr_len = gre_parse_header(skb, \u0026tpi, \u0026reason, htons(ETH_P_IP), 0);\nnet/ipv4/ip_gre.c-466-\tif (hdr_len \u003c 0)\n--\nnet/ipv6/ip6_gre.c=387=static int ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt,\n--\nnet/ipv6/ip6_gre.c-394-\nnet/ipv6/ip6_gre.c:395:\tif (gre_parse_header(skb, \u0026tpi, NULL, htons(ETH_P_IPV6),\nnet/ipv6/ip6_gre.c-396-\t\t\t offset) \u003c 0)\n--\nnet/ipv6/ip6_gre.c=584=static int gre_rcv(struct sk_buff *skb)\n--\nnet/ipv6/ip6_gre.c-589-\nnet/ipv6/ip6_gre.c:590:\thdr_len = gre_parse_header(skb, \u0026tpi, \u0026reason, htons(ETH_P_IPV6), 0);\nnet/ipv6/ip6_gre.c-591-\tif (hdr_len \u003c 0)\n"
} |
| 5/2 | 2026/09/18 04:45 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 29755 output: 23 thoughts: 17 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Investigating the `ip6_tnl_xmit` Callers** Okay, so we're looking at `ip6_tnl_xmit`. My initial thought is, this function is critical for IPv6 tunneling. Therefore, understanding its usage is going to be key to whatever we're debugging or optimizing. Before diving into the nitty-gritty of the code itself, I want to quickly get a high-level picture of *who* is calling this. What are the various contexts in which packets are being injected into a tunnel? Are we seeing a performance bottleneck, or some weird behavior? Finding the callers will provide valuable insight to start with. |
| 6/2 | 2026/09/18 04:45 | tool | grepper |
0mArgs:{
"Expression": "ip6_tnl_xmit\\("
}Results: {
"Output": "include/net/ip6_tunnel.h=143=int ip6_tnl_xmit_ctl(struct ip6_tnl *t, const struct in6_addr *laddr,\ninclude/net/ip6_tunnel.h-144-\t\t const struct in6_addr *raddr);\ninclude/net/ip6_tunnel.h:145:int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,\ninclude/net/ip6_tunnel.h-146-\t\t struct flowi6 *fl6, int encap_limit, __u32 *pmtu, __u8 proto,\n--\nnet/ipv6/ip6_gre.c=732=static netdev_tx_t __gre6_xmit(struct sk_buff *skb,\n--\nnet/ipv6/ip6_gre.c-808-\nnet/ipv6/ip6_gre.c:809:\treturn ip6_tnl_xmit(skb, dev, dsfield, fl6, encap_limit, pmtu,\nnet/ipv6/ip6_gre.c-810-\t\t\t NEXTHDR_GRE, reason);\n--\nnet/ipv6/ip6_gre.c=961=static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,\n--\nnet/ipv6/ip6_gre.c-1129-\t}\nnet/ipv6/ip6_gre.c:1130:\terr = ip6_tnl_xmit(skb, dev, dsfield, \u0026fl6, encap_limit, \u0026mtu,\nnet/ipv6/ip6_gre.c-1131-\t\t\t NEXTHDR_GRE, \u0026reason);\n--\nnet/ipv6/ip6_tunnel.c=1089=EXPORT_SYMBOL_GPL(ip6_tnl_xmit_ctl);\n--\nnet/ipv6/ip6_tunnel.c-1111-\nnet/ipv6/ip6_tunnel.c:1112:int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,\nnet/ipv6/ip6_tunnel.c-1113-\t\t struct flowi6 *fl6, int encap_limit, __u32 *pmtu,\n--\nnet/ipv6/ip6_tunnel.c=1329=static inline int\nnet/ipv6/ip6_tunnel.c:1330:ipxip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev,\nnet/ipv6/ip6_tunnel.c-1331-\t\tu8 protocol, enum skb_drop_reason *reason)\n--\nnet/ipv6/ip6_tunnel.c-1444-\nnet/ipv6/ip6_tunnel.c:1445:\terr = ip6_tnl_xmit(skb, dev, dsfield, \u0026fl6, encap_limit, \u0026mtu,\nnet/ipv6/ip6_tunnel.c-1446-\t\t\t protocol, reason);\n--\nnet/ipv6/ip6_tunnel.c=1468=ip6_tnl_start_xmit(struct sk_buff *skb, struct net_device *dev)\n--\nnet/ipv6/ip6_tunnel.c-1497-\nnet/ipv6/ip6_tunnel.c:1498:\tret = ipxip6_tnl_xmit(skb, dev, ipproto, \u0026reason);\nnet/ipv6/ip6_tunnel.c-1499-\tif (ret \u003c 0)\n"
} |
| 7/2 | 2026/09/18 04:45 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 30889 output: 24 thoughts: 24 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Investigating Callers to `ip6_tnl_xmit`**
Okay, so I'm thinking about `ip6_tnl_xmit`. My initial concern is, "What else is using this function?". We're dealing with network code here, so understanding its dependencies is absolutely crucial for any modifications, debugging, or optimization efforts. It's a key function for IPv6 tunnel transmission, so I need to know the potential impact of changes or interactions. The next logical step is to quickly determine all the callers. It is possible there are several variations of a similar function, so I want to know exactly what is calling the one specifically mentioned. My initial reflex is to use `grep` (or my preferred code search tool, which would depend on the codebase), but I want to keep the search as simple and targeted as possible. I would search for the exact function name, as I do not want to see any function called "ip6_tnl_xmit(". I need to eliminate the possibility that I am getting results from a slightly different, similar function name. The parens are not a part of the function name, and are more likely to generate false positive results. Let's see what that unadorned search yields first, before complicating things. I can refine it later if necessary, but starting simple is usually the best approach to find the information I need, fast.
|
| 8/2 | 2026/09/18 04:46 | tool | grepper |
0mArgs:{
"Expression": "ip6_tnl_xmit\\b"
}Results: {
"Output": "include/net/ip6_tunnel.h=143=int ip6_tnl_xmit_ctl(struct ip6_tnl *t, const struct in6_addr *laddr,\ninclude/net/ip6_tunnel.h-144-\t\t const struct in6_addr *raddr);\ninclude/net/ip6_tunnel.h:145:int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,\ninclude/net/ip6_tunnel.h-146-\t\t struct flowi6 *fl6, int encap_limit, __u32 *pmtu, __u8 proto,\n--\nnet/ipv6/ip6_gre.c=732=static netdev_tx_t __gre6_xmit(struct sk_buff *skb,\n--\nnet/ipv6/ip6_gre.c-808-\nnet/ipv6/ip6_gre.c:809:\treturn ip6_tnl_xmit(skb, dev, dsfield, fl6, encap_limit, pmtu,\nnet/ipv6/ip6_gre.c-810-\t\t\t NEXTHDR_GRE, reason);\n--\nnet/ipv6/ip6_gre.c=961=static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,\n--\nnet/ipv6/ip6_gre.c-1129-\t}\nnet/ipv6/ip6_gre.c:1130:\terr = ip6_tnl_xmit(skb, dev, dsfield, \u0026fl6, encap_limit, \u0026mtu,\nnet/ipv6/ip6_gre.c-1131-\t\t\t NEXTHDR_GRE, \u0026reason);\n--\nnet/ipv6/ip6_tunnel.c=1089=EXPORT_SYMBOL_GPL(ip6_tnl_xmit_ctl);\n--\nnet/ipv6/ip6_tunnel.c-1091-/**\nnet/ipv6/ip6_tunnel.c:1092: * ip6_tnl_xmit - encapsulate packet and send\nnet/ipv6/ip6_tunnel.c-1093- * @skb: the outgoing socket buffer\n--\nnet/ipv6/ip6_tunnel.c-1111-\nnet/ipv6/ip6_tunnel.c:1112:int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,\nnet/ipv6/ip6_tunnel.c-1113-\t\t struct flowi6 *fl6, int encap_limit, __u32 *pmtu,\n--\nnet/ipv6/ip6_tunnel.c-1326-}\nnet/ipv6/ip6_tunnel.c:1327:EXPORT_SYMBOL(ip6_tnl_xmit);\nnet/ipv6/ip6_tunnel.c-1328-\nnet/ipv6/ip6_tunnel.c=1329=static inline int\nnet/ipv6/ip6_tunnel.c:1330:ipxip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev,\nnet/ipv6/ip6_tunnel.c-1331-\t\tu8 protocol, enum skb_drop_reason *reason)\n--\nnet/ipv6/ip6_tunnel.c-1444-\nnet/ipv6/ip6_tunnel.c:1445:\terr = ip6_tnl_xmit(skb, dev, dsfield, \u0026fl6, encap_limit, \u0026mtu,\nnet/ipv6/ip6_tunnel.c-1446-\t\t\t protocol, reason);\n--\nnet/ipv6/ip6_tunnel.c=1468=ip6_tnl_start_xmit(struct sk_buff *skb, struct net_device *dev)\n--\nnet/ipv6/ip6_tunnel.c-1497-\nnet/ipv6/ip6_tunnel.c:1498:\tret = ipxip6_tnl_xmit(skb, dev, ipproto, \u0026reason);\nnet/ipv6/ip6_tunnel.c-1499-\tif (ret \u003c 0)\n"
} |
| 9/2 | 2026/09/18 04:46 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 32330 output: 38 thoughts: 103 Thoughts: **Kconfig Flag Investigation for GRE Tunneling Modules** Okay, so I need to figure out the necessary Kconfig flags to build these GRE-related modules: `net/ipv4/gre_demux.c`, `net/ipv4/ip_gre.c`, `net/ipv4/ip_tunnel.c`, `net/ipv6/ip6_gre.c`, and `net/ipv6/ip6_tunnel.c`. My usual approach is to check the relevant Makefiles. I'll need to examine the Makefiles located in `net/ipv4/` and `net/ipv6/` to see what dependencies and configuration options are being used. This should give me a pretty clear picture. |
| 10/2 | 2026/09/18 04:46 | tool | grepper |
0mArgs:{
"Expression": "ip_gre|gre_demux|ip_tunnel",
"PathPrefix": "net/ipv4/Makefile"
}Results: {
"Output": "net/ipv4/Makefile=6=obj-y := route.o inetpeer.o protocol.o \\\n--\nnet/ipv4/Makefile-15-\t fib_frontend.o fib_semantics.o fib_trie.o fib_notifier.o \\\nnet/ipv4/Makefile:16:\t inet_fragment.o ping.o ip_tunnel_core.o gre_offload.o \\\nnet/ipv4/Makefile-17-\t metrics.o netlink.o nexthop.o udp_tunnel_stub.o\nnet/ipv4/Makefile-18-\nnet/ipv4/Makefile:19:obj-$(CONFIG_NET_IP_TUNNEL) += ip_tunnel.o\nnet/ipv4/Makefile-20-obj-$(CONFIG_SYSCTL) += sysctl_net_ipv4.o\n--\nnet/ipv4/Makefile=25=obj-$(CONFIG_NET_IPIP) += ipip.o\nnet/ipv4/Makefile:26:gre-y := gre_demux.o\nnet/ipv4/Makefile-27-fou-y := fou_core.o fou_nl.o fou_bpf.o\n--\nnet/ipv4/Makefile=29=obj-$(CONFIG_NET_IPGRE_DEMUX) += gre.o\nnet/ipv4/Makefile:30:obj-$(CONFIG_NET_IPGRE) += ip_gre.o\nnet/ipv4/Makefile-31-udp_tunnel-y := udp_tunnel_core.o udp_tunnel_nic.o\n"
} |
| 11/2 | 2026/09/18 04:46 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 33006 output: 35 thoughts: 12 Thoughts: Here's my take on those thoughts, tailored for an expert audience: **Diving into the IPv6 Makefile** Okay, so I'm thinking, "Right, time to get a handle on how this IPv6 module is built." Specifically, I'm zeroing in on the `net/ipv6/Makefile`. This is where all the interesting magic happens, right? I'm anticipating that this is where I'll find the dependencies for the IPv6 protocol stack, the compilation flags and object file definitions. Knowing this, I expect to find this file is pivotal for understanding the specific nuances of the IPv6 implementation within this kernel version. I'm keen to see if they're using any particularly clever build tricks here, any custom macros or conditional compilation based on the hardware or kernel config. Time to see what's what! |
| 12/2 | 2026/09/18 04:46 | tool | grepper |
0mArgs:{
"Expression": "ip6_gre|ip6_tunnel",
"PathPrefix": "net/ipv6/Makefile"
}Results: {
"Output": "net/ipv6/Makefile=41=obj-$(CONFIG_IPV6_SIT) += sit.o\nnet/ipv6/Makefile:42:obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o\nnet/ipv6/Makefile:43:obj-$(CONFIG_IPV6_GRE) += ip6_gre.o\nnet/ipv6/Makefile-44-obj-$(CONFIG_IPV6_FOU) += fou6.o\n"
} |
| 13/2 | 2026/09/18 04:46 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 33351 output: 32 thoughts: 44 Thoughts: Okay, here's my take on those Kconfig definitions, thinking like I usually do when debugging or setting up networking features: **Digging into the GRE Tunneling Landscape** Alright, time to get my hands dirty and dive into these Kconfig definitions related to Generic Routing Encapsulation (GRE) and IPv6 tunneling. Let's see, I need to check how things are configured. First up, `NET_IPGRE`. That's the core, right? The foundation for all the GRE goodness. Then, we have `NET_IPGRE_DEMUX`. I remember this one; it's about handling multiple GRE tunnels over a single UDP port. Important for efficiency, if I recall correctly. Then, to round things out, we have the IPv6-specific options: `IPV6_GRE` – specifically the GRE tunnel feature for IPv6, and `IPV6_TUNNEL` – which must handle the general IPv6 tunnel support, probably including things like SIT and 6in4. And finally, `NET_IP_TUNNEL`. That's the generic IP tunnel support, seems to be necessary. Time to check what these depend on, how they interact, and what other configurations are affected when I enable or disable them. I'm especially interested in dependencies and defaults; that's where the real magic (or potential gotchas) usually lurks. I need to make sure I understand the complete picture, and which combinations will give me the functionality I need, and don't end up accidentally enabling something that I don't need or is a security risk. Gotta pay attention to the details here. |
| 14/2 | 2026/09/18 04:46 | tool | grepper |
0mArgs:{
"Expression": "config NET_IPGRE",
"PathPrefix": "net/ipv4/Kconfig"
}Results: {
"Output": "net/ipv4/Kconfig=162=config NET_IPIP\n--\nnet/ipv4/Kconfig-179-\nnet/ipv4/Kconfig:180:config NET_IPGRE_DEMUX\nnet/ipv4/Kconfig-181-\ttristate \"IP: GRE demultiplexer\"\n--\nnet/ipv4/Kconfig=186=config NET_IP_TUNNEL\n--\nnet/ipv4/Kconfig-191-\nnet/ipv4/Kconfig:192:config NET_IPGRE\nnet/ipv4/Kconfig-193-\ttristate \"IP: GRE tunnels over IP\"\n--\nnet/ipv4/Kconfig-206-\nnet/ipv4/Kconfig:207:config NET_IPGRE_BROADCAST\nnet/ipv4/Kconfig-208-\tbool \"IP: broadcast GRE over IP\"\n"
} |
| 15/2 | 2026/09/18 04:46 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 33998 output: 35 thoughts: 21 Thoughts: Here's a summary of my thought process, as requested: **Diving into `NET_IPGRE` in `net/ipv4/Kconfig`** Okay, so I'm focusing in on `NET_IPGRE` right now, specifically within `net/ipv4/Kconfig`. Time to get my bearings and understand this configuration option. I'm assuming this is related to GRE (Generic Routing Encapsulation) tunnels, which is a key protocol for encapsulating and routing packets. I need to figure out what functionality this option enables. Is it for GRE tunnel support within IPv4? Or is it more specific than that? Knowing the specifics will help me understand its dependencies, implications, and potentially its impact on other parts of the kernel. Let's see what the Kconfig file has to offer. |
| 16/2 | 2026/09/18 04:46 | tool | read-file |
0mArgs:{
"File": "net/ipv4/Kconfig",
"FirstLine": 175,
"LineCount": 40
}Results: {
"Contents": " 175:\t\n 176:\t\t Saying Y to this option will produce two modules ( = code which can\n 177:\t\t be inserted in and removed from the running kernel whenever you\n 178:\t\t want). Most people won't need this and can say N.\n 179:\t\n 180:\tconfig NET_IPGRE_DEMUX\n 181:\t\ttristate \"IP: GRE demultiplexer\"\n 182:\t\thelp\n 183:\t\t This is helper module to demultiplex GRE packets on GRE version field criteria.\n 184:\t\t Required by ip_gre and pptp modules.\n 185:\t\n 186:\tconfig NET_IP_TUNNEL\n 187:\t\ttristate\n 188:\t\tselect DST_CACHE\n 189:\t\tselect GRO_CELLS\n 190:\t\tdefault n\n 191:\t\n 192:\tconfig NET_IPGRE\n 193:\t\ttristate \"IP: GRE tunnels over IP\"\n 194:\t\tdepends on NET_IPGRE_DEMUX\n 195:\t\tselect NET_IP_TUNNEL\n 196:\t\thelp\n 197:\t\t Tunneling means encapsulating data of one protocol type within\n 198:\t\t another protocol and sending it over a channel that understands the\n 199:\t\t encapsulating protocol. This particular tunneling driver implements\n 200:\t\t GRE (Generic Routing Encapsulation) and at this time allows\n 201:\t\t encapsulating of IPv4 or IPv6 over existing IPv4 infrastructure.\n 202:\t\t This driver is useful if the other endpoint is a Cisco router: Cisco\n 203:\t\t likes GRE much better than the other Linux tunneling driver (\"IP\n 204:\t\t tunneling\" above). In addition, GRE allows multicast redistribution\n 205:\t\t through the tunnel.\n 206:\t\n 207:\tconfig NET_IPGRE_BROADCAST\n 208:\t\tbool \"IP: broadcast GRE over IP\"\n 209:\t\tdepends on IP_MULTICAST \u0026\u0026 NET_IPGRE\n 210:\t\thelp\n 211:\t\t One application of GRE/IP is to construct a broadcast WAN (Wide Area\n 212:\t\t Network), which looks like a normal Ethernet LAN (Local Area\n 213:\t\t Network), but can be distributed all over the Internet. If you want\n 214:\t\t to do that, say Y here and to \"IP multicast routing\" below.\n"
} |
| 17/2 | 2026/09/18 04:46 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 34869 output: 41 thoughts: 28 Thoughts: Here's my thought process, as if I were considering this myself: **Investigating IPv6 Tunneling Configuration** Okay, so I need to figure out the configuration options related to IPv6 tunnels, specifically GRE. My instinct is to head straight to the source – the kernel's Kconfig files. The logical place to start is `net/ipv6/Kconfig`. That's where I expect to find the dependencies and the overall build options for IPv6 networking features. I'm looking for two specific configuration symbols: `IPV6_TUNNEL` which should be the umbrella option for all IPv6 tunneling, and `IPV6_GRE`, which should be the specific one for GRE over IPv6. This is the starting point for understanding how these features are enabled, disabled, and configured within the build system. Let's see what the Kconfig file reveals... |
| 18/2 | 2026/09/18 04:46 | tool | grepper |
0mArgs:{
"Expression": "config IPV6_GRE|config IPV6_TUNNEL",
"PathPrefix": "net/ipv6/Kconfig"
}Results: {
"Output": "net/ipv6/Kconfig=196=config IPV6_NDISC_NODETYPE\n--\nnet/ipv6/Kconfig-198-\nnet/ipv6/Kconfig:199:config IPV6_TUNNEL\nnet/ipv6/Kconfig-200-\ttristate \"IPv6: IP-in-IPv6 tunnel (RFC2473)\"\n--\nnet/ipv6/Kconfig-209-\nnet/ipv6/Kconfig:210:config IPV6_GRE\nnet/ipv6/Kconfig-211-\ttristate \"IPv6: GRE tunnel\"\n"
} |
| 19/2 | 2026/09/18 04:46 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 35284 output: 35 thoughts: 23 Thoughts: Here's my summary, as though I'm reading and processing the `net/ipv6/Kconfig` file: **Diving into the IPv6 Configuration Options** Okay, time to get into the heart of the IPv6 configuration within the kernel. Let me zero in on lines 198 through 230 of `net/ipv6/Kconfig`. This is where the core IPv6 features are defined and, from a quick glance, it looks like this section is dedicated to options related to fragmentation and reassembly. Right, so we have `CONFIG_IPV6_FRAGMENTS`, which probably dictates if the kernel even *supports* IPv6 fragmentation at all. Hmm, I wonder if it’s enabled by default, or if it depends on other flags. Then there's `CONFIG_IPV6_MIP6`, which I *think* is Mobile IPv6, but I should verify the exact implications of this configuration option. I need to remember where to find the documentation for that if I haven't already. I bet `CONFIG_IPV6_FRAGMENTATION` is an orthogonal option, perhaps enabling more fine-grained control over how fragmentation is handled? Maybe a way to disable it globally for testing, if that's even supported. I should keep an eye on how these things interact with each other. If I need to troubleshoot some IPv6 performance issues, these configuration settings are probably going to be a key area to investigate. Then there's `CONFIG_IPV6_FRAG_LOW_LEVEL`... that sounds lower-level than what I was expecting. I'll need to remember to check that out, as well. I should also pay attention to any dependencies listed, particularly on whether these options are mutually exclusive or if their states affect the build output in other ways. All these IPv6 fragment options are pretty important; I can see how they could impact the performance of my network if misconfigured. It's likely that certain types of traffic or network environments are sensitive to these settings, particularly when working with larger MTU sizes and potential path MTU discovery issues. Okay, let's take a closer look at the dependencies and help text for each of these options now... |
| 20/2 | 2026/09/18 04:46 | tool | read-file |
0mArgs:{
"File": "net/ipv6/Kconfig",
"FirstLine": 198,
"LineCount": 30
}Results: {
"Contents": " 198:\t\n 199:\tconfig IPV6_TUNNEL\n 200:\t\ttristate \"IPv6: IP-in-IPv6 tunnel (RFC2473)\"\n 201:\t\tselect INET6_TUNNEL\n 202:\t\tselect DST_CACHE\n 203:\t\tselect GRO_CELLS\n 204:\t\thelp\n 205:\t\t Support for IPv6-in-IPv6 and IPv4-in-IPv6 tunnels described in\n 206:\t\t RFC 2473.\n 207:\t\n 208:\t\t If unsure, say N.\n 209:\t\n 210:\tconfig IPV6_GRE\n 211:\t\ttristate \"IPv6: GRE tunnel\"\n 212:\t\tselect IPV6_TUNNEL\n 213:\t\tselect NET_IP_TUNNEL\n 214:\t\tdepends on NET_IPGRE_DEMUX\n 215:\t\thelp\n 216:\t\t Tunneling means encapsulating data of one protocol type within\n 217:\t\t another protocol and sending it over a channel that understands the\n 218:\t\t encapsulating protocol. This particular tunneling driver implements\n 219:\t\t GRE (Generic Routing Encapsulation) and at this time allows\n 220:\t\t encapsulating of IPv4 or IPv6 over existing IPv6 infrastructure.\n 221:\t\t This driver is useful if the other endpoint is a Cisco router: Cisco\n 222:\t\t likes GRE much better than the other Linux tunneling driver (\"IP\n 223:\t\t tunneling\" above). In addition, GRE allows multicast redistribution\n 224:\t\t through the tunnel.\n 225:\t\n 226:\t\t Saying M here will produce a module called ip6_gre. If unsure, say N.\n 227:\t\n"
} |
| 21/2 | 2026/09/18 04:46 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 36279 output: 268 thoughts: 11614 Thoughts: **Analysis of Network Tunneling Patch for Fuzzing Suitability** Alright, let's break down this patch. My primary concern here is determining if these changes warrant fuzzing. The patch introduces new `SKB_DROP_REASON_*` values across various IPv4 and IPv6 GRE and tunnel code paths. Specifically, it seems like we're replacing the previous error handling, which sometimes involved boolean flags, with more specific drop reasons that are passed to the `kfree_skb_reason()` function. This looks like a significant refactoring of error reporting within the core networking stack, particularly around the GRE and IP tunnel functionality (ip_gre, ip6_gre, ip_tunnel, ip6_tunnel). Initially, I looked for "purely decorative logging or tracepoints that do not alter control flow or data structures" to see if it fell into the `WorthFuzzing=false` negative criteria, but I see now that this is a misconception of the nature of the work. While the patch primarily *adds* drop reasons and uses `kfree_skb_reason` (which *does* involve tracepoints), the *implementation* of those new drop reasons has fundamentally changed control flow. Consider the implications of the signature change to `gre_parse_header` from a boolean `csum_err` to an `enum skb_drop_reason *reason`. Now, a null `reason` pointer is not equivalent to a checksum error being skipped as before. *That* control flow difference needs fuzzing attention. Also, `pskb_may_pull` changed to `pskb_may_pull_reason` that provides a reason for the failure. Further, the restructuring of conditional checks in `ip_tunnel_rcv` and `__ip6_tnl_rcv`, and function signatures in other places are certainly non-trivial changes that affect the behavior of these functions. These are fundamental shifts in how the kernel handles errors in this specific networking area. Crucially, this isn't just about adding more tracepoints. The patch alters the handling of errors and error conditions, potentially changing how certain packets are processed or dropped. The change from boolean flags to enums that are dereferenced, passed around as pointers, is the crucial part that necessitates fuzzing. And the code being modified is clearly reachable within standard virtualized environments. The GRE and tunnel devices are readily created via netlink, which Syzkaller frequently uses. Thus, the affected code is easily exercisable, and there are many places where a drop reason, and thus control flow, is altered, depending on whether or not a `reason` value is written to, or is NULL. I am confident that these changes *do* affect control flow and warrant extensive fuzzing to detect any latent bugs. Therefore, because this refactoring and core networking changes touch reachable code, I'm marking it `WorthFuzzing=true`. To target the fuzzing effectively, I will focus on the following functions, because they are the main entrypoints to the tunnel subsystem: `gre_parse_header`, `ip_tunnel_rcv`, `ip_tunnel_xmit`, `ip_md_tunnel_xmit`, `ipgre_xmit`, `erspan_rcv`, `ip6_tnl_xmit`, `ip6_tnl_start_xmit`, `ip6gre_tunnel_xmit` and `ip6erspan_tunnel_xmit`. The following kernel configs are also required: `NET_IPGRE`, `NET_IPGRE_DEMUX`, `NET_IP_TUNNEL`, `IPV6_GRE`, and `IPV6_TUNNEL`. These should give the fuzzer the best chance of finding subtle issues introduced by this error handling refactor. |
| 22/2 | 2026/09/18 04:47 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"NET_IPGRE",
"NET_IPGRE_DEMUX",
"NET_IP_TUNNEL",
"IPV6_GRE",
"IPV6_TUNNEL"
],
"FocusSymbols": [
"gre_parse_header",
"ip_tunnel_rcv",
"ip_tunnel_xmit",
"ip_md_tunnel_xmit",
"ipgre_xmit",
"erspan_rcv",
"ip6_tnl_xmit",
"ip6_tnl_start_xmit",
"ip6gre_tunnel_xmit",
"ip6erspan_tunnel_xmit"
],
"Reasoning": "The patch modifies IPv4/IPv6 GRE and IP tunneling drivers to track and propagate granular skb drop reasons (SKB_DROP_REASON_*) to kfree_skb_reason(). This alters function signatures, pointer propagation, and branching/validation logic across rx and tx paths in reachable virtual networking drivers. Fuzzing is warranted to verify that new drop paths, pointer handling, and refactored conditional checks behave correctly under malformed or edge-case tunnel packets.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"NET_IPGRE",
"NET_IPGRE_DEMUX",
"NET_IP_TUNNEL",
"IPV6_GRE",
"IPV6_TUNNEL"
],
"FocusSymbols": [
"gre_parse_header",
"ip_tunnel_rcv",
"ip_tunnel_xmit",
"ip_md_tunnel_xmit",
"ipgre_xmit",
"erspan_rcv",
"ip6_tnl_xmit",
"ip6_tnl_start_xmit",
"ip6gre_tunnel_xmit",
"ip6erspan_tunnel_xmit"
],
"Reasoning": "The patch modifies IPv4/IPv6 GRE and IP tunneling drivers to track and propagate granular skb drop reasons (SKB_DROP_REASON_*) to kfree_skb_reason(). This alters function signatures, pointer propagation, and branching/validation logic across rx and tx paths in reachable virtual networking drivers. Fuzzing is warranted to verify that new drop paths, pointer handling, and refactored conditional checks behave correctly under malformed or edge-case tunnel packets.",
"WorthFuzzing": true
} |
| 23/1 | 2026/09/18 04:47 | action | If |
1mArgs:{
"WorthFuzzing": true
}Results: null |
| 24/2 | 2026/09/18 04:47 | agent | kmsan-evaluator |
1mModel:core Args: null Results: {
"KMSANReasoning": "The patch series updates the GRE and IPv4/IPv6 tunnel receive and transmit paths to record detailed sk_buff drop reasons by converting kfree_skb() invocations to kfree_skb_reason() and propagating `enum skb_drop_reason` pointers.\n\nAnalysis of uninitialized memory risks (KMSAN relevance):\n1. Scalar enum initialization: In all modified functions (`gre_rcv`, `ipgre_xmit`, `erspan_xmit`, `gre_tap_xmit`, `ip_tunnel_rcv`, `ip_md_tunnel_xmit`, `ip_tunnel_xmit`, `ip6gre_tunnel_xmit`, `ip6erspan_tunnel_xmit`, `__ip6_tnl_rcv`, `ip6_tnl_start_xmit`), local `reason` variables are explicitly initialized to `SKB_DROP_REASON_NOT_SPECIFIED` before any use or passing by reference.\n2. Pointer parameters: In functions taking `enum skb_drop_reason *reason` (such as `gre_parse_header`, `erspan_rcv`, `ipgre_rcv`, `ip6gre_rcv`, `ip6erspan_rcv`, `__gre6_xmit`, `ip6_tnl_xmit`), nullness is checked when necessary, and callers always provide pointers to previously initialized storage.\n3. Drop reason query functions: Calls converted to `pskb_may_pull_reason()`, `pskb_inet_may_pull_reason()`, and `skb_vlan_inet_prepare()` preserve identical buffer length and validity checks as the previous boolean wrappers, returning fully initialized enum values with no change to buffer parsing bounds.\n4. No kernel-to-user info-leaks: No uninitialized stack/heap structures or padding are exposed to user space via copy_to_user, netlink, or socket options.\n\nContrast with KASAN:\nAny potential errors in skb manipulation, buffer indexing, or reference counts in these networking paths are memory corruption or out-of-bounds access issues detectable by KASAN and DEBUG_NET. No uninitialized memory reads or information leaks are created, so a dedicated KMSAN fuzzing run is not warranted.",
"NeedsKMSAN": false
}Instruction: You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.
CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
a comprehensive suite of debugging tools and sanitizers, including KASAN
(out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
(locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
or page allocations) and kernel-to-user memory info-leaks.
Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.
Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.
Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
attributes, ioctl output arguments, socket options, or BPF buffers) where fields
or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
uninitialized bytes of existing buffers.
Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
dereferences, locking deadlocks, or use-after-free bugs (these are already caught
by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.
Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit 69b321fab7f696a20d12dd8898306fc1b8f69cd2
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Sep 18 04:45:43 2026 +0000
syz-cluster: applied patch under review
diff --git a/include/net/dropreason-core.h b/include/net/dropreason-core.h
index 12f9096515912..30378a0d22726 100644
--- a/include/net/dropreason-core.h
+++ b/include/net/dropreason-core.h
@@ -129,6 +129,12 @@
FN(PSP_INPUT) \
FN(PSP_OUTPUT) \
FN(RECURSION_LIMIT) \
+ FN(TNL_OPT_MISMATCH) \
+ FN(TNL_OLD_SEQ) \
+ FN(GRE_INVALID_HDR) \
+ FN(GRE_CSUM) \
+ FN(GRE_TUNNEL_NOT_FOUND) \
+ FN(TNL_ENCAP) \
FNe(MAX)
/**
@@ -612,6 +618,38 @@ enum skb_drop_reason {
SKB_DROP_REASON_PSP_OUTPUT,
/** @SKB_DROP_REASON_RECURSION_LIMIT: Dead loop on virtual device. */
SKB_DROP_REASON_RECURSION_LIMIT,
+ /**
+ * @SKB_DROP_REASON_TNL_OPT_MISMATCH: the tunnel options
+ * carried by the packet do not match the tunnel configuration, e.g.
+ * a GRE tunnel configured with 'icsum' or 'iseq' received a packet
+ * with no checksum or no sequence number.
+ */
+ SKB_DROP_REASON_TNL_OPT_MISMATCH,
+ /**
+ * @SKB_DROP_REASON_TNL_OLD_SEQ: the sequence number carried
+ * by the packet is older than the one expected by the tunnel, e.g.
+ * after the remote endpoint restarted and reset its sequence
+ * numbering.
+ */
+ SKB_DROP_REASON_TNL_OLD_SEQ,
+ /**
+ * @SKB_DROP_REASON_GRE_INVALID_HDR: the GRE header is invalid, e.g.
+ * an unsupported version or the routing bit is set.
+ */
+ SKB_DROP_REASON_GRE_INVALID_HDR,
+ /** @SKB_DROP_REASON_GRE_CSUM: GRE checksum error */
+ SKB_DROP_REASON_GRE_CSUM,
+ /**
+ * @SKB_DROP_REASON_GRE_TUNNEL_NOT_FOUND: no GRE tunnel found for the
+ * endpoints and the key the packet carries.
+ */
+ SKB_DROP_REASON_GRE_TUNNEL_NOT_FOUND,
+ /**
+ * @SKB_DROP_REASON_TNL_ENCAP: failed to build the
+ * encapsulation header of a tunnel, e.g. an unknown or
+ * unregistered encapsulation type.
+ */
+ SKB_DROP_REASON_TNL_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/include/net/gre.h b/include/net/gre.h
index b55f67ecd2fc4..a63f26c3f78ed 100644
--- a/include/net/gre.h
+++ b/include/net/gre.h
@@ -33,7 +33,7 @@ int gre_add_protocol(const struct gre_protocol *proto, u8 version);
int gre_del_protocol(const struct gre_protocol *proto, u8 version);
int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
- bool *csum_err, __be16 proto, int nhs);
+ enum skb_drop_reason *reason, __be16 proto, int nhs);
static inline bool netif_is_gretap(const struct net_device *dev)
{
diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h
index b99805ee2fd14..95f6d12254df6 100644
--- a/include/net/ip6_tunnel.h
+++ b/include/net/ip6_tunnel.h
@@ -143,7 +143,8 @@ int ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb,
int ip6_tnl_xmit_ctl(struct ip6_tnl *t, const struct in6_addr *laddr,
const struct in6_addr *raddr);
int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,
- struct flowi6 *fl6, int encap_limit, __u32 *pmtu, __u8 proto);
+ struct flowi6 *fl6, int encap_limit, __u32 *pmtu, __u8 proto,
+ enum skb_drop_reason *reason);
__u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 *raw);
__u32 ip6_tnl_get_cap(struct ip6_tnl *t, const struct in6_addr *laddr,
const struct in6_addr *raddr);
diff --git a/net/ipv4/gre_demux.c b/net/ipv4/gre_demux.c
index 96fd7dc6d82dd..c5d3847848efd 100644
--- a/net/ipv4/gre_demux.c
+++ b/net/ipv4/gre_demux.c
@@ -58,26 +58,44 @@ EXPORT_SYMBOL_GPL(gre_del_protocol);
/* Fills in tpi and returns header length to be pulled.
* Note that caller must use pskb_may_pull() before pulling GRE header.
+ *
+ * @reason is only written when the header is rejected, so the caller has
+ * to initialise it before the call.
+ *
+ * A NULL @reason means that the caller is not interested in the drop
+ * reason, and also that a checksum failure must not be reported: the
+ * checksum is still computed, the packet is just not rejected over it.
+ * This is what the ICMP error handlers need, as they only get a part of
+ * the original packet.
*/
int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
- bool *csum_err, __be16 proto, int nhs)
+ enum skb_drop_reason *reason, __be16 proto, int nhs)
{
const struct gre_base_hdr *greh;
__be32 *options;
int hdr_len;
- if (unlikely(!pskb_may_pull(skb, nhs + sizeof(struct gre_base_hdr))))
+ if (unlikely(!pskb_may_pull(skb, nhs + sizeof(struct gre_base_hdr)))) {
+ if (reason)
+ *reason = SKB_DROP_REASON_HDR_TRUNC;
return -EINVAL;
+ }
greh = (struct gre_base_hdr *)(skb->data + nhs);
- if (unlikely(greh->flags & (GRE_VERSION | GRE_ROUTING)))
+ if (unlikely(greh->flags & (GRE_VERSION | GRE_ROUTING))) {
+ if (reason)
+ *reason = SKB_DROP_REASON_GRE_INVALID_HDR;
return -EINVAL;
+ }
gre_flags_to_tnl_flags(tpi->flags, greh->flags);
hdr_len = gre_calc_hlen(tpi->flags);
- if (!pskb_may_pull(skb, nhs + hdr_len))
+ if (!pskb_may_pull(skb, nhs + hdr_len)) {
+ if (reason)
+ *reason = SKB_DROP_REASON_HDR_TRUNC;
return -EINVAL;
+ }
greh = (struct gre_base_hdr *)(skb->data + nhs);
tpi->proto = greh->protocol;
@@ -87,8 +105,8 @@ int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
if (!skb_checksum_simple_validate(skb)) {
skb_checksum_try_convert(skb, IPPROTO_GRE,
null_compute_pseudo);
- } else if (csum_err) {
- *csum_err = true;
+ } else if (reason) {
+ *reason = SKB_DROP_REASON_GRE_CSUM;
return -EINVAL;
}
@@ -116,8 +134,11 @@ int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
val = skb_header_pointer(skb, nhs + hdr_len,
sizeof(_val), &_val);
- if (!val)
+ if (!val) {
+ if (reason)
+ *reason = SKB_DROP_REASON_HDR_TRUNC;
return -EINVAL;
+ }
tpi->proto = proto;
if ((*val & 0xF0) != 0x40)
hdr_len += 4;
@@ -132,8 +153,11 @@ int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
greh->protocol == htons(ETH_P_ERSPAN2)) {
struct erspan_base_hdr *ershdr;
- if (!pskb_may_pull(skb, nhs + hdr_len + sizeof(*ershdr)))
+ if (!pskb_may_pull(skb, nhs + hdr_len + sizeof(*ershdr))) {
+ if (reason)
+ *reason = SKB_DROP_REASON_HDR_TRUNC;
return -EINVAL;
+ }
ershdr = (struct erspan_base_hdr *)(skb->data + nhs + hdr_len);
tpi->key = cpu_to_be32(get_session_id(ershdr));
@@ -145,16 +169,20 @@ EXPORT_SYMBOL(gre_parse_header);
static int gre_rcv(struct sk_buff *skb)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
const struct gre_protocol *proto;
u8 ver;
int ret;
- if (!pskb_may_pull(skb, 12))
+ reason = pskb_may_pull_reason(skb, 12);
+ if (reason)
goto drop;
ver = skb->data[1]&0x7f;
- if (ver >= GREPROTO_MAX)
+ if (ver >= GREPROTO_MAX) {
+ reason = SKB_DROP_REASON_UNHANDLED_PROTO;
goto drop;
+ }
rcu_read_lock();
proto = rcu_dereference(gre_proto[ver]);
@@ -167,11 +195,11 @@ static int gre_rcv(struct sk_buff *skb)
drop_nohandler:
rcu_read_unlock();
dev_core_stats_rx_nohandler_inc(skb->dev);
- kfree_skb(skb);
+ kfree_skb_reason(skb, SKB_DROP_REASON_UNHANDLED_PROTO);
return NET_RX_DROP;
drop:
dev_core_stats_rx_dropped_inc(skb->dev);
- kfree_skb(skb);
+ kfree_skb_reason(skb, reason);
return NET_RX_DROP;
}
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 5e877018e006c..71360af429ea7 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -265,7 +265,7 @@ static bool is_erspan_type1(int gre_hdr_len)
}
static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
- int gre_hdr_len)
+ int gre_hdr_len, enum skb_drop_reason *reason)
{
struct net *net = dev_net(skb->dev);
struct metadata_dst *tun_dst = NULL;
@@ -289,8 +289,10 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
iph->saddr, iph->daddr, 0);
} else {
if (unlikely(!pskb_may_pull(skb,
- gre_hdr_len + sizeof(*ershdr))))
+ gre_hdr_len + sizeof(*ershdr)))) {
+ *reason = SKB_DROP_REASON_HDR_TRUNC;
return PACKET_REJECT;
+ }
ershdr = (struct erspan_base_hdr *)(skb->data + gre_hdr_len);
ver = ershdr->ver;
@@ -306,8 +308,10 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
else
len = gre_hdr_len + erspan_hdr_len(ver);
- if (unlikely(!pskb_may_pull(skb, len)))
+ if (unlikely(!pskb_may_pull(skb, len))) {
+ *reason = SKB_DROP_REASON_HDR_TRUNC;
return PACKET_REJECT;
+ }
if (__iptunnel_pull_header(skb,
len,
@@ -327,8 +331,10 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
tun_dst = ip_tun_rx_dst(skb, flags,
tun_id, sizeof(*md));
- if (!tun_dst)
+ if (!tun_dst) {
+ *reason = SKB_DROP_REASON_NOMEM;
return PACKET_REJECT;
+ }
/* MUST set options_len before referencing options */
info = &tun_dst->u.tun_info;
@@ -356,15 +362,17 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
ip_tunnel_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error);
return PACKET_RCVD;
}
+ *reason = SKB_DROP_REASON_GRE_TUNNEL_NOT_FOUND;
return PACKET_REJECT;
drop:
- kfree_skb(skb);
+ kfree_skb_reason(skb, SKB_DROP_REASON_NOMEM);
return PACKET_RCVD;
}
static int __ipgre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
- struct ip_tunnel_net *itn, int hdr_len, bool raw_proto)
+ struct ip_tunnel_net *itn, int hdr_len, bool raw_proto,
+ enum skb_drop_reason *reason)
{
struct metadata_dst *tun_dst = NULL;
const struct iphdr *iph;
@@ -400,22 +408,25 @@ static int __ipgre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
tun_id = key32_to_tunnel_id(tpi->key);
tun_dst = ip_tun_rx_dst(skb, flags, tun_id, 0);
- if (!tun_dst)
+ if (!tun_dst) {
+ *reason = SKB_DROP_REASON_NOMEM;
return PACKET_REJECT;
+ }
}
ip_tunnel_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error);
return PACKET_RCVD;
}
+ *reason = SKB_DROP_REASON_GRE_TUNNEL_NOT_FOUND;
return PACKET_NEXT;
drop:
- kfree_skb(skb);
+ kfree_skb_reason(skb, SKB_DROP_REASON_NOMEM);
return PACKET_RCVD;
}
static int ipgre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
- int hdr_len)
+ int hdr_len, enum skb_drop_reason *reason)
{
struct net *net = dev_net(skb->dev);
struct ip_tunnel_net *itn;
@@ -426,21 +437,21 @@ static int ipgre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
else
itn = net_generic(net, ipgre_net_id);
- res = __ipgre_rcv(skb, tpi, itn, hdr_len, false);
+ res = __ipgre_rcv(skb, tpi, itn, hdr_len, false, reason);
if (res == PACKET_NEXT && tpi->proto == htons(ETH_P_TEB)) {
/* ipgre tunnels in collect metadata mode should receive
* also ETH_P_TEB traffic.
*/
itn = net_generic(net, ipgre_net_id);
- res = __ipgre_rcv(skb, tpi, itn, hdr_len, true);
+ res = __ipgre_rcv(skb, tpi, itn, hdr_len, true, reason);
}
return res;
}
static int gre_rcv(struct sk_buff *skb)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct tnl_ptk_info tpi;
- bool csum_err = false;
int hdr_len;
#ifdef CONFIG_NET_IPGRE_BROADCAST
@@ -451,25 +462,25 @@ static int gre_rcv(struct sk_buff *skb)
}
#endif
- hdr_len = gre_parse_header(skb, &tpi, &csum_err, htons(ETH_P_IP), 0);
+ hdr_len = gre_parse_header(skb, &tpi, &reason, htons(ETH_P_IP), 0);
if (hdr_len < 0)
goto drop;
if (unlikely(tpi.proto == htons(ETH_P_ERSPAN) ||
tpi.proto == htons(ETH_P_ERSPAN2))) {
- if (erspan_rcv(skb, &tpi, hdr_len) == PACKET_RCVD)
+ if (erspan_rcv(skb, &tpi, hdr_len, &reason) == PACKET_RCVD)
return 0;
goto out;
}
- if (ipgre_rcv(skb, &tpi, hdr_len) == PACKET_RCVD)
+ if (ipgre_rcv(skb, &tpi, hdr_len, &reason) == PACKET_RCVD)
return 0;
out:
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
drop:
dev_core_stats_rx_dropped_inc(skb->dev);
- kfree_skb(skb);
+ kfree_skb_reason(skb, reason);
return 0;
}
@@ -496,6 +507,7 @@ static int gre_handle_offloads(struct sk_buff *skb, bool csum)
static void gre_fb_xmit(struct sk_buff *skb, struct net_device *dev,
__be16 proto)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct ip_tunnel *tunnel = netdev_priv(dev);
IP_TUNNEL_DECLARE_FLAGS(flags) = { };
struct ip_tunnel_info *tun_info;
@@ -504,19 +516,25 @@ static void gre_fb_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 err_free_skb;
+ }
key = &tun_info->key;
tunnel_hlen = gre_calc_hlen(key->tun_flags);
- if (skb_cow_head(skb, dev->needed_headroom))
+ if (skb_cow_head(skb, dev->needed_headroom)) {
+ reason = SKB_DROP_REASON_NOMEM;
goto err_free_skb;
+ }
/* Push Tunnel header. */
if (gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT,
- tunnel->parms.o_flags)))
+ tunnel->parms.o_flags))) {
+ reason = SKB_DROP_REASON_NOMEM;
goto err_free_skb;
+ }
__set_bit(IP_TUNNEL_CSUM_BIT, flags);
__set_bit(IP_TUNNEL_KEY_BIT, flags);
@@ -533,12 +551,13 @@ static void gre_fb_xmit(struct sk_buff *skb, struct net_device *dev,
return;
err_free_skb:
- kfree_skb(skb);
+ kfree_skb_reason(skb, reason);
DEV_STATS_INC(dev, tx_dropped);
}
static void erspan_fb_xmit(struct sk_buff *skb, struct net_device *dev)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct ip_tunnel *tunnel = netdev_priv(dev);
IP_TUNNEL_DECLARE_FLAGS(flags) = { };
struct ip_tunnel_info *tun_info;
@@ -552,29 +571,41 @@ static void erspan_fb_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 err_free_skb;
+ }
key = &tun_info->key;
- if (!test_bit(IP_TUNNEL_ERSPAN_OPT_BIT, tun_info->key.tun_flags))
+ if (!test_bit(IP_TUNNEL_ERSPAN_OPT_BIT, tun_info->key.tun_flags)) {
+ reason = SKB_DROP_REASON_TUNNEL_TXINFO;
goto err_free_skb;
- if (tun_info->options_len < sizeof(*md))
+ }
+ if (tun_info->options_len < sizeof(*md)) {
+ reason = SKB_DROP_REASON_TUNNEL_TXINFO;
goto err_free_skb;
+ }
md = ip_tunnel_info_opts(tun_info);
/* ERSPAN has fixed 8 byte GRE header */
version = md->version;
tunnel_hlen = 8 + erspan_hdr_len(version);
- if (skb_cow_head(skb, dev->needed_headroom))
+ if (skb_cow_head(skb, dev->needed_headroom)) {
+ reason = SKB_DROP_REASON_NOMEM;
goto err_free_skb;
+ }
- if (gre_handle_offloads(skb, false))
+ if (gre_handle_offloads(skb, false)) {
+ reason = SKB_DROP_REASON_NOMEM;
goto err_free_skb;
+ }
if (skb->len > dev->mtu + dev->hard_header_len) {
- if (pskb_trim(skb, dev->mtu + dev->hard_header_len))
+ if (pskb_trim(skb, dev->mtu + dev->hard_header_len)) {
+ reason = SKB_DROP_REASON_NOMEM;
goto err_free_skb;
+ }
truncate = true;
}
@@ -606,6 +637,7 @@ static void erspan_fb_xmit(struct sk_buff *skb, struct net_device *dev)
truncate, true);
proto = htons(ETH_P_ERSPAN2);
} else {
+ reason = SKB_DROP_REASON_UNHANDLED_PROTO;
goto err_free_skb;
}
@@ -618,7 +650,7 @@ static void erspan_fb_xmit(struct sk_buff *skb, struct net_device *dev)
return;
err_free_skb:
- kfree_skb(skb);
+ kfree_skb_reason(skb, reason);
DEV_STATS_INC(dev, tx_dropped);
}
@@ -649,11 +681,13 @@ static int gre_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
static netdev_tx_t ipgre_xmit(struct sk_buff *skb,
struct net_device *dev)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct ip_tunnel *tunnel = netdev_priv(dev);
IP_TUNNEL_DECLARE_FLAGS(flags);
const struct iphdr *tnl_params;
- if (!pskb_inet_may_pull(skb))
+ reason = pskb_inet_may_pull_reason(skb);
+ if (reason)
goto free_skb;
if (tunnel->collect_md) {
@@ -664,10 +698,13 @@ static netdev_tx_t ipgre_xmit(struct sk_buff *skb,
if (dev->header_ops) {
int pull_len = tunnel->hlen + sizeof(struct iphdr);
- if (skb_cow_head(skb, 0))
+ if (skb_cow_head(skb, 0)) {
+ reason = SKB_DROP_REASON_NOMEM;
goto free_skb;
+ }
- if (!pskb_may_pull(skb, pull_len))
+ reason = pskb_may_pull_reason(skb, pull_len);
+ if (reason)
goto free_skb;
tnl_params = (const struct iphdr *)skb->data;
@@ -677,25 +714,31 @@ static netdev_tx_t ipgre_xmit(struct sk_buff *skb,
skb_reset_mac_header(skb);
if (skb->ip_summed == CHECKSUM_PARTIAL &&
- skb_checksum_start(skb) < skb->data)
+ skb_checksum_start(skb) < skb->data) {
+ reason = SKB_DROP_REASON_SKB_CSUM;
goto free_skb;
+ }
} else {
- if (skb_cow_head(skb, dev->needed_headroom))
+ if (skb_cow_head(skb, dev->needed_headroom)) {
+ reason = SKB_DROP_REASON_NOMEM;
goto free_skb;
+ }
tnl_params = &tunnel->parms.iph;
}
ip_tunnel_flags_copy(flags, tunnel->parms.o_flags);
- if (gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT, flags)))
+ if (gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT, flags))) {
+ reason = SKB_DROP_REASON_NOMEM;
goto free_skb;
+ }
__gre_xmit(skb, dev, tnl_params, skb->protocol, flags);
return NETDEV_TX_OK;
free_skb:
- kfree_skb(skb);
+ kfree_skb_reason(skb, reason);
DEV_STATS_INC(dev, tx_dropped);
return NETDEV_TX_OK;
}
@@ -703,12 +746,14 @@ static netdev_tx_t ipgre_xmit(struct sk_buff *skb,
static netdev_tx_t erspan_xmit(struct sk_buff *skb,
struct net_device *dev)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct ip_tunnel *tunnel = netdev_priv(dev);
IP_TUNNEL_DECLARE_FLAGS(flags);
bool truncate = false;
__be16 proto;
- if (!pskb_inet_may_pull(skb))
+ reason = pskb_inet_may_pull_reason(skb);
+ if (reason)
goto free_skb;
if (tunnel->collect_md) {
@@ -716,15 +761,21 @@ static netdev_tx_t erspan_xmit(struct sk_buff *skb,
return NETDEV_TX_OK;
}
- if (gre_handle_offloads(skb, false))
+ if (gre_handle_offloads(skb, false)) {
+ reason = SKB_DROP_REASON_NOMEM;
goto free_skb;
+ }
- if (skb_cow_head(skb, dev->needed_headroom))
+ if (skb_cow_head(skb, dev->needed_headroom)) {
+ reason = SKB_DROP_REASON_NOMEM;
goto free_skb;
+ }
if (skb->len > dev->mtu + dev->hard_header_len) {
- if (pskb_trim(skb, dev->mtu + dev->hard_header_len))
+ if (pskb_trim(skb, dev->mtu + dev->hard_header_len)) {
+ reason = SKB_DROP_REASON_NOMEM;
goto free_skb;
+ }
truncate = true;
}
@@ -745,6 +796,7 @@ static netdev_tx_t erspan_xmit(struct sk_buff *skb,
truncate, true);
proto = htons(ETH_P_ERSPAN2);
} else {
+ reason = SKB_DROP_REASON_UNHANDLED_PROTO;
goto free_skb;
}
@@ -753,7 +805,7 @@ static netdev_tx_t erspan_xmit(struct sk_buff *skb,
return NETDEV_TX_OK;
free_skb:
- kfree_skb(skb);
+ kfree_skb_reason(skb, reason);
DEV_STATS_INC(dev, tx_dropped);
return NETDEV_TX_OK;
}
@@ -761,10 +813,12 @@ static netdev_tx_t erspan_xmit(struct sk_buff *skb,
static netdev_tx_t gre_tap_xmit(struct sk_buff *skb,
struct net_device *dev)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct ip_tunnel *tunnel = netdev_priv(dev);
IP_TUNNEL_DECLARE_FLAGS(flags);
- if (!pskb_inet_may_pull(skb))
+ reason = pskb_inet_may_pull_reason(skb);
+ if (reason)
goto free_skb;
if (tunnel->collect_md) {
@@ -774,17 +828,21 @@ static netdev_tx_t gre_tap_xmit(struct sk_buff *skb,
ip_tunnel_flags_copy(flags, tunnel->parms.o_flags);
- if (gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT, flags)))
+ if (gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT, flags))) {
+ reason = SKB_DROP_REASON_NOMEM;
goto free_skb;
+ }
- if (skb_cow_head(skb, dev->needed_headroom))
+ if (skb_cow_head(skb, dev->needed_headroom)) {
+ reason = SKB_DROP_REASON_NOMEM;
goto free_skb;
+ }
__gre_xmit(skb, dev, &tunnel->parms.iph, htons(ETH_P_TEB), flags);
return NETDEV_TX_OK;
free_skb:
- kfree_skb(skb);
+ kfree_skb_reason(skb, reason);
DEV_STATS_INC(dev, tx_dropped);
return NETDEV_TX_OK;
}
diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
index 0875474a578a4..66cb0b86fa79a 100644
--- a/net/ipv4/ip_tunnel.c
+++ b/net/ipv4/ip_tunnel.c
@@ -384,6 +384,7 @@ int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb,
const struct tnl_ptk_info *tpi, struct metadata_dst *tun_dst,
bool log_ecn_error)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
const struct iphdr *iph = ip_hdr(skb);
int nh, err;
@@ -398,14 +399,22 @@ int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb,
test_bit(IP_TUNNEL_CSUM_BIT, tpi->flags)) {
DEV_STATS_INC(tunnel->dev, rx_crc_errors);
DEV_STATS_INC(tunnel->dev, rx_errors);
+ reason = SKB_DROP_REASON_TNL_OPT_MISMATCH;
goto drop;
}
if (test_bit(IP_TUNNEL_SEQ_BIT, tunnel->parms.i_flags)) {
- if (!test_bit(IP_TUNNEL_SEQ_BIT, tpi->flags) ||
- (tunnel->i_seqno && (s32)(ntohl(tpi->seq) - tunnel->i_seqno) < 0)) {
+ if (!test_bit(IP_TUNNEL_SEQ_BIT, tpi->flags)) {
DEV_STATS_INC(tunnel->dev, rx_fifo_errors);
DEV_STATS_INC(tunnel->dev, rx_errors);
+ reason = SKB_DROP_REASON_TNL_OPT_MISMATCH;
+ goto drop;
+ }
+ if (tunnel->i_seqno &&
+ (s32)(ntohl(tpi->seq) - tunnel->i_seqno) < 0) {
+ DEV_STATS_INC(tunnel->dev, rx_fifo_errors);
+ DEV_STATS_INC(tunnel->dev, rx_errors);
+ reason = SKB_DROP_REASON_TNL_OLD_SEQ;
goto drop;
}
tunnel->i_seqno = ntohl(tpi->seq) + 1;
@@ -419,7 +428,8 @@ int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb,
skb_set_network_header(skb, (tunnel->dev->type == ARPHRD_ETHER) ? ETH_HLEN : 0);
- if (!pskb_inet_may_pull(skb)) {
+ reason = pskb_inet_may_pull_reason(skb);
+ if (reason) {
DEV_STATS_INC(tunnel->dev, rx_length_errors);
DEV_STATS_INC(tunnel->dev, rx_errors);
goto drop;
@@ -434,6 +444,7 @@ int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb,
if (err > 1) {
DEV_STATS_INC(tunnel->dev, rx_frame_errors);
DEV_STATS_INC(tunnel->dev, rx_errors);
+ reason = SKB_DROP_REASON_IP_TUNNEL_ECN;
goto drop;
}
}
@@ -457,7 +468,7 @@ int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb,
drop:
if (tun_dst)
dst_release((struct dst_entry *)tun_dst);
- kfree_skb(skb);
+ kfree_skb_reason(skb, reason);
return 0;
}
EXPORT_SYMBOL_GPL(ip_tunnel_rcv);
@@ -575,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;
@@ -588,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);
@@ -608,8 +622,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_TNL_ENCAP;
goto tx_error;
+ }
use_cache = ip_tunnel_dst_cache_usable(skb, tun_info);
if (use_cache)
@@ -618,6 +634,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)
@@ -627,6 +644,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;
}
@@ -635,6 +653,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;
}
@@ -652,6 +671,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;
}
@@ -666,13 +686,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;
@@ -700,9 +721,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 &&
@@ -723,8 +750,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);
@@ -741,8 +770,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
@@ -769,8 +800,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_TNL_ENCAP;
goto tx_error;
+ }
if (connected && md) {
use_cache = ip_tunnel_dst_cache_usable(skb, tun_info);
@@ -787,6 +820,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)
@@ -800,6 +834,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;
}
@@ -809,6 +844,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;
}
@@ -843,7 +879,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;
}
@@ -859,7 +895,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);
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index 8ebda0b6a78b2..5d1e55813fce2 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -454,7 +454,8 @@ static int ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
return 0;
}
-static int ip6gre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi)
+static int ip6gre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
+ enum skb_drop_reason *reason)
{
const struct ipv6hdr *ipv6h;
struct ip6_tnl *tunnel;
@@ -473,8 +474,10 @@ static int ip6gre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi)
tun_id = key32_to_tunnel_id(tpi->key);
tun_dst = ipv6_tun_rx_dst(skb, flags, tun_id, 0);
- if (!tun_dst)
+ if (!tun_dst) {
+ *reason = SKB_DROP_REASON_NOMEM;
return PACKET_REJECT;
+ }
ip6_tnl_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error);
} else {
@@ -484,12 +487,14 @@ static int ip6gre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi)
return PACKET_RCVD;
}
+ *reason = SKB_DROP_REASON_GRE_TUNNEL_NOT_FOUND;
return PACKET_REJECT;
}
static int ip6erspan_rcv(struct sk_buff *skb,
struct tnl_ptk_info *tpi,
- int gre_hdr_len)
+ int gre_hdr_len,
+ enum skb_drop_reason *reason)
{
struct erspan_base_hdr *ershdr;
const struct ipv6hdr *ipv6h;
@@ -497,8 +502,10 @@ static int ip6erspan_rcv(struct sk_buff *skb,
struct ip6_tnl *tunnel;
u8 ver;
- if (unlikely(!pskb_may_pull(skb, sizeof(*ershdr))))
+ if (unlikely(!pskb_may_pull(skb, sizeof(*ershdr)))) {
+ *reason = SKB_DROP_REASON_HDR_TRUNC;
return PACKET_REJECT;
+ }
ipv6h = ipv6_hdr(skb);
ershdr = (struct erspan_base_hdr *)skb->data;
@@ -510,13 +517,17 @@ static int ip6erspan_rcv(struct sk_buff *skb,
if (tunnel) {
int len = erspan_hdr_len(ver);
- if (unlikely(!pskb_may_pull(skb, len)))
+ if (unlikely(!pskb_may_pull(skb, len))) {
+ *reason = SKB_DROP_REASON_HDR_TRUNC;
return PACKET_REJECT;
+ }
if (__iptunnel_pull_header(skb, len,
htons(ETH_P_TEB),
- false, false) < 0)
+ false, false) < 0) {
+ *reason = SKB_DROP_REASON_NOMEM;
return PACKET_REJECT;
+ }
if (tunnel->parms.collect_md) {
struct erspan_metadata *pkt_md, *md;
@@ -532,8 +543,10 @@ static int ip6erspan_rcv(struct sk_buff *skb,
tun_dst = ipv6_tun_rx_dst(skb, flags, tun_id,
sizeof(*md));
- if (!tun_dst)
+ if (!tun_dst) {
+ *reason = SKB_DROP_REASON_NOMEM;
return PACKET_REJECT;
+ }
/* MUST set options_len before referencing options */
info = &tun_dst->u.tun_info;
@@ -564,37 +577,40 @@ static int ip6erspan_rcv(struct sk_buff *skb,
return PACKET_RCVD;
}
+ *reason = SKB_DROP_REASON_GRE_TUNNEL_NOT_FOUND;
return PACKET_REJECT;
}
static int gre_rcv(struct sk_buff *skb)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct tnl_ptk_info tpi;
- bool csum_err = false;
int hdr_len;
- hdr_len = gre_parse_header(skb, &tpi, &csum_err, htons(ETH_P_IPV6), 0);
+ hdr_len = gre_parse_header(skb, &tpi, &reason, htons(ETH_P_IPV6), 0);
if (hdr_len < 0)
goto drop;
- if (iptunnel_pull_header(skb, hdr_len, tpi.proto, false))
+ if (iptunnel_pull_header(skb, hdr_len, tpi.proto, false)) {
+ reason = SKB_DROP_REASON_NOMEM;
goto drop;
+ }
if (unlikely(tpi.proto == htons(ETH_P_ERSPAN) ||
tpi.proto == htons(ETH_P_ERSPAN2))) {
- if (ip6erspan_rcv(skb, &tpi, hdr_len) == PACKET_RCVD)
+ if (ip6erspan_rcv(skb, &tpi, hdr_len, &reason) == PACKET_RCVD)
return 0;
goto out;
}
- if (ip6gre_rcv(skb, &tpi) == PACKET_RCVD)
+ if (ip6gre_rcv(skb, &tpi, &reason) == PACKET_RCVD)
return 0;
out:
icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
drop:
dev_core_stats_rx_dropped_inc(skb->dev);
- kfree_skb(skb);
+ kfree_skb_reason(skb, reason);
return 0;
}
@@ -677,10 +693,10 @@ static int prepare_ip6gre_xmit_ipv6(struct sk_buff *skb,
return 0;
}
-static int prepare_ip6gre_xmit_other(struct sk_buff *skb,
- struct net_device *dev,
- struct flowi6 *fl6, __u8 *dsfield,
- int *encap_limit)
+static void prepare_ip6gre_xmit_other(struct sk_buff *skb,
+ struct net_device *dev,
+ struct flowi6 *fl6, __u8 *dsfield,
+ int *encap_limit)
{
struct ip6_tnl *t = netdev_priv(dev);
@@ -700,8 +716,6 @@ static int prepare_ip6gre_xmit_other(struct sk_buff *skb,
fl6->flowi6_mark = t->parms.fwmark;
fl6->flowi6_uid = sock_net_uid(dev_net(dev), NULL);
-
- return 0;
}
static struct ip_tunnel_info *skb_tunnel_info_txcheck(struct sk_buff *skb)
@@ -718,7 +732,8 @@ static struct ip_tunnel_info *skb_tunnel_info_txcheck(struct sk_buff *skb)
static netdev_tx_t __gre6_xmit(struct sk_buff *skb,
struct net_device *dev, __u8 dsfield,
struct flowi6 *fl6, int encap_limit,
- __u32 *pmtu, __be16 proto)
+ __u32 *pmtu, __be16 proto,
+ enum skb_drop_reason *reason)
{
struct ip6_tnl *tunnel = netdev_priv(dev);
IP_TUNNEL_DECLARE_FLAGS(flags);
@@ -742,8 +757,10 @@ static netdev_tx_t __gre6_xmit(struct sk_buff *skb,
tun_info = skb_tunnel_info_txcheck(skb);
if (IS_ERR(tun_info) ||
- unlikely(ip_tunnel_info_af(tun_info) != AF_INET6))
+ unlikely(ip_tunnel_info_af(tun_info) != AF_INET6)) {
+ *reason = SKB_DROP_REASON_TUNNEL_TXINFO;
return -EINVAL;
+ }
key = &tun_info->key;
memset(fl6, 0, sizeof(*fl6));
@@ -761,8 +778,11 @@ static netdev_tx_t __gre6_xmit(struct sk_buff *skb,
ip_tunnel_flags_and(flags, flags, key->tun_flags);
tun_hlen = gre_calc_hlen(flags);
- if (skb_cow_head(skb, dev->needed_headroom ?: tun_hlen + tunnel->encap_hlen))
+ if (skb_cow_head(skb, dev->needed_headroom ?:
+ tun_hlen + tunnel->encap_hlen)) {
+ *reason = SKB_DROP_REASON_NOMEM;
return -ENOMEM;
+ }
gre_build_header(skb, tun_hlen,
flags, protocol,
@@ -772,8 +792,10 @@ static netdev_tx_t __gre6_xmit(struct sk_buff *skb,
0);
} else {
- if (skb_cow_head(skb, dev->needed_headroom ?: tunnel->hlen))
+ if (skb_cow_head(skb, dev->needed_headroom ?: tunnel->hlen)) {
+ *reason = SKB_DROP_REASON_NOMEM;
return -ENOMEM;
+ }
ip_tunnel_flags_copy(flags, tunnel->parms.o_flags);
@@ -785,10 +807,11 @@ static netdev_tx_t __gre6_xmit(struct sk_buff *skb,
}
return ip6_tnl_xmit(skb, dev, dsfield, fl6, encap_limit, pmtu,
- NEXTHDR_GRE);
+ NEXTHDR_GRE, reason);
}
-static inline int ip6gre_xmit_ipv4(struct sk_buff *skb, struct net_device *dev)
+static inline int ip6gre_xmit_ipv4(struct sk_buff *skb, struct net_device *dev,
+ enum skb_drop_reason *reason)
{
struct ip6_tnl *t = netdev_priv(dev);
int encap_limit = -1;
@@ -805,11 +828,13 @@ static inline int ip6gre_xmit_ipv4(struct sk_buff *skb, struct net_device *dev)
err = gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT,
t->parms.o_flags));
- if (err)
+ if (err) {
+ *reason = SKB_DROP_REASON_NOMEM;
return -1;
+ }
err = __gre6_xmit(skb, dev, dsfield, &fl6, encap_limit, &mtu,
- skb->protocol);
+ skb->protocol, reason);
if (err != 0) {
/* XXX: send ICMP error even if DF is not set. */
if (err == -EMSGSIZE)
@@ -821,7 +846,8 @@ static inline int ip6gre_xmit_ipv4(struct sk_buff *skb, struct net_device *dev)
return 0;
}
-static inline int ip6gre_xmit_ipv6(struct sk_buff *skb, struct net_device *dev)
+static inline int ip6gre_xmit_ipv6(struct sk_buff *skb, struct net_device *dev,
+ enum skb_drop_reason *reason)
{
struct ip6_tnl *t = netdev_priv(dev);
struct ipv6hdr *ipv6h = ipv6_hdr(skb);
@@ -831,19 +857,25 @@ static inline int ip6gre_xmit_ipv6(struct sk_buff *skb, struct net_device *dev)
__u32 mtu;
int err;
- if (ipv6_addr_equal(&t->parms.raddr, &ipv6h->saddr))
+ if (ipv6_addr_equal(&t->parms.raddr, &ipv6h->saddr)) {
+ *reason = SKB_DROP_REASON_RECURSION_LIMIT;
return -1;
+ }
if (!t->parms.collect_md &&
- prepare_ip6gre_xmit_ipv6(skb, dev, &fl6, &dsfield, &encap_limit))
+ prepare_ip6gre_xmit_ipv6(skb, dev, &fl6, &dsfield, &encap_limit)) {
+ *reason = SKB_DROP_REASON_IPV6_BAD_EXTHDR;
return -1;
+ }
if (gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT,
- t->parms.o_flags)))
+ t->parms.o_flags))) {
+ *reason = SKB_DROP_REASON_NOMEM;
return -1;
+ }
err = __gre6_xmit(skb, dev, dsfield, &fl6, encap_limit,
- &mtu, skb->protocol);
+ &mtu, skb->protocol, reason);
if (err != 0) {
if (err == -EMSGSIZE)
icmpv6_ndo_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
@@ -853,7 +885,8 @@ static inline int ip6gre_xmit_ipv6(struct sk_buff *skb, struct net_device *dev)
return 0;
}
-static int ip6gre_xmit_other(struct sk_buff *skb, struct net_device *dev)
+static int ip6gre_xmit_other(struct sk_buff *skb, struct net_device *dev,
+ enum skb_drop_reason *reason)
{
struct ip6_tnl *t = netdev_priv(dev);
int encap_limit = -1;
@@ -862,15 +895,18 @@ static int ip6gre_xmit_other(struct sk_buff *skb, struct net_device *dev)
__u32 mtu;
int err;
- if (!t->parms.collect_md &&
- prepare_ip6gre_xmit_other(skb, dev, &fl6, &dsfield, &encap_limit))
- return -1;
+ if (!t->parms.collect_md)
+ prepare_ip6gre_xmit_other(skb, dev, &fl6,
+ &dsfield, &encap_limit);
err = gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT,
t->parms.o_flags));
- if (err)
+ if (err) {
+ *reason = SKB_DROP_REASON_NOMEM;
return err;
- err = __gre6_xmit(skb, dev, dsfield, &fl6, encap_limit, &mtu, skb->protocol);
+ }
+ err = __gre6_xmit(skb, dev, dsfield, &fl6, encap_limit, &mtu,
+ skb->protocol, reason);
return err;
}
@@ -878,16 +914,20 @@ static int ip6gre_xmit_other(struct sk_buff *skb, struct net_device *dev)
static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,
struct net_device *dev)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct ip_tunnel_info *tun_info = NULL;
struct ip6_tnl *t = netdev_priv(dev);
__be16 payload_protocol;
int ret;
- if (!pskb_inet_may_pull(skb))
+ reason = pskb_inet_may_pull_reason(skb);
+ if (reason)
goto tx_err;
- if (!ip6_tnl_xmit_ctl(t, &t->parms.laddr, &t->parms.raddr))
+ if (!ip6_tnl_xmit_ctl(t, &t->parms.laddr, &t->parms.raddr)) {
+ reason = SKB_DROP_REASON_DEV_READY;
goto tx_err;
+ }
if (t->parms.collect_md)
tun_info = skb_tunnel_info_txcheck(skb);
@@ -895,13 +935,13 @@ static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,
payload_protocol = skb_protocol(skb, true);
switch (payload_protocol) {
case htons(ETH_P_IP):
- ret = ip6gre_xmit_ipv4(skb, dev);
+ ret = ip6gre_xmit_ipv4(skb, dev, &reason);
break;
case htons(ETH_P_IPV6):
- ret = ip6gre_xmit_ipv6(skb, dev);
+ ret = ip6gre_xmit_ipv6(skb, dev, &reason);
break;
default:
- ret = ip6gre_xmit_other(skb, dev);
+ ret = ip6gre_xmit_other(skb, dev, &reason);
break;
}
@@ -914,13 +954,14 @@ static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,
if (!IS_ERR(tun_info))
DEV_STATS_INC(dev, tx_errors);
DEV_STATS_INC(dev, tx_dropped);
- kfree_skb(skb);
+ kfree_skb_reason(skb, reason);
return NETDEV_TX_OK;
}
static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
struct net_device *dev)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct ip_tunnel_info *tun_info = NULL;
struct ip6_tnl *t = netdev_priv(dev);
struct dst_entry *dst = skb_dst(skb);
@@ -934,18 +975,25 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
__u32 mtu;
int nhoff;
- if (!pskb_inet_may_pull(skb))
+ reason = pskb_inet_may_pull_reason(skb);
+ if (reason)
goto tx_err;
- if (!ip6_tnl_xmit_ctl(t, &t->parms.laddr, &t->parms.raddr))
+ if (!ip6_tnl_xmit_ctl(t, &t->parms.laddr, &t->parms.raddr)) {
+ reason = SKB_DROP_REASON_DEV_READY;
goto tx_err;
+ }
- if (gre_handle_offloads(skb, false))
+ if (gre_handle_offloads(skb, false)) {
+ reason = SKB_DROP_REASON_NOMEM;
goto tx_err;
+ }
if (skb->len > dev->mtu + dev->hard_header_len) {
- if (pskb_trim(skb, dev->mtu + dev->hard_header_len))
+ if (pskb_trim(skb, dev->mtu + dev->hard_header_len)) {
+ reason = SKB_DROP_REASON_NOMEM;
goto tx_err;
+ }
truncate = true;
}
@@ -965,8 +1013,10 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
truncate = true;
}
- if (skb_cow_head(skb, dev->needed_headroom ?: t->hlen))
+ if (skb_cow_head(skb, dev->needed_headroom ?: t->hlen)) {
+ reason = SKB_DROP_REASON_NOMEM;
goto tx_err;
+ }
IPCB(skb)->flags = 0;
@@ -980,8 +1030,10 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
tun_info = skb_tunnel_info_txcheck(skb);
if (IS_ERR(tun_info) ||
- unlikely(ip_tunnel_info_af(tun_info) != AF_INET6))
+ unlikely(ip_tunnel_info_af(tun_info) != AF_INET6)) {
+ reason = SKB_DROP_REASON_TUNNEL_TXINFO;
goto tx_err;
+ }
key = &tun_info->key;
memset(&fl6, 0, sizeof(fl6));
@@ -993,10 +1045,14 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
dsfield = key->tos;
if (!test_bit(IP_TUNNEL_ERSPAN_OPT_BIT,
- tun_info->key.tun_flags))
+ tun_info->key.tun_flags)) {
+ reason = SKB_DROP_REASON_TUNNEL_TXINFO;
goto tx_err;
- if (tun_info->options_len < sizeof(*md))
+ }
+ if (tun_info->options_len < sizeof(*md)) {
+ reason = SKB_DROP_REASON_TUNNEL_TXINFO;
goto tx_err;
+ }
md = ip_tunnel_info_opts(tun_info);
tun_id = tunnel_id_to_key32(key->tun_id);
@@ -1014,6 +1070,7 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
truncate, false);
proto = htons(ETH_P_ERSPAN2);
} else {
+ reason = SKB_DROP_REASON_UNHANDLED_PROTO;
goto tx_err;
}
} else {
@@ -1024,11 +1081,16 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
&dsfield, &encap_limit);
break;
case htons(ETH_P_IPV6):
- if (ipv6_addr_equal(&t->parms.raddr, &ipv6_hdr(skb)->saddr))
+ if (ipv6_addr_equal(&t->parms.raddr,
+ &ipv6_hdr(skb)->saddr)) {
+ reason = SKB_DROP_REASON_RECURSION_LIMIT;
goto tx_err;
+ }
if (prepare_ip6gre_xmit_ipv6(skb, dev, &fl6,
- &dsfield, &encap_limit))
+ &dsfield, &encap_limit)) {
+ reason = SKB_DROP_REASON_IPV6_BAD_EXTHDR;
goto tx_err;
+ }
break;
default:
memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6));
@@ -1047,6 +1109,7 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
truncate, false);
proto = htons(ETH_P_ERSPAN2);
} else {
+ reason = SKB_DROP_REASON_UNHANDLED_PROTO;
goto tx_err;
}
@@ -1065,7 +1128,7 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
dst->ops->update_pmtu(dst, NULL, skb, mtu, false);
}
err = ip6_tnl_xmit(skb, dev, dsfield, &fl6, encap_limit, &mtu,
- NEXTHDR_GRE);
+ NEXTHDR_GRE, &reason);
if (err != 0) {
/* XXX: send ICMP error even if DF is not set. */
if (err == -EMSGSIZE) {
@@ -1084,7 +1147,7 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
if (!IS_ERR(tun_info))
DEV_STATS_INC(dev, tx_errors);
DEV_STATS_INC(dev, tx_dropped);
- kfree_skb(skb);
+ kfree_skb_reason(skb, reason);
return NETDEV_TX_OK;
}
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index d5ff50a2ac017..138151ed5797e 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -813,6 +813,7 @@ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb,
struct sk_buff *skb),
bool log_ecn_err)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
const struct ipv6hdr *ipv6h;
int nh, err;
@@ -820,15 +821,22 @@ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb,
test_bit(IP_TUNNEL_CSUM_BIT, tpi->flags)) {
DEV_STATS_INC(tunnel->dev, rx_crc_errors);
DEV_STATS_INC(tunnel->dev, rx_errors);
+ reason = SKB_DROP_REASON_TNL_OPT_MISMATCH;
goto drop;
}
if (test_bit(IP_TUNNEL_SEQ_BIT, tunnel->parms.i_flags)) {
- if (!test_bit(IP_TUNNEL_SEQ_BIT, tpi->flags) ||
- (tunnel->i_seqno &&
- (s32)(ntohl(tpi->seq) - tunnel->i_seqno) < 0)) {
+ if (!test_bit(IP_TUNNEL_SEQ_BIT, tpi->flags)) {
DEV_STATS_INC(tunnel->dev, rx_fifo_errors);
DEV_STATS_INC(tunnel->dev, rx_errors);
+ reason = SKB_DROP_REASON_TNL_OPT_MISMATCH;
+ goto drop;
+ }
+ if (tunnel->i_seqno &&
+ (s32)(ntohl(tpi->seq) - tunnel->i_seqno) < 0) {
+ DEV_STATS_INC(tunnel->dev, rx_fifo_errors);
+ DEV_STATS_INC(tunnel->dev, rx_errors);
+ reason = SKB_DROP_REASON_TNL_OLD_SEQ;
goto drop;
}
tunnel->i_seqno = ntohl(tpi->seq) + 1;
@@ -838,7 +846,8 @@ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb,
/* Warning: All skb pointers will be invalidated! */
if (tunnel->dev->type == ARPHRD_ETHER) {
- if (!pskb_may_pull(skb, ETH_HLEN)) {
+ reason = pskb_may_pull_reason(skb, ETH_HLEN);
+ if (reason) {
DEV_STATS_INC(tunnel->dev, rx_length_errors);
DEV_STATS_INC(tunnel->dev, rx_errors);
goto drop;
@@ -859,7 +868,8 @@ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb,
skb_reset_network_header(skb);
- if (skb_vlan_inet_prepare(skb, true)) {
+ reason = skb_vlan_inet_prepare(skb, true);
+ if (reason) {
DEV_STATS_INC(tunnel->dev, rx_length_errors);
DEV_STATS_INC(tunnel->dev, rx_errors);
goto drop;
@@ -881,6 +891,7 @@ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb,
if (err > 1) {
DEV_STATS_INC(tunnel->dev, rx_frame_errors);
DEV_STATS_INC(tunnel->dev, rx_errors);
+ reason = SKB_DROP_REASON_IP_TUNNEL_ECN;
goto drop;
}
}
@@ -898,7 +909,7 @@ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb,
drop:
if (tun_dst)
dst_release((struct dst_entry *)tun_dst);
- kfree_skb(skb);
+ kfree_skb_reason(skb, reason);
return 0;
}
@@ -1086,6 +1097,7 @@ EXPORT_SYMBOL_GPL(ip6_tnl_xmit_ctl);
* @encap_limit: encapsulation limit
* @pmtu: Path MTU is stored if packet is too big
* @proto: next header value
+ * @reason: drop reason, only written when the packet is dropped
*
* Description:
* Build new header and do some sanity checks on the packet before sending
@@ -1099,7 +1111,7 @@ EXPORT_SYMBOL_GPL(ip6_tnl_xmit_ctl);
int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,
struct flowi6 *fl6, int encap_limit, __u32 *pmtu,
- __u8 proto)
+ __u8 proto, enum skb_drop_reason *reason)
{
struct ip6_tnl *t = netdev_priv(dev);
struct net *net = t->net;
@@ -1132,13 +1144,17 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,
struct neighbour *neigh;
int addr_type;
- if (!skb_dst(skb))
+ if (!skb_dst(skb)) {
+ *reason = SKB_DROP_REASON_NO_TX_TARGET;
goto tx_err_link_failure;
+ }
neigh = dst_neigh_lookup(skb_dst(skb),
&ipv6_hdr(skb)->daddr);
- if (!neigh)
+ if (!neigh) {
+ *reason = SKB_DROP_REASON_NEIGH_CREATEFAIL;
goto tx_err_link_failure;
+ }
addr6 = (struct in6_addr *)&neigh->primary_key;
addr_type = ipv6_addr_type(addr6);
@@ -1151,8 +1167,10 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,
} else if (payload_protocol == htons(ETH_P_IP)) {
const struct rtable *rt = skb_rtable(skb);
- if (!rt)
+ if (!rt) {
+ *reason = SKB_DROP_REASON_NO_TX_TARGET;
goto tx_err_link_failure;
+ }
if (rt->rt_gw_family == AF_INET6)
memcpy(&fl6->daddr, &rt->rt_gw6, sizeof(fl6->daddr));
@@ -1169,8 +1187,10 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,
if (use_cache)
dst = dst_cache_get(&t->dst_cache);
- if (!ip6_tnl_xmit_ctl(t, &fl6->saddr, &fl6->daddr))
+ if (!ip6_tnl_xmit_ctl(t, &fl6->saddr, &fl6->daddr)) {
+ *reason = SKB_DROP_REASON_DEV_READY;
goto tx_err_link_failure;
+ }
if (!dst) {
route_lookup:
@@ -1179,18 +1199,23 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,
dst = ip6_route_output(net, NULL, fl6);
- if (dst->error)
+ if (dst->error) {
+ *reason = SKB_DROP_REASON_IP_OUTNOROUTES;
goto tx_err_link_failure;
+ }
dst = xfrm_lookup(net, dst, flowi6_to_flowi(fl6), NULL, 0);
if (IS_ERR(dst)) {
err = PTR_ERR(dst);
dst = NULL;
+ *reason = SKB_DROP_REASON_IP_OUTNOROUTES;
goto tx_err_link_failure;
}
if (t->parms.collect_md && ipv6_addr_any(&fl6->saddr) &&
ipv6_dev_get_saddr(net, ip6_dst_idev(dst)->dev,
- &fl6->daddr, 0, &fl6->saddr))
+ &fl6->daddr, 0, &fl6->saddr)) {
+ *reason = SKB_DROP_REASON_IP_OUTNOROUTES;
goto tx_err_link_failure;
+ }
ndst = dst;
}
@@ -1200,6 +1225,7 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,
DEV_STATS_INC(dev, collisions);
net_warn_ratelimited("%s: Local routing loop detected!\n",
t->parms.name);
+ *reason = SKB_DROP_REASON_RECURSION_LIMIT;
goto tx_err_dst_release;
}
mtu = dst6_mtu(dst) - eth_hlen - psh_hlen - t->tun_hlen;
@@ -1214,6 +1240,7 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,
if (skb->len - t->tun_hlen - eth_hlen > mtu && !skb_is_gso(skb)) {
*pmtu = mtu;
err = -EMSGSIZE;
+ *reason = SKB_DROP_REASON_PKT_TOO_BIG;
goto tx_err_dst_release;
}
@@ -1236,12 +1263,16 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,
*/
max_headroom += LL_RESERVED_SPACE(tdev);
- if (skb_cow_head(skb, max_headroom))
+ if (skb_cow_head(skb, max_headroom)) {
+ *reason = SKB_DROP_REASON_NOMEM;
goto tx_err_dst_release;
+ }
if (t->parms.collect_md) {
- if (t->encap.type != TUNNEL_ENCAP_NONE)
+ if (t->encap.type != TUNNEL_ENCAP_NONE) {
+ *reason = SKB_DROP_REASON_TNL_ENCAP;
goto tx_err_dst_release;
+ }
} else {
if (use_cache && ndst)
dst_cache_set_ip6(&t->dst_cache, ndst, &fl6->saddr);
@@ -1265,8 +1296,10 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,
ip_tunnel_adj_headroom(dev, max_headroom);
err = ip6_tnl_encap(skb, t, &proto, fl6);
- if (err)
+ if (err) {
+ *reason = SKB_DROP_REASON_TNL_ENCAP;
return err;
+ }
if (encap_limit >= 0) {
init_tel_txopt(&opt, encap_limit);
@@ -1295,7 +1328,7 @@ EXPORT_SYMBOL(ip6_tnl_xmit);
static inline int
ipxip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev,
- u8 protocol)
+ u8 protocol, enum skb_drop_reason *reason)
{
struct ip6_tnl *t = netdev_priv(dev);
struct ipv6hdr *ipv6h;
@@ -1309,8 +1342,10 @@ ipxip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev,
int err;
tproto = READ_ONCE(t->parms.proto);
- if (tproto != protocol && tproto != 0)
+ if (tproto != protocol && tproto != 0) {
+ *reason = SKB_DROP_REASON_UNHANDLED_PROTO;
return -1;
+ }
if (t->parms.collect_md) {
struct ip_tunnel_info *tun_info;
@@ -1318,8 +1353,10 @@ ipxip6_tnl_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_INET6))
+ ip_tunnel_info_af(tun_info) != AF_INET6)) {
+ *reason = SKB_DROP_REASON_TUNNEL_TXINFO;
return -1;
+ }
key = &tun_info->key;
memset(&fl6, 0, sizeof(fl6));
fl6.flowi6_proto = protocol;
@@ -1356,6 +1393,7 @@ ipxip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev,
if (tel->encap_limit == 0) {
icmpv6_ndo_send(skb, ICMPV6_PARAMPROB,
ICMPV6_HDR_FIELD, offset + 2);
+ *reason = SKB_DROP_REASON_IPV6_BAD_EXTHDR;
return -1;
}
encap_limit = tel->encap_limit - 1;
@@ -1397,13 +1435,15 @@ ipxip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev,
fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL);
dsfield = INET_ECN_encapsulate(dsfield, orig_dsfield);
- if (iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6))
+ if (iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6)) {
+ *reason = SKB_DROP_REASON_NOMEM;
return -1;
+ }
skb_set_inner_ipproto(skb, protocol);
err = ip6_tnl_xmit(skb, dev, dsfield, &fl6, encap_limit, &mtu,
- protocol);
+ protocol, reason);
if (err != 0) {
/* XXX: send ICMP error even if DF is not set. */
if (err == -EMSGSIZE)
@@ -1427,11 +1467,13 @@ ipxip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev,
static netdev_tx_t
ip6_tnl_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct ip6_tnl *t = netdev_priv(dev);
u8 ipproto;
int ret;
- if (!pskb_inet_may_pull(skb))
+ reason = pskb_inet_may_pull_reason(skb);
+ if (reason)
goto tx_err;
switch (skb->protocol) {
@@ -1439,18 +1481,21 @@ ip6_tnl_start_xmit(struct sk_buff *skb, struct net_device *dev)
ipproto = IPPROTO_IPIP;
break;
case htons(ETH_P_IPV6):
- if (ip6_tnl_addr_conflict(t, ipv6_hdr(skb)))
+ if (ip6_tnl_addr_conflict(t, ipv6_hdr(skb))) {
+ reason = SKB_DROP_REASON_RECURSION_LIMIT;
goto tx_err;
+ }
ipproto = IPPROTO_IPV6;
break;
case htons(ETH_P_MPLS_UC):
ipproto = IPPROTO_MPLS;
break;
default:
+ reason = SKB_DROP_REASON_UNHANDLED_PROTO;
goto tx_err;
}
- ret = ipxip6_tnl_xmit(skb, dev, ipproto);
+ ret = ipxip6_tnl_xmit(skb, dev, ipproto, &reason);
if (ret < 0)
goto tx_err;
@@ -1459,7 +1504,7 @@ ip6_tnl_start_xmit(struct sk_buff *skb, struct net_device *dev)
tx_err:
DEV_STATS_INC(dev, tx_errors);
DEV_STATS_INC(dev, tx_dropped);
- kfree_skb(skb);
+ kfree_skb_reason(skb, reason);
return NETDEV_TX_OK;
}
|
| 25/3 | 2026/09/18 04:47 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 26733 output: 19 thoughts: 1878 Thoughts: **KMSAN Fuzzing Session Justification** Okay, so I'm looking at this patch series and trying to figure out if it warrants a dedicated KMSAN fuzzing session. Let's break it down: The patch adds new drop reasons to `net/dropreason-core.h` like `TNL_OPT_MISMATCH`, `TNL_OLD_SEQ`, etc. Then it changes several GRE and IP tunnel functions. Specifically, it's replacing `bool *csum_err` with `enum skb_drop_reason *reason` in `gre_parse_header` and then using `kfree_skb_reason` instead of `kfree_skb`. This means we have a bunch of functions now taking and using a `reason` variable, a very good change. So my main concern is uninitialized memory. If we're passing a `reason` variable, we *must* make sure it's initialized before use to prevent any information leaks or unexpected behavior. Let's see... `gre_parse_header` has a comment about this - "the caller has to initialise it before the call". The comment suggests a NULL check. Right, so I need to check every single place where `reason` is now being used or passed. Let's go through the functions, one by one. I'll make sure the `reason` variable is initialized *before* it's passed or used. Okay, checking them all... and it looks like in all the functions like `gre_rcv`, `gre_fb_xmit`, `ip_tunnel_rcv`, etc. the `reason` variable is initialized to `SKB_DROP_REASON_NOT_SPECIFIED`. Good. That covers the immediate changes in this patch. Wait a second, there are other callers of `gre_parse_header`, the thing which started this whole `reason` thing. Let me quickly scan the codebase for `gre_parse_header` using a grepper to get a complete picture. Gotta be thorough here! |
| 26/3 | 2026/09/18 04:47 | tool | grepper |
0mArgs:{
"Expression": "gre_parse_header"
}Results: {
"Output": "include/net/gre.h=33=int gre_del_protocol(const struct gre_protocol *proto, u8 version);\ninclude/net/gre.h-34-\ninclude/net/gre.h:35:int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,\ninclude/net/gre.h-36-\t\t enum skb_drop_reason *reason, __be16 proto, int nhs);\n--\nnet/ipv4/gre_demux.c=57=EXPORT_SYMBOL_GPL(gre_del_protocol);\n--\nnet/ipv4/gre_demux.c-70- */\nnet/ipv4/gre_demux.c:71:int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,\nnet/ipv4/gre_demux.c-72-\t\t enum skb_drop_reason *reason, __be16 proto, int nhs)\n--\nnet/ipv4/gre_demux.c-167-}\nnet/ipv4/gre_demux.c:168:EXPORT_SYMBOL(gre_parse_header);\nnet/ipv4/gre_demux.c-169-\n--\nnet/ipv4/ip_gre.c=219=static void gre_err(struct sk_buff *skb, u32 info)\n--\nnet/ipv4/ip_gre.c-239-\nnet/ipv4/ip_gre.c:240:\tif (gre_parse_header(skb, \u0026tpi, NULL, htons(ETH_P_IP),\nnet/ipv4/ip_gre.c-241-\t\t\t iph-\u003eihl * 4) \u003c 0)\n--\nnet/ipv4/ip_gre.c=451=static int gre_rcv(struct sk_buff *skb)\n--\nnet/ipv4/ip_gre.c-464-\nnet/ipv4/ip_gre.c:465:\thdr_len = gre_parse_header(skb, \u0026tpi, \u0026reason, htons(ETH_P_IP), 0);\nnet/ipv4/ip_gre.c-466-\tif (hdr_len \u003c 0)\n--\nnet/ipv6/ip6_gre.c=387=static int ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt,\n--\nnet/ipv6/ip6_gre.c-394-\nnet/ipv6/ip6_gre.c:395:\tif (gre_parse_header(skb, \u0026tpi, NULL, htons(ETH_P_IPV6),\nnet/ipv6/ip6_gre.c-396-\t\t\t offset) \u003c 0)\n--\nnet/ipv6/ip6_gre.c=584=static int gre_rcv(struct sk_buff *skb)\n--\nnet/ipv6/ip6_gre.c-589-\nnet/ipv6/ip6_gre.c:590:\thdr_len = gre_parse_header(skb, \u0026tpi, \u0026reason, htons(ETH_P_IPV6), 0);\nnet/ipv6/ip6_gre.c-591-\tif (hdr_len \u003c 0)\n"
} |
| 27/3 | 2026/09/18 04:47 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 29907 output: 23 thoughts: 18 Thoughts: Here's my summary, as though I'm thinking these thoughts: **Investigating Potential Issues with `ip6_tnl_xmit`** Okay, so we've been looking at this area, and I think it's prudent to broaden the scope a bit. We've focused on X, Y, and Z, but it feels like we might be missing something. To get a truly comprehensive understanding, I need to check the callers of `ip6_tnl_xmit` as well. That function is a critical point, and by tracing its usage, we'll gain more insight into *why* it's called, *when* it's called, and *under what conditions* it's called. This approach, looking at the call sites, is essential for identifying potential bottlenecks or unexpected behavior that stems from the way this function is used within the wider system. I need to understand the call context to really grasp the potential implications. Let's dig in and see what those callers are doing. |
| 28/3 | 2026/09/18 04:47 | tool | grepper |
0mArgs:{
"Expression": "ip6_tnl_xmit\\("
}Results: {
"Output": "include/net/ip6_tunnel.h=143=int ip6_tnl_xmit_ctl(struct ip6_tnl *t, const struct in6_addr *laddr,\ninclude/net/ip6_tunnel.h-144-\t\t const struct in6_addr *raddr);\ninclude/net/ip6_tunnel.h:145:int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,\ninclude/net/ip6_tunnel.h-146-\t\t struct flowi6 *fl6, int encap_limit, __u32 *pmtu, __u8 proto,\n--\nnet/ipv6/ip6_gre.c=732=static netdev_tx_t __gre6_xmit(struct sk_buff *skb,\n--\nnet/ipv6/ip6_gre.c-808-\nnet/ipv6/ip6_gre.c:809:\treturn ip6_tnl_xmit(skb, dev, dsfield, fl6, encap_limit, pmtu,\nnet/ipv6/ip6_gre.c-810-\t\t\t NEXTHDR_GRE, reason);\n--\nnet/ipv6/ip6_gre.c=961=static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,\n--\nnet/ipv6/ip6_gre.c-1129-\t}\nnet/ipv6/ip6_gre.c:1130:\terr = ip6_tnl_xmit(skb, dev, dsfield, \u0026fl6, encap_limit, \u0026mtu,\nnet/ipv6/ip6_gre.c-1131-\t\t\t NEXTHDR_GRE, \u0026reason);\n--\nnet/ipv6/ip6_tunnel.c=1089=EXPORT_SYMBOL_GPL(ip6_tnl_xmit_ctl);\n--\nnet/ipv6/ip6_tunnel.c-1111-\nnet/ipv6/ip6_tunnel.c:1112:int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,\nnet/ipv6/ip6_tunnel.c-1113-\t\t struct flowi6 *fl6, int encap_limit, __u32 *pmtu,\n--\nnet/ipv6/ip6_tunnel.c=1329=static inline int\nnet/ipv6/ip6_tunnel.c:1330:ipxip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev,\nnet/ipv6/ip6_tunnel.c-1331-\t\tu8 protocol, enum skb_drop_reason *reason)\n--\nnet/ipv6/ip6_tunnel.c-1444-\nnet/ipv6/ip6_tunnel.c:1445:\terr = ip6_tnl_xmit(skb, dev, dsfield, \u0026fl6, encap_limit, \u0026mtu,\nnet/ipv6/ip6_tunnel.c-1446-\t\t\t protocol, reason);\n--\nnet/ipv6/ip6_tunnel.c=1468=ip6_tnl_start_xmit(struct sk_buff *skb, struct net_device *dev)\n--\nnet/ipv6/ip6_tunnel.c-1497-\nnet/ipv6/ip6_tunnel.c:1498:\tret = ipxip6_tnl_xmit(skb, dev, ipproto, \u0026reason);\nnet/ipv6/ip6_tunnel.c-1499-\tif (ret \u003c 0)\n"
} |
| 29/3 | 2026/09/18 04:47 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 31100 output: 22 thoughts: 891 Thoughts: **Analyzing SKB Drop Reasons in GRE Tunneling** Okay, so I've updated all the callers of `ip6_tnl_xmit` and `gre_parse_header`, and now it's time to examine the receive paths for GRE tunnels. I need to make sure I've accounted for all the nuances with the `reason` parameter used for `kfree_skb_reason`. Specifically, I want to confirm that there are no uninitialized reads of the `reason` variable, which can lead to unexpected behavior or information leakage. I'm starting by looking at the functions: `ipgre_rcv`, `erspan_rcv`, `ip6gre_rcv`, and `ip6erspan_rcv`. They all share a common pattern in how they're called: they are all called by `gre_rcv` with a pointer to `reason`. Within `gre_rcv`, an `enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;` is declared and initialized. The `reason` variable, or pointer to it, is then passed down the stack to the inner `..._rcv` functions. My inspection reveals the key: if an error occurs, the `reason` variable is always set either directly (`reason = ...`) or through a dereferenced pointer (`*reason = ...`). The exception to this is `gre_err` and `ip6gre_err`, which pass `NULL` as the `reason` parameter. This is safe because `gre_parse_header` checks for a `NULL` `reason` before dereferencing it. Next, I need to ensure there are no other places where `reason` is potentially read uninitialized. In `gre_demux.c`, `gre_rcv` is another location where `reason` is present. It also initializes `reason` to `SKB_DROP_REASON_NOT_SPECIFIED`, then assigns a value from `pskb_may_pull_reason(skb, 12)`. If `pskb_may_pull_reason` returns a non-zero value, it's immediately used in a call to `kfree_skb_reason`. Since `reason` is initialized before use, this appears safe. I'll need to check the definition of `pskb_may_pull_reason` to be 100% sure, but at this point, it looks good. I will use grepper to search for its definition. |
| 30/3 | 2026/09/18 04:47 | tool | grepper |
0mArgs:{
"Expression": "pskb_may_pull_reason"
}Results: {
"Output": "drivers/net/vxlan/vxlan_core.c=1548=static enum skb_drop_reason vxlan_remcsum(struct sk_buff *skb, u32 vxflags)\n--\ndrivers/net/vxlan/vxlan_core.c-1559-\ndrivers/net/vxlan/vxlan_core.c:1560:\treason = pskb_may_pull_reason(skb, offset + sizeof(u16));\ndrivers/net/vxlan/vxlan_core.c-1561-\tif (reason)\n--\ndrivers/net/vxlan/vxlan_core.c=1657=static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)\n--\ndrivers/net/vxlan/vxlan_core.c-1672-\t/* Need UDP and VXLAN header to be present */\ndrivers/net/vxlan/vxlan_core.c:1673:\treason = pskb_may_pull_reason(skb, VXLAN_HLEN);\ndrivers/net/vxlan/vxlan_core.c-1674-\tif (reason)\n--\ninclude/linux/skbuff.h=2861=static __always_inline enum skb_drop_reason\ninclude/linux/skbuff.h:2862:pskb_may_pull_reason(struct sk_buff *skb, unsigned int len)\ninclude/linux/skbuff.h-2863-{\n--\ninclude/linux/skbuff.h=2880=pskb_may_pull(struct sk_buff *skb, unsigned int len)\ninclude/linux/skbuff.h-2881-{\ninclude/linux/skbuff.h:2882:\treturn pskb_may_pull_reason(skb, len) == SKB_NOT_DROPPED_YET;\ninclude/linux/skbuff.h-2883-}\n--\ninclude/linux/skbuff.h=3292=pskb_network_may_pull_reason(struct sk_buff *skb, unsigned int len)\ninclude/linux/skbuff.h-3293-{\ninclude/linux/skbuff.h:3294:\treturn pskb_may_pull_reason(skb, skb_network_offset(skb) + len);\ninclude/linux/skbuff.h-3295-}\n--\ninclude/net/ip_tunnels.h=494=skb_vlan_inet_prepare(struct sk_buff *skb, bool inner_proto_inherit)\n--\ninclude/net/ip_tunnels.h-518-\t */\ninclude/net/ip_tunnels.h:519:\treason = pskb_may_pull_reason(skb, maclen + nhlen);\ninclude/net/ip_tunnels.h-520-\tif (reason)\n--\nnet/bridge/br_device.c=30=netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)\nnet/bridge/br_device.c-31-{\nnet/bridge/br_device.c:32:\tenum skb_drop_reason reason = pskb_may_pull_reason(skb, ETH_HLEN);\nnet/bridge/br_device.c-33-\tstruct net_bridge_mcast_port *pmctx_null = NULL;\n--\nnet/core/filter.c=1681=static inline int __bpf_try_make_writable(struct sk_buff *skb,\n--\nnet/core/filter.c-1684-#ifdef CONFIG_DEBUG_NET\nnet/core/filter.c:1685:\t/* Avoid a splat in pskb_may_pull_reason() */\nnet/core/filter.c-1686-\tif (write_len \u003e INT_MAX)\n--\nnet/ipv4/arp.c=970=static int arp_rcv(struct sk_buff *skb, struct net_device *dev,\n--\nnet/ipv4/arp.c-986-\t/* ARP header, plus 2 device addresses, plus 2 IP addresses. */\nnet/ipv4/arp.c:987:\tdrop_reason = pskb_may_pull_reason(skb, arp_hdr_len(dev));\nnet/ipv4/arp.c-988-\tif (drop_reason != SKB_NOT_DROPPED_YET)\n--\nnet/ipv4/gre_demux.c=170=static int gre_rcv(struct sk_buff *skb)\n--\nnet/ipv4/gre_demux.c-176-\nnet/ipv4/gre_demux.c:177:\treason = pskb_may_pull_reason(skb, 12);\nnet/ipv4/gre_demux.c-178-\tif (reason)\n--\nnet/ipv4/ip_gre.c=681=static netdev_tx_t ipgre_xmit(struct sk_buff *skb,\n--\nnet/ipv4/ip_gre.c-705-\nnet/ipv4/ip_gre.c:706:\t\treason = pskb_may_pull_reason(skb, pull_len);\nnet/ipv4/ip_gre.c-707-\t\tif (reason)\n--\nnet/ipv6/icmp.c=1037=enum skb_drop_reason icmpv6_notify(struct sk_buff *skb, u8 type,\n--\nnet/ipv6/icmp.c-1047-\nnet/ipv6/icmp.c:1048:\treason = pskb_may_pull_reason(skb, sizeof(struct ipv6hdr));\nnet/ipv6/icmp.c-1049-\tif (reason != SKB_NOT_DROPPED_YET)\n--\nnet/ipv6/icmp.c-1067-\t/* Checkin header including 8 bytes of inner protocol header. */\nnet/ipv6/icmp.c:1068:\treason = pskb_may_pull_reason(skb, inner_offset + 8);\nnet/ipv6/icmp.c-1069-\tif (reason != SKB_NOT_DROPPED_YET)\n--\nnet/ipv6/ip6_tunnel.c=808=static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb,\n--\nnet/ipv6/ip6_tunnel.c-848-\tif (tunnel-\u003edev-\u003etype == ARPHRD_ETHER) {\nnet/ipv6/ip6_tunnel.c:849:\t\treason = pskb_may_pull_reason(skb, ETH_HLEN);\nnet/ipv6/ip6_tunnel.c-850-\t\tif (reason) {\n"
} |
| 31/3 | 2026/09/18 04:47 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 34273 output: 37 thoughts: 103 Thoughts: **Analyzing Packet Buffer Pulling** Okay, so I'm looking at this `pskb_may_pull_reason` function, and it seems pretty straightforward on the surface, returning either a success code of `0` (which is `SKB_NOT_DROPPED_YET`) or a specific drop reason if something goes wrong. This is the classic, "return a zero for success, negative for error" pattern you see everywhere in kernel programming. I need to understand the failure modes though, and the underlying logic, so I should really dig into its implementation. I'll need to look at `include/linux/skbuff.h` to see exactly what causes it to return an error, and what those various `SKB_DROP_REASON_*` codes actually *mean*. Things like "packet too small" or "no memory" are pretty obvious, but the devil's in the details, so a quick code review is in order. It's time to understand how the packet buffer pulling function can fail, and what exactly triggers these specific error conditions. |
| 32/3 | 2026/09/18 04:48 | tool | read-file |
0mArgs:{
"File": "include/linux/skbuff.h",
"FirstLine": 2860,
"LineCount": 25
}Results: {
"Contents": "2860:\t\n2861:\tstatic __always_inline enum skb_drop_reason\n2862:\tpskb_may_pull_reason(struct sk_buff *skb, unsigned int len)\n2863:\t{\n2864:\t\tDEBUG_NET_WARN_ON_ONCE(len \u003e INT_MAX);\n2865:\t\tskb_might_realloc(skb);\n2866:\t\n2867:\t\tif (likely(len \u003c= skb_headlen(skb)))\n2868:\t\t\treturn SKB_NOT_DROPPED_YET;\n2869:\t\n2870:\t\tif (unlikely(len \u003e skb-\u003elen))\n2871:\t\t\treturn SKB_DROP_REASON_PKT_TOO_SMALL;\n2872:\t\n2873:\t\tif (unlikely(!__pskb_pull_tail(skb, len - skb_headlen(skb))))\n2874:\t\t\treturn SKB_DROP_REASON_NOMEM;\n2875:\t\n2876:\t\treturn SKB_NOT_DROPPED_YET;\n2877:\t}\n2878:\t\n2879:\tstatic __always_inline bool\n2880:\tpskb_may_pull(struct sk_buff *skb, unsigned int len)\n2881:\t{\n2882:\t\treturn pskb_may_pull_reason(skb, len) == SKB_NOT_DROPPED_YET;\n2883:\t}\n2884:\t\n"
} |
| 33/3 | 2026/09/18 04:48 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 35054 output: 24 thoughts: 58 Thoughts: **Analyzing Packet Drop Reasons** Okay, so the `SKB_NOT_DROPPED_YET` flag is currently set to zero. That's good; it means we haven't identified this packet as dropped yet. This implies that if I use `if (reason)` to check the `reason` variable, I can effectively determine if the packet has been dropped (if the `reason` variable is set to a non-zero value, then it *has* been dropped). Now, let's turn our attention to `pskb_inet_may_pull_reason` to see if it provides further clues. I should inspect this function. |
| 34/3 | 2026/09/18 04:48 | tool | grepper |
0mArgs:{
"Expression": "pskb_inet_may_pull_reason"
}Results: {
"Output": "drivers/net/vxlan/vxlan_core.c=1657=static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)\n--\ndrivers/net/vxlan/vxlan_core.c-1778-\ndrivers/net/vxlan/vxlan_core.c:1779:\treason = pskb_inet_may_pull_reason(skb);\ndrivers/net/vxlan/vxlan_core.c-1780-\tif (reason) {\n--\ninclude/net/ip_tunnels.h=465=static inline enum skb_drop_reason\ninclude/net/ip_tunnels.h:466:pskb_inet_may_pull_reason(struct sk_buff *skb)\ninclude/net/ip_tunnels.h-467-{\n--\ninclude/net/ip_tunnels.h=486=static inline bool pskb_inet_may_pull(struct sk_buff *skb)\ninclude/net/ip_tunnels.h-487-{\ninclude/net/ip_tunnels.h:488:\treturn pskb_inet_may_pull_reason(skb) == SKB_NOT_DROPPED_YET;\ninclude/net/ip_tunnels.h-489-}\n--\nnet/ipv4/ip_gre.c=681=static netdev_tx_t ipgre_xmit(struct sk_buff *skb,\n--\nnet/ipv4/ip_gre.c-688-\nnet/ipv4/ip_gre.c:689:\treason = pskb_inet_may_pull_reason(skb);\nnet/ipv4/ip_gre.c-690-\tif (reason)\n--\nnet/ipv4/ip_gre.c=746=static netdev_tx_t erspan_xmit(struct sk_buff *skb,\n--\nnet/ipv4/ip_gre.c-754-\nnet/ipv4/ip_gre.c:755:\treason = pskb_inet_may_pull_reason(skb);\nnet/ipv4/ip_gre.c-756-\tif (reason)\n--\nnet/ipv4/ip_gre.c=813=static netdev_tx_t gre_tap_xmit(struct sk_buff *skb,\n--\nnet/ipv4/ip_gre.c-819-\nnet/ipv4/ip_gre.c:820:\treason = pskb_inet_may_pull_reason(skb);\nnet/ipv4/ip_gre.c-821-\tif (reason)\n--\nnet/ipv4/ip_tunnel.c=383=int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb,\n--\nnet/ipv4/ip_tunnel.c-430-\nnet/ipv4/ip_tunnel.c:431:\treason = pskb_inet_may_pull_reason(skb);\nnet/ipv4/ip_tunnel.c-432-\tif (reason) {\n--\nnet/ipv6/ip6_gre.c=914=static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,\n--\nnet/ipv6/ip6_gre.c-922-\nnet/ipv6/ip6_gre.c:923:\treason = pskb_inet_may_pull_reason(skb);\nnet/ipv6/ip6_gre.c-924-\tif (reason)\n--\nnet/ipv6/ip6_gre.c=961=static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,\n--\nnet/ipv6/ip6_gre.c-977-\nnet/ipv6/ip6_gre.c:978:\treason = pskb_inet_may_pull_reason(skb);\nnet/ipv6/ip6_gre.c-979-\tif (reason)\n--\nnet/ipv6/ip6_tunnel.c=1468=ip6_tnl_start_xmit(struct sk_buff *skb, struct net_device *dev)\n--\nnet/ipv6/ip6_tunnel.c-1474-\nnet/ipv6/ip6_tunnel.c:1475:\treason = pskb_inet_may_pull_reason(skb);\nnet/ipv6/ip6_tunnel.c-1476-\tif (reason)\n"
} |
| 35/3 | 2026/09/18 04:48 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 36424 output: 38 thoughts: 101 Thoughts: **My Analysis of `pskb_inet_may_pull_reason` and VLAN Preparation** Okay, so `pskb_inet_may_pull_reason` can return `SKB_NOT_DROPPED_YET` (0) or, crucially, an error code indicating the reason for a potential drop. That's good to keep in mind, because knowing *why* a packet might be dropped is obviously critical for debugging and performance tuning. Now, I see this call to `skb_vlan_inet_prepare` in `include/net/ip_tunnels.h`: ```c reason = skb_vlan_inet_prepare(skb, true); ``` Right, I need to understand what `skb_vlan_inet_prepare` is doing and what that 'true' argument implies. I'm going to jump straight into reading the definition of `skb_vlan_inet_prepare`. I need to fully grok how VLAN tagging and encapsulation are being handled in this context, and how it impacts the packet's processing. Specifically I want to check how the return value might be related to the `pskb_inet_may_pull_reason` logic. |
| 36/3 | 2026/09/18 04:48 | tool | read-file |
0mArgs:{
"File": "include/net/ip_tunnels.h",
"FirstLine": 490,
"LineCount": 40
}Results: {
"Contents": " 490:\t\n 491:\t/* Variant of pskb_inet_may_pull().\n 492:\t */\n 493:\tstatic inline enum skb_drop_reason\n 494:\tskb_vlan_inet_prepare(struct sk_buff *skb, bool inner_proto_inherit)\n 495:\t{\n 496:\t\tint nhlen = 0, maclen = inner_proto_inherit ? 0 : ETH_HLEN;\n 497:\t\t__be16 type = skb-\u003eprotocol;\n 498:\t\tenum skb_drop_reason reason;\n 499:\t\n 500:\t\t/* Essentially this is skb_protocol(skb, true)\n 501:\t\t * And we get MAC len.\n 502:\t\t */\n 503:\t\tif (eth_type_vlan(type))\n 504:\t\t\ttype = __vlan_get_protocol(skb, type, \u0026maclen);\n 505:\t\n 506:\t\tswitch (type) {\n 507:\t#if IS_ENABLED(CONFIG_IPV6)\n 508:\t\tcase htons(ETH_P_IPV6):\n 509:\t\t\tnhlen = sizeof(struct ipv6hdr);\n 510:\t\t\tbreak;\n 511:\t#endif\n 512:\t\tcase htons(ETH_P_IP):\n 513:\t\t\tnhlen = sizeof(struct iphdr);\n 514:\t\t\tbreak;\n 515:\t\t}\n 516:\t\t/* For ETH_P_IPV6/ETH_P_IP we make sure to pull\n 517:\t\t * a base network header in skb-\u003ehead.\n 518:\t\t */\n 519:\t\treason = pskb_may_pull_reason(skb, maclen + nhlen);\n 520:\t\tif (reason)\n 521:\t\t\treturn reason;\n 522:\t\n 523:\t\tskb_set_network_header(skb, maclen);\n 524:\t\n 525:\t\treturn SKB_NOT_DROPPED_YET;\n 526:\t}\n 527:\t\n 528:\tstatic inline int ip_encap_hlen(struct ip_tunnel_encap *e)\n 529:\t{\n"
} |
| 37/3 | 2026/09/18 04:48 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 37431 output: 517 thoughts: 3228 Thoughts: **Analysis of GRE and IP Tunnel SKB Drop Reason Instrumentation Patch** Alright, let's break this down. My goal is to ensure this patch is safe, especially concerning uninitialized memory accesses and potential information leaks, and whether a KMSAN tag is needed. The core of this patch is straightforward: it introduces a more granular way to track why SKBs (socket buffer) are dropped in GRE and IP/IPv6 tunnel code. It adds `enum skb_drop_reason` and replaces calls to `kfree_skb()` with `kfree_skb_reason()`, along with adding error checks where this information is relevant. First, I examined all the code changes systematically. The general pattern is that a local `enum skb_drop_reason reason` variable is declared on the stack, *always* initialized to `SKB_DROP_REASON_NOT_SPECIFIED`. This is crucial. Then, throughout the function, if an error condition is met, the `reason` variable is set to a specific `SKB_DROP_REASON_*` value, and finally, `kfree_skb_reason(skb, reason)` is called. This ensures that there is no possibility of reading an uninitialized variable when passing `reason`. I inspected the changes in `net/ipv4/ip_tunnel.c`. The introduction of `reason` in `ip_tunnel_rcv` is exemplary. Errors like CRC errors, sequence number mismatches, and length errors set the `reason` before calling `kfree_skb_reason()`. Also in `ip_md_tunnel_xmit` and `ip_tunnel_xmit` this is also true. I also checked `net/ipv6/ip6_gre.c` and `net/ipv6/ip6_tunnel.c`. In `ip6_tnl_xmit`, the `reason` variable is an explicit argument. I traced all its callers to ensure that they properly initialize or handle the `reason` variable and I did. All callers pass a properly initialized value. The patch also refactors some checks, replacing `!pskb_may_pull()` with `pskb_may_pull_reason()` and similar changes. While these are semantically identical, the `reason` variant is directly used which helps with readability. These changes do *not* introduce any new buffer access vulnerabilities, nor do they rely on uninitialized values. The existing bounds checking and pull operations are effectively equivalent, just now potentially providing an extra benefit. This patch *does not* introduce any of the risky scenarios that KMSAN is designed to detect. I can confirm this because: 1. **No New Data Structures/Info Leaks:** No new data structures are introduced that might have uninitialized fields or padding. There's no copying of kernel data to user space that could expose uninitialized memory. The only user-exposed element is the `skb_drop_reason` tracepoint, which receives a *scalar* `enum` already initialized or set to a concrete value. 2. **No Uninitialized Reads**: Every single `reason` variable is initialized *before* any use. There is no conditional logic that depends on potentially uninitialized variables. 3. **No Altered Bounds or Length Logic**: The bounds/length checks have been rewritten, but with semantically-equivalent code. Existing buffer operations have not had their safety conditions relaxed. Therefore, because this patch strictly adds instrumentation *without* introducing any possibility of uninitialized memory reads or information leaks, `NeedsKMSAN = false`. Any pointer errors or buffer overruns in the modified code *would* be caught by the existing KASAN and standard kernel debug facilities. We're adding a new debug ability, but we're not touching any memory in ways that could introduce the bugs that KMSAN's dedicated fuzzing would find. This patch looks good. |
| 38/3 | 2026/09/18 04:48 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch series updates the GRE and IPv4/IPv6 tunnel receive and transmit paths to record detailed sk_buff drop reasons by converting kfree_skb() invocations to kfree_skb_reason() and propagating `enum skb_drop_reason` pointers.\n\nAnalysis of uninitialized memory risks (KMSAN relevance):\n1. Scalar enum initialization: In all modified functions (`gre_rcv`, `ipgre_xmit`, `erspan_xmit`, `gre_tap_xmit`, `ip_tunnel_rcv`, `ip_md_tunnel_xmit`, `ip_tunnel_xmit`, `ip6gre_tunnel_xmit`, `ip6erspan_tunnel_xmit`, `__ip6_tnl_rcv`, `ip6_tnl_start_xmit`), local `reason` variables are explicitly initialized to `SKB_DROP_REASON_NOT_SPECIFIED` before any use or passing by reference.\n2. Pointer parameters: In functions taking `enum skb_drop_reason *reason` (such as `gre_parse_header`, `erspan_rcv`, `ipgre_rcv`, `ip6gre_rcv`, `ip6erspan_rcv`, `__gre6_xmit`, `ip6_tnl_xmit`), nullness is checked when necessary, and callers always provide pointers to previously initialized storage.\n3. Drop reason query functions: Calls converted to `pskb_may_pull_reason()`, `pskb_inet_may_pull_reason()`, and `skb_vlan_inet_prepare()` preserve identical buffer length and validity checks as the previous boolean wrappers, returning fully initialized enum values with no change to buffer parsing bounds.\n4. No kernel-to-user info-leaks: No uninitialized stack/heap structures or padding are exposed to user space via copy_to_user, netlink, or socket options.\n\nContrast with KASAN:\nAny potential errors in skb manipulation, buffer indexing, or reference counts in these networking paths are memory corruption or out-of-bounds access issues detectable by KASAN and DEBUG_NET. No uninitialized memory reads or information leaks are created, so a dedicated KMSAN fuzzing run is not warranted.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch series updates the GRE and IPv4/IPv6 tunnel receive and transmit paths to record detailed sk_buff drop reasons by converting kfree_skb() invocations to kfree_skb_reason() and propagating `enum skb_drop_reason` pointers.\n\nAnalysis of uninitialized memory risks (KMSAN relevance):\n1. Scalar enum initialization: In all modified functions (`gre_rcv`, `ipgre_xmit`, `erspan_xmit`, `gre_tap_xmit`, `ip_tunnel_rcv`, `ip_md_tunnel_xmit`, `ip_tunnel_xmit`, `ip6gre_tunnel_xmit`, `ip6erspan_tunnel_xmit`, `__ip6_tnl_rcv`, `ip6_tnl_start_xmit`), local `reason` variables are explicitly initialized to `SKB_DROP_REASON_NOT_SPECIFIED` before any use or passing by reference.\n2. Pointer parameters: In functions taking `enum skb_drop_reason *reason` (such as `gre_parse_header`, `erspan_rcv`, `ipgre_rcv`, `ip6gre_rcv`, `ip6erspan_rcv`, `__gre6_xmit`, `ip6_tnl_xmit`), nullness is checked when necessary, and callers always provide pointers to previously initialized storage.\n3. Drop reason query functions: Calls converted to `pskb_may_pull_reason()`, `pskb_inet_may_pull_reason()`, and `skb_vlan_inet_prepare()` preserve identical buffer length and validity checks as the previous boolean wrappers, returning fully initialized enum values with no change to buffer parsing bounds.\n4. No kernel-to-user info-leaks: No uninitialized stack/heap structures or padding are exposed to user space via copy_to_user, netlink, or socket options.\n\nContrast with KASAN:\nAny potential errors in skb manipulation, buffer indexing, or reference counts in these networking paths are memory corruption or out-of-bounds access issues detectable by KASAN and DEBUG_NET. No uninitialized memory reads or information leaks are created, so a dedicated KMSAN fuzzing run is not warranted.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|