| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/10/18 01:13 | flow | patch-triage |
9mArgs:null Results: {
"EnableConfigs": [
"BRIDGE_NETFILTER",
"IP_VS",
"IPV6_TUNNEL",
"NET_IPIP",
"NETFILTER_NETLINK_QUEUE",
"NF_FLOW_TABLE",
"NF_TABLES",
"NFT_COMPAT"
],
"FocusSymbols": [
"brnf_device_event",
"ip_vs_sync_conn",
"nf_flow_tunnel_push",
"nf_queue_nf_hook_drop",
"nfqnl_nf_hook_drop",
"nft_chain_parse_hook",
"nft_compat_chain_validate_dependency",
"nft_exthdr_tcp_set_eval",
"nft_exthdr_tcp_set_init",
"nft_exthdr_tcp_strip_eval"
],
"KMSANReasoning": "The patch series touches several netfilter and networking subsystems:\n1. `br_netfilter`: Disables bridge netfilter in non-init user namespaces, adding an `in_userns` flag to `struct brnf_net`. The pernet data structure is zero-allocated via standard netns allocation (`kzalloc`).\n2. `nft_exthdr`: Refactors `nft_exthdr_tcp_set_eval` and `nft_exthdr_tcp_strip_eval` to remove temporary local stack buffers (`buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE]`), inspecting the TCP header directly with `skb_header_pointer` and linearizing with `skb_ensure_writable`. No uninitialized stack memory is left or accessed.\n3. `ip_vs_sync`: Fixes a data race where concurrent modification of `cp-\u003eflags` could cause mismatch between message length calculation and copied options by snapshotting `flags` with `READ_ONCE()`.\n4. `nf_flow_table`: Adds an `encap_proto` field to `struct flow_offload_tunnel` and `struct net_device_path` to unify IPv4/IPv6 tunnel offloading. Flow offload structures are zero-initialized via `kmem_cache_zalloc`, and device paths are zero-initialized with `memset()`. The variables in `nf_flow_tunnel_ip_push` and `nf_flow_tunnel_ip6_push` are fully initialized before use.\n5. `nf_tables`, `nf_queue`, `nf_conntrack`: Implements RCU lookups and correct module refcounting for chain types, selective queue entry flushing in `nfqnl_nf_hook_drop`, and flushes conntrack GC work during netns teardown to prevent use-after-free.\n\nNone of the changes introduce uninitialized memory reads, info-leaks across the kernel-user boundary, or conditional branches on uninitialized variables. Any risks in these changes relate to object lifetimes, concurrency, or out-of-bounds access, which are already covered by KASAN, KCSAN, and LOCKDEP in standard test configurations. Spawning a dedicated KMSAN fuzzing session is not warranted.",
"NeedsKMSAN": false,
"Reasoning": "The patch series touches several reachable networking and netfilter subsystems, including nf_tables (chain type reference counting and lookup, TCP exthdr validation and initialization), nfnetlink queue (selective hook dropping upon hook unregistration), bridge netfilter (disabling in non-init user namespaces), IPVS (data race fixes in connection sync), and flowtable (tunnel push/pop refactoring and protocol encapsulation for IPIP and IPv6 tunnels). All modified code paths are fully reachable via netlink, network namespaces, and network traffic in standard virtualized environments.",
"WorthFuzzing": true
} |
| 1/1 | 2026/10/18 01:13 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 07d7ac72f49783118181ec85a3e13f4dc7c70a24\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Sun Oct 18 01:13:21 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/Documentation/networking/bridge.rst b/Documentation/networking/bridge.rst\nindex c1e6ea52c9e59..dded1a6edd606 100644\n--- a/Documentation/networking/bridge.rst\n+++ b/Documentation/networking/bridge.rst\n@@ -277,8 +277,9 @@ Netfilter\n =========\n \n The bridge netfilter module is a legacy feature that allows to filter bridged\n-packets with iptables and ip6tables. Its use is discouraged. Users should\n-consider using nftables for packet filtering.\n+packets with iptables and ip6tables. This feature is scheduled for removal and\n+not available in user namespaces. Consider using nftables for packet filtering\n+instead.\n \n The older ebtables tool is more feature-limited compared to nftables, but\n just like nftables it doesn't need this module either to function.\ndiff --git a/include/linux/netdevice.h b/include/linux/netdevice.h\nindex f61c972313095..fee3728ad6663 100644\n--- a/include/linux/netdevice.h\n+++ b/include/linux/netdevice.h\n@@ -911,6 +911,7 @@ struct net_device_path {\n \t\t\t};\n \n \t\t\tu8\tinner_proto;\n+\t\t\tu8\tencap_proto;\n \t\t} tun;\n \t\tstruct {\n \t\t\tenum {\ndiff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h\nindex 5b611efaa3cd1..5b22d31cb1407 100644\n--- a/include/net/netfilter/nf_flow_table.h\n+++ b/include/net/netfilter/nf_flow_table.h\n@@ -118,6 +118,7 @@ struct flow_offload_tunnel {\n \t};\n \n \tu8\tinner_proto;\n+\tu8\tencap_proto;\n };\n \n struct flow_offload_tuple {\ndiff --git a/include/net/netfilter/nf_queue.h b/include/net/netfilter/nf_queue.h\nindex fc3e81c07364f..86021c55ba3ce 100644\n--- a/include/net/netfilter/nf_queue.h\n+++ b/include/net/netfilter/nf_queue.h\n@@ -35,7 +35,8 @@ struct nf_queue_entry {\n struct nf_queue_handler {\n \tint\t\t(*outfn)(struct nf_queue_entry *entry,\n \t\t\t\t unsigned int queuenum);\n-\tvoid\t\t(*nf_hook_drop)(struct net *net);\n+\tvoid\t\t(*nf_hook_drop)(struct net *net,\n+\t\t\t\t\tconst struct nf_hook_ops *ops);\n };\n \n void nf_register_queue_handler(const struct nf_queue_handler *qh);\ndiff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c\nindex 0a394e5f43916..e15355ff215a7 100644\n--- a/net/bridge/br_netfilter_hooks.c\n+++ b/net/bridge/br_netfilter_hooks.c\n@@ -54,6 +54,7 @@ static unsigned int brnf_net_id __read_mostly;\n \n struct brnf_net {\n \tbool enabled;\n+\tbool in_userns;\n \n #ifdef CONFIG_SYSCTL\n \tstruct ctl_table_header *ctl_hdr;\n@@ -1100,7 +1101,7 @@ static int brnf_device_event(struct notifier_block *unused, unsigned long event,\n \n \tnet = dev_net(dev);\n \tbrnet = net_generic(net, brnf_net_id);\n-\tif (brnet-\u003eenabled)\n+\tif (brnet-\u003eenabled || brnet-\u003ein_userns)\n \t\treturn NOTIFY_OK;\n \n \tret = nf_register_net_hooks(net, br_nf_ops, ARRAY_SIZE(br_nf_ops));\n@@ -1229,9 +1230,11 @@ static inline void br_netfilter_sysctl_default(struct brnf_net *brnf)\n \tbrnf-\u003efilter_pppoe_tagged = 0;\n \tbrnf-\u003epass_vlan_indev = 0;\n }\n+#endif\n \n static int br_netfilter_sysctl_init_net(struct net *net)\n {\n+#ifdef CONFIG_SYSCTL\n \tstruct ctl_table *table = brnf_table;\n \tstruct brnf_net *brnet;\n \n@@ -1259,25 +1262,39 @@ static int br_netfilter_sysctl_init_net(struct net *net)\n \n \t\treturn -ENOMEM;\n \t}\n-\n+#endif\n \treturn 0;\n }\n \n static void br_netfilter_sysctl_exit_net(struct net *net,\n \t\t\t\t\t struct brnf_net *brnet)\n {\n-\tconst struct ctl_table *table = brnet-\u003ectl_hdr-\u003ectl_table_arg;\n+#ifdef CONFIG_SYSCTL\n+\tstruct ctl_table_header *header = brnet-\u003ectl_hdr;\n+\tconst struct ctl_table *table;\n \n-\tunregister_net_sysctl_table(brnet-\u003ectl_hdr);\n+\tif (!header)\n+\t\treturn;\n+\n+\ttable = header-\u003ectl_table_arg;\n+\tunregister_net_sysctl_table(header);\n \tif (!net_eq(net, \u0026init_net))\n \t\tkfree(table);\n+#endif\n }\n \n static int __net_init brnf_init_net(struct net *net)\n {\n+\tif (net-\u003euser_ns != \u0026init_user_ns) {\n+\t\tstruct brnf_net *brnet = net_generic(net, brnf_net_id);\n+\n+\t\tbrnet-\u003ein_userns = true;\n+\t\tpr_warn_once(\"br_netfilter no longer supported in user namespaces\\n\");\n+\t\treturn 0;\n+\t}\n+\n \treturn br_netfilter_sysctl_init_net(net);\n }\n-#endif\n \n static void __net_exit brnf_exit_net(struct net *net)\n {\n@@ -1289,15 +1306,11 @@ static void __net_exit brnf_exit_net(struct net *net)\n \t\tbrnet-\u003eenabled = false;\n \t}\n \n-#ifdef CONFIG_SYSCTL\n \tbr_netfilter_sysctl_exit_net(net, brnet);\n-#endif\n }\n \n static struct pernet_operations brnf_net_ops __read_mostly = {\n-#ifdef CONFIG_SYSCTL\n \t.init = brnf_init_net,\n-#endif\n \t.exit = brnf_exit_net,\n \t.id = \u0026brnf_net_id,\n \t.size = sizeof(struct brnf_net),\n@@ -1318,7 +1331,7 @@ static int __init br_netfilter_init(void)\n \t}\n \n \tRCU_INIT_POINTER(nf_br_ops, \u0026br_ops);\n-\tprintk(KERN_NOTICE \"Bridge firewalling registered\\n\");\n+\tpr_warn(\"br_netfilter is slated for removal in 2027.\\n\");\n \treturn 0;\n }\n \ndiff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c\nindex 2ffe64e736e08..15189e1fa0682 100644\n--- a/net/ipv4/ipip.c\n+++ b/net/ipv4/ipip.c\n@@ -379,6 +379,7 @@ static int ipip_fill_forward_path(struct net_device_path_ctx *ctx,\n \tpath-\u003etun.src_v4.s_addr = tiph-\u003esaddr;\n \tpath-\u003etun.dst_v4.s_addr = tiph-\u003edaddr;\n \tpath-\u003etun.inner_proto = IPPROTO_IPIP;\n+\tpath-\u003etun.encap_proto = AF_INET;\n \tpath-\u003etun.dst = \u0026rt-\u003edst;\n \tpath-\u003edev = ctx-\u003edev;\n \ndiff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c\nindex d5ff50a2ac017..64a184bca4677 100644\n--- a/net/ipv6/ip6_tunnel.c\n+++ b/net/ipv6/ip6_tunnel.c\n@@ -1868,6 +1868,7 @@ static int ip6_tnl_fill_forward_path(struct net_device_path_ctx *ctx,\n \t\tpath-\u003etun.src_v6 = fl6.saddr;\n \t\tpath-\u003etun.dst_v6 = fl6.daddr;\n \t\tpath-\u003etun.inner_proto = IPPROTO_IPV6;\n+\t\tpath-\u003etun.encap_proto = AF_INET6;\n \t\tpath-\u003etun.dst = dst;\n \t\tpath-\u003edev = ctx-\u003edev;\n \t\tctx-\u003edev = dst-\u003edev;\ndiff --git a/net/netfilter/core.c b/net/netfilter/core.c\nindex 940dea2663e95..44c856c1e621c 100644\n--- a/net/netfilter/core.c\n+++ b/net/netfilter/core.c\n@@ -529,7 +529,7 @@ static void __nf_unregister_net_hook(struct net *net, int pf,\n \tif (!p)\n \t\treturn;\n \n-\tnf_queue_nf_hook_drop(net);\n+\tnf_queue_nf_hook_drop(net, reg);\n \tnf_hook_entries_free(p);\n }\n \ndiff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c\nindex 45655cff078cb..157f5c8361ab4 100644\n--- a/net/netfilter/ipvs/ip_vs_sync.c\n+++ b/net/netfilter/ipvs/ip_vs_sync.c\n@@ -543,13 +543,15 @@ static void ip_vs_sync_conn_v0(struct netns_ipvs *ipvs, struct ip_vs_conn *cp,\n \tstruct ip_vs_sync_conn_v0 *s;\n \tstruct ip_vs_sync_buff *buff;\n \tstruct ipvs_master_sync_state *ms;\n-\tint id;\n+\tu32 flags, seq_mask;\n \tunsigned int len;\n+\tint id;\n \n \tif (unlikely(cp-\u003eaf != AF_INET))\n \t\treturn;\n+\tflags = READ_ONCE(cp-\u003eflags);\n \t/* Do not sync ONE PACKET */\n-\tif (cp-\u003eflags \u0026 IP_VS_CONN_F_ONE_PACKET)\n+\tif (flags \u0026 IP_VS_CONN_F_ONE_PACKET)\n \t\treturn;\n \n \tif (!ip_vs_sync_conn_needed(ipvs, cp, pkts))\n@@ -564,8 +566,8 @@ static void ip_vs_sync_conn_v0(struct netns_ipvs *ipvs, struct ip_vs_conn *cp,\n \tid = select_master_thread_id(ipvs, cp);\n \tms = \u0026ipvs-\u003ems[id];\n \tbuff = ms-\u003esync_buff;\n-\tlen = (cp-\u003eflags \u0026 IP_VS_CONN_F_SEQ_MASK) ? FULL_CONN_SIZE :\n-\t\tSIMPLE_CONN_SIZE;\n+\tseq_mask = flags \u0026 IP_VS_CONN_F_SEQ_MASK;\n+\tlen = seq_mask ? FULL_CONN_SIZE : SIMPLE_CONN_SIZE;\n \tif (buff) {\n \t\tm = (struct ip_vs_sync_mesg_v0 *) buff-\u003emesg;\n \t\t/* Send buffer if it is for v1 */\n@@ -597,9 +599,9 @@ static void ip_vs_sync_conn_v0(struct netns_ipvs *ipvs, struct ip_vs_conn *cp,\n \ts-\u003ecaddr = cp-\u003ecaddr.ip;\n \ts-\u003evaddr = cp-\u003evaddr.ip;\n \ts-\u003edaddr = cp-\u003edaddr.ip;\n-\ts-\u003eflags = htons(cp-\u003eflags \u0026 ~IP_VS_CONN_F_HASHED);\n+\ts-\u003eflags = htons(flags \u0026 ~IP_VS_CONN_F_HASHED);\n \ts-\u003estate = htons(cp-\u003estate);\n-\tif (cp-\u003eflags \u0026 IP_VS_CONN_F_SEQ_MASK) {\n+\tif (seq_mask) {\n \t\tstruct ip_vs_sync_conn_options *opt =\n \t\t\t(struct ip_vs_sync_conn_options *)\u0026s[1];\n \t\tmemcpy(opt, \u0026cp-\u003esync_conn_opt, sizeof(*opt));\n@@ -635,6 +637,7 @@ void ip_vs_sync_conn(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, int pkts)\n \tint id;\n \t__u8 *p;\n \tunsigned int len, pe_name_len, pad;\n+\tu32 flags, seq_mask;\n \n \t/* Handle old version of the protocol */\n \tif (sysctl_sync_ver(ipvs) == 0) {\n@@ -647,6 +650,7 @@ void ip_vs_sync_conn(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, int pkts)\n sloop:\n \tif (!ip_vs_sync_conn_needed(ipvs, cp, pkts))\n \t\tgoto control;\n+\tflags = READ_ONCE(cp-\u003eflags);\n \n \t/* Sanity checks */\n \tpe_name_len = 0;\n@@ -674,7 +678,8 @@ void ip_vs_sync_conn(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, int pkts)\n #endif\n \t\tlen = sizeof(struct ip_vs_sync_v4);\n \n-\tif (cp-\u003eflags \u0026 IP_VS_CONN_F_SEQ_MASK)\n+\tseq_mask = flags \u0026 IP_VS_CONN_F_SEQ_MASK;\n+\tif (seq_mask)\n \t\tlen += sizeof(struct ip_vs_sync_conn_options) + 2;\n \n \tif (cp-\u003epe_data_len)\n@@ -720,7 +725,7 @@ void ip_vs_sync_conn(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, int pkts)\n \t/* Set message type \u0026 copy members */\n \ts-\u003ev4.type = (cp-\u003eaf == AF_INET6 ? STYPE_F_INET6 : 0);\n \ts-\u003ev4.ver_size = htons(len \u0026 SVER_MASK);\t/* Version 0 */\n-\ts-\u003ev4.flags = htonl(cp-\u003eflags \u0026 ~IP_VS_CONN_F_HASHED);\n+\ts-\u003ev4.flags = htonl(flags \u0026 ~IP_VS_CONN_F_HASHED);\n \ts-\u003ev4.state = htons(cp-\u003estate);\n \ts-\u003ev4.protocol = cp-\u003eprotocol;\n \ts-\u003ev4.cport = cp-\u003ecport;\n@@ -744,7 +749,7 @@ void ip_vs_sync_conn(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, int pkts)\n \t\ts-\u003ev4.vaddr = cp-\u003evaddr.ip;\n \t\ts-\u003ev4.daddr = cp-\u003edaddr.ip;\n \t}\n-\tif (cp-\u003eflags \u0026 IP_VS_CONN_F_SEQ_MASK) {\n+\tif (seq_mask) {\n \t\t*(p++) = IPVS_OPT_SEQ_DATA;\n \t\t*(p++) = sizeof(struct ip_vs_sync_conn_options);\n \t\thton_seq(\u0026cp-\u003ein_seq, (struct ip_vs_seq *)p);\ndiff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c\nindex f9b8927327ba9..931b76aacd934 100644\n--- a/net/netfilter/nf_conntrack_core.c\n+++ b/net/netfilter/nf_conntrack_core.c\n@@ -2410,7 +2410,7 @@ nf_ct_iterate_destroy(int (*iter)(struct nf_conn *i, void *data), void *data)\n \n \t\tif (atomic_read(\u0026cnet-\u003ecount) == 0)\n \t\t\tcontinue;\n-\t\tnf_queue_nf_hook_drop(net);\n+\t\tnf_queue_nf_hook_drop(net, NULL);\n \t}\n \tup_read(\u0026net_rwsem);\n \n@@ -2506,6 +2506,11 @@ void nf_conntrack_cleanup_net_list(struct list_head *net_exit_list)\n \t\tgoto i_see_dead_people;\n \t}\n \n+\t/* must wait for current scan loop, worker also looks at\n+\t * struct nf_conntrack_net, which is about to be free'd.\n+\t */\n+\tflush_work(\u0026conntrack_gc_work.dwork.work);\n+\n \tlist_for_each_entry(net, net_exit_list, exit_list) {\n \t\twarn_on_keymap_list_leak(net);\n \t\tnf_conntrack_ecache_pernet_fini(net);\ndiff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c\nindex c8c29a9a16844..e13f9397e8cbd 100644\n--- a/net/netfilter/nf_flow_table_ip.c\n+++ b/net/netfilter/nf_flow_table_ip.c\n@@ -189,22 +189,24 @@ static void nf_flow_tuple_encap(struct nf_flowtable_ctx *ctx,\n \t\tbreak;\n \t}\n \n+\tif (ctx-\u003etun.inner_proto != IPPROTO_IPIP \u0026\u0026\n+\t ctx-\u003etun.inner_proto != IPPROTO_IPV6)\n+\t\treturn;\n+\n \tswitch (ctx-\u003eether_type) {\n \tcase htons(ETH_P_IP):\n \t\tiph = (struct iphdr *)(skb_network_header(skb) + offset);\n-\t\tif (ctx-\u003etun.inner_proto == IPPROTO_IPIP) {\n-\t\t\ttuple-\u003etun.dst_v4.s_addr = iph-\u003edaddr;\n-\t\t\ttuple-\u003etun.src_v4.s_addr = iph-\u003esaddr;\n-\t\t\ttuple-\u003etun.inner_proto = IPPROTO_IPIP;\n-\t\t}\n+\t\ttuple-\u003etun.dst_v4.s_addr = iph-\u003edaddr;\n+\t\ttuple-\u003etun.src_v4.s_addr = iph-\u003esaddr;\n+\t\ttuple-\u003etun.inner_proto = ctx-\u003etun.inner_proto;\n+\t\ttuple-\u003etun.encap_proto = AF_INET;\n \t\tbreak;\n \tcase htons(ETH_P_IPV6):\n \t\tip6h = (struct ipv6hdr *)(skb_network_header(skb) + offset);\n-\t\tif (ctx-\u003etun.inner_proto == IPPROTO_IPV6) {\n-\t\t\ttuple-\u003etun.dst_v6 = ip6h-\u003edaddr;\n-\t\t\ttuple-\u003etun.src_v6 = ip6h-\u003esaddr;\n-\t\t\ttuple-\u003etun.inner_proto = IPPROTO_IPV6;\n-\t\t}\n+\t\ttuple-\u003etun.dst_v6 = ip6h-\u003edaddr;\n+\t\ttuple-\u003etun.src_v6 = ip6h-\u003esaddr;\n+\t\ttuple-\u003etun.inner_proto = ctx-\u003etun.inner_proto;\n+\t\ttuple-\u003etun.encap_proto = AF_INET6;\n \t\tbreak;\n \tdefault:\n \t\tbreak;\n@@ -372,6 +374,11 @@ static void nf_flow_ip_tunnel_pop(struct nf_flowtable_ctx *ctx,\n \n \tskb_pull(skb, ctx-\u003etun.hdr_size);\n \tskb_reset_network_header(skb);\n+\n+\tif (ctx-\u003etun.inner_proto == IPPROTO_IPIP)\n+\t\tskb-\u003eprotocol = htons(ETH_P_IP);\n+\telse\n+\t\tskb-\u003eprotocol = htons(ETH_P_IPV6);\n }\n \n static bool nf_flow_skb_encap_protocol(struct nf_flowtable_ctx *ctx,\n@@ -580,23 +587,44 @@ static int nf_flow_pppoe_push(struct sk_buff *skb, u16 id,\n \treturn 0;\n }\n \n-static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb,\n-\t\t\t\t struct flow_offload_tuple *tuple,\n-\t\t\t\t struct dst_entry *dst, __be32 *ip_daddr)\n+static int nf_flow_tunnel_ip_push(struct net *net, struct sk_buff *skb,\n+\t\t\t\t struct flow_offload_tuple *tuple,\n+\t\t\t\t struct dst_entry *dst,\n+\t\t\t\t union nf_inet_addr **ip_addr)\n {\n-\tstruct iphdr *iph = (struct iphdr *)skb_network_header(skb);\n-\tstruct rtable *rt = dst_rtable(dst);\n-\tu8 tos = iph-\u003etos, ttl = iph-\u003ettl;\n-\t__be16 frag_off = iph-\u003efrag_off;\n-\tu32 headroom = sizeof(*iph);\n+\t__be16 frag_off = 0;\n+\tstruct iphdr *iph;\n+\tu8 tos = 0, ttl;\n+\tu32 headroom;\n \tint err;\n \n+\tswitch (tuple-\u003etun.inner_proto) {\n+\tcase IPPROTO_IPV6: {\n+\t\tstruct ipv6hdr *ip6h;\n+\n+\t\tip6h = (struct ipv6hdr *)skb_network_header(skb);\n+\t\ttos = ipv6_get_dsfield(ip6h);\n+\t\tttl = ip6h-\u003ehop_limit;\n+\t\tfrag_off = htons(IP_DF);\n+\t\tbreak;\n+\t}\n+\tcase IPPROTO_IPIP:\n+\t\tiph = (struct iphdr *)skb_network_header(skb);\n+\t\tfrag_off = iph-\u003efrag_off;\n+\t\ttos = iph-\u003etos;\n+\t\tttl = iph-\u003ettl;\n+\t\tbreak;\n+\tdefault:\n+\t\treturn 0;\n+\t}\n+\n \terr = iptunnel_handle_offloads(skb, SKB_GSO_IPXIP4);\n \tif (err)\n \t\treturn err;\n \n-\tskb_set_inner_ipproto(skb, IPPROTO_IPIP);\n-\theadroom += LL_RESERVED_SPACE(rt-\u003edst.dev) + rt-\u003edst.header_len;\n+\tskb_set_inner_ipproto(skb, tuple-\u003etun.inner_proto);\n+\theadroom = sizeof(*iph) + LL_RESERVED_SPACE(dst-\u003edev) +\n+\t\t dst-\u003eheader_len;\n \terr = skb_cow_head(skb, headroom);\n \tif (err)\n \t\treturn err;\n@@ -607,11 +635,12 @@ static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb,\n \t/* Push down and install the IP header. */\n \tskb_push(skb, sizeof(*iph));\n \tskb_reset_network_header(skb);\n+\tskb-\u003eprotocol = htons(ETH_P_IP);\n \n \tiph = ip_hdr(skb);\n \tiph-\u003eversion\t= 4;\n \tiph-\u003eihl\t= sizeof(*iph) \u003e\u003e 2;\n-\tiph-\u003efrag_off\t= ip_mtu_locked(\u0026rt-\u003edst) ? 0 : frag_off;\n+\tiph-\u003efrag_off\t= ip_mtu_locked(dst) ? 0 : frag_off;\n \tiph-\u003eprotocol\t= tuple-\u003etun.inner_proto;\n \tiph-\u003etos\t= tos;\n \tiph-\u003edaddr\t= tuple-\u003etun.src_v4.s_addr;\n@@ -621,81 +650,91 @@ static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb,\n \t__ip_select_ident(net, iph, skb_shinfo(skb)-\u003egso_segs ?: 1);\n \tip_send_check(iph);\n \n-\t*ip_daddr = tuple-\u003etun.src_v4.s_addr;\n-\n-\treturn 0;\n-}\n-\n-static int nf_flow_tunnel_v4_push(struct net *net, struct sk_buff *skb,\n-\t\t\t\t struct flow_offload_tuple *tuple,\n-\t\t\t\t struct dst_entry *dst, __be32 *ip_daddr)\n-{\n-\tif (tuple-\u003etun_num)\n-\t\treturn nf_flow_tunnel_ipip_push(net, skb, tuple, dst, ip_daddr);\n+\t*ip_addr = (union nf_inet_addr *)\u0026tuple-\u003etun.src_v4;\n \n \treturn 0;\n }\n \n-static int nf_flow_tunnel_ip6ip6_push(struct net *net, struct sk_buff *skb,\n-\t\t\t\t struct flow_offload_tuple *tuple,\n-\t\t\t\t struct dst_entry *dst,\n-\t\t\t\t struct in6_addr **ip6_daddr)\n+static int nf_flow_tunnel_ip6_push(struct net *net, struct sk_buff *skb,\n+\t\t\t\t struct flow_offload_tuple *tuple,\n+\t\t\t\t struct dst_entry *dst,\n+\t\t\t\t union nf_inet_addr **ip_addr)\n {\n-\tstruct ipv6hdr *ip6h = (struct ipv6hdr *)skb_network_header(skb);\n-\t__u8 dsfield = ipv6_get_dsfield(ip6h);\n-\tstruct rtable *rt = dst_rtable(dst);\n \tstruct flowi6 fl6 = {\n \t\t.daddr = tuple-\u003etun.src_v6,\n \t\t.saddr = tuple-\u003etun.dst_v6,\n-\t\t.flowi6_proto = IPPROTO_IPV6,\n+\t\t.flowi6_proto = tuple-\u003etun.inner_proto,\n \t};\n-\tu8 hop_limit = ip6h-\u003ehop_limit;\n+\tu8 hop_limit, dsfield;\n+\tstruct ipv6hdr *ip6h;\n \tint err, mtu;\n \tu32 headroom;\n \n+\tswitch (tuple-\u003etun.inner_proto) {\n+\tcase IPPROTO_IPIP: {\n+\t\tstruct iphdr *iph = (struct iphdr *)skb_network_header(skb);\n+\n+\t\tdsfield = ipv4_get_dsfield(iph);\n+\t\thop_limit = iph-\u003ettl;\n+\t\tbreak;\n+\t}\n+\tcase IPPROTO_IPV6:\n+\t\tip6h = (struct ipv6hdr *)skb_network_header(skb);\n+\t\tdsfield = ipv6_get_dsfield(ip6h);\n+\t\thop_limit = ip6h-\u003ehop_limit;\n+\t\tbreak;\n+\tdefault:\n+\t\treturn 0;\n+\t}\n+\n \terr = iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6);\n \tif (err)\n \t\treturn err;\n \n-\tskb_set_inner_ipproto(skb, IPPROTO_IPV6);\n-\theadroom = sizeof(*ip6h) + LL_RESERVED_SPACE(rt-\u003edst.dev) +\n-\t\t rt-\u003edst.header_len;\n+\tskb_set_inner_ipproto(skb, tuple-\u003etun.inner_proto);\n+\theadroom = sizeof(*ip6h) + LL_RESERVED_SPACE(dst-\u003edev) +\n+\t\t dst-\u003eheader_len;\n \terr = skb_cow_head(skb, headroom);\n \tif (err)\n \t\treturn err;\n \n \tskb_scrub_packet(skb, true);\n-\tmtu = dst_mtu(\u0026rt-\u003edst) - sizeof(*ip6h);\n+\tmtu = dst_mtu(dst) - sizeof(*ip6h);\n \tmtu = max(mtu, IPV6_MIN_MTU);\n \tskb_dst_update_pmtu_no_confirm(skb, mtu);\n \n \tskb_push(skb, sizeof(*ip6h));\n \tskb_reset_network_header(skb);\n+\tskb-\u003eprotocol = htons(ETH_P_IPV6);\n \n \tip6h = ipv6_hdr(skb);\n \tip6_flow_hdr(ip6h, dsfield,\n \t\t ip6_make_flowlabel(net, skb, fl6.flowlabel, true, \u0026fl6));\n \tip6h-\u003ehop_limit = hop_limit;\n-\tip6h-\u003enexthdr = IPPROTO_IPV6;\n+\tip6h-\u003enexthdr = tuple-\u003etun.inner_proto;\n \tip6h-\u003edaddr = tuple-\u003etun.src_v6;\n \tip6h-\u003esaddr = tuple-\u003etun.dst_v6;\n \tipv6_hdr(skb)-\u003epayload_len = htons(skb-\u003elen - sizeof(*ip6h));\n \tIP6CB(skb)-\u003enhoff = offsetof(struct ipv6hdr, nexthdr);\n \n-\t*ip6_daddr = \u0026tuple-\u003etun.src_v6;\n+\t*ip_addr = (union nf_inet_addr *)\u0026tuple-\u003etun.src_v6;\n \n \treturn 0;\n }\n \n-static int nf_flow_tunnel_v6_push(struct net *net, struct sk_buff *skb,\n-\t\t\t\t struct flow_offload_tuple *tuple,\n-\t\t\t\t struct dst_entry *dst,\n-\t\t\t\t struct in6_addr **ip6_daddr)\n+static int nf_flow_tunnel_push(struct net *net, struct sk_buff *skb,\n+\t\t\t struct flow_offload_tuple *tuple,\n+\t\t\t struct dst_entry *dst,\n+\t\t\t union nf_inet_addr **ip_addr)\n {\n-\tif (tuple-\u003etun_num)\n-\t\treturn nf_flow_tunnel_ip6ip6_push(net, skb, tuple, dst, ip6_daddr);\n-\n-\treturn 0;\n+\tswitch (tuple-\u003etun.encap_proto) {\n+\tcase AF_INET:\n+\t\treturn nf_flow_tunnel_ip_push(net, skb, tuple, dst, ip_addr);\n+\tcase AF_INET6:\n+\t\treturn nf_flow_tunnel_ip6_push(net, skb, tuple, dst, ip_addr);\n+\tdefault:\n+\t\treturn 0;\n+\t}\n }\n \n static int nf_flow_encap_push(struct sk_buff *skb,\n@@ -802,10 +841,10 @@ static int nf_flow_queue_xmit4(struct sk_buff *skb,\n \tstruct flow_offload_tuple *other_tuple;\n \tenum flow_offload_tuple_dir dir;\n \tstruct nf_flow_xmit xmit = {};\n+\tunion nf_inet_addr *ip_addr;\n \tstruct flow_offload *flow;\n \tstruct neighbour *neigh;\n \tstruct rtable *rt;\n-\t__be32 ip_daddr;\n \n \tif (unlikely(tuplehash-\u003etuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) {\n \t\trt = dst_rtable(tuplehash-\u003etuple.dst_cache);\n@@ -818,10 +857,10 @@ static int nf_flow_queue_xmit4(struct sk_buff *skb,\n \tdir = tuplehash-\u003etuple.dir;\n \tflow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);\n \tother_tuple = \u0026flow-\u003etuplehash[!dir].tuple;\n-\tip_daddr = other_tuple-\u003esrc_v4.s_addr;\n+\tip_addr = (union nf_inet_addr *)\u0026other_tuple-\u003esrc_v4;\n \n-\tif (nf_flow_tunnel_v4_push(state-\u003enet, skb, other_tuple,\n-\t\t\t\t tuplehash-\u003etuple.dst_cache, \u0026ip_daddr) \u003c 0)\n+\tif (nf_flow_tunnel_push(state-\u003enet, skb, other_tuple,\n+\t\t\t\ttuplehash-\u003etuple.dst_cache, \u0026ip_addr) \u003c 0)\n \t\treturn NF_DROP;\n \n \tswitch (tuplehash-\u003etuple.xmit_type) {\n@@ -832,7 +871,7 @@ static int nf_flow_queue_xmit4(struct sk_buff *skb,\n \t\t\tflow_offload_teardown(flow);\n \t\t\treturn NF_DROP;\n \t\t}\n-\t\tneigh = ip_neigh_gw4(rt-\u003edst.dev, rt_nexthop(rt, ip_daddr));\n+\t\tneigh = ip_neigh_gw4(rt-\u003edst.dev, rt_nexthop(rt, ip_addr-\u003eip));\n \t\tif (IS_ERR(neigh)) {\n \t\t\tflow_offload_teardown(flow);\n \t\t\treturn NF_DROP;\n@@ -1130,7 +1169,7 @@ static int nf_flow_queue_xmit6(struct sk_buff *skb,\n \tstruct flow_offload_tuple *other_tuple;\n \tenum flow_offload_tuple_dir dir;\n \tstruct nf_flow_xmit xmit = {};\n-\tstruct in6_addr *ip6_daddr;\n+\tunion nf_inet_addr *ip_addr;\n \tstruct flow_offload *flow;\n \tstruct neighbour *neigh;\n \tstruct rt6_info *rt;\n@@ -1146,11 +1185,10 @@ static int nf_flow_queue_xmit6(struct sk_buff *skb,\n \tdir = tuplehash-\u003etuple.dir;\n \tflow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);\n \tother_tuple = \u0026flow-\u003etuplehash[!dir].tuple;\n-\tip6_daddr = \u0026other_tuple-\u003esrc_v6;\n+\tip_addr = (union nf_inet_addr *)\u0026other_tuple-\u003esrc_v6;\n \n-\tif (nf_flow_tunnel_v6_push(state-\u003enet, skb, other_tuple,\n-\t\t\t\t tuplehash-\u003etuple.dst_cache,\n-\t\t\t\t \u0026ip6_daddr) \u003c 0)\n+\tif (nf_flow_tunnel_push(state-\u003enet, skb, other_tuple,\n+\t\t\t\ttuplehash-\u003etuple.dst_cache, \u0026ip_addr) \u003c 0)\n \t\treturn NF_DROP;\n \n \tswitch (tuplehash-\u003etuple.xmit_type) {\n@@ -1161,7 +1199,7 @@ static int nf_flow_queue_xmit6(struct sk_buff *skb,\n \t\t\tflow_offload_teardown(flow);\n \t\t\treturn NF_DROP;\n \t\t}\n-\t\tneigh = ip_neigh_gw6(rt-\u003edst.dev, rt6_nexthop(rt, ip6_daddr));\n+\t\tneigh = ip_neigh_gw6(rt-\u003edst.dev, rt6_nexthop(rt, \u0026ip_addr-\u003ein6));\n \t\tif (IS_ERR(neigh)) {\n \t\t\tflow_offload_teardown(flow);\n \t\t\treturn NF_DROP;\ndiff --git a/net/netfilter/nf_flow_table_path.c b/net/netfilter/nf_flow_table_path.c\nindex d90013685bf12..841f099434d98 100644\n--- a/net/netfilter/nf_flow_table_path.c\n+++ b/net/netfilter/nf_flow_table_path.c\n@@ -138,6 +138,7 @@ static int nft_dev_path_info(struct net_device_path_stack *stack,\n \t\t\t\tinfo-\u003etun.dst_v6 = path-\u003etun.dst_v6;\n \t\t\t\tinfo-\u003etun.inner_proto = path-\u003etun.inner_proto;\n \t\t\t\tinfo-\u003etun_dst = path-\u003etun.dst;\n+\t\t\t\tinfo-\u003etun.encap_proto = path-\u003etun.encap_proto;\n \t\t\t\tinfo-\u003enum_tuns++;\n \t\t\t} else {\n \t\t\t\tif (info-\u003enum_encaps \u003e= NF_FLOW_TABLE_ENCAP_MAX)\n@@ -249,6 +250,7 @@ static int nft_dev_forward_path(const struct nft_pktinfo *pkt,\n \t\troute-\u003etuple[!dir].in.tun.src_v6 = info.tun.dst_v6;\n \t\troute-\u003etuple[!dir].in.tun.dst_v6 = info.tun.src_v6;\n \t\troute-\u003etuple[!dir].in.tun.inner_proto = info.tun.inner_proto;\n+\t\troute-\u003etuple[!dir].in.tun.encap_proto = info.tun.encap_proto;\n \t\troute-\u003etuple[!dir].in.num_tuns = info.num_tuns;\n \t\tdst_release(route-\u003etuple[dir].dst);\n \t\troute-\u003etuple[dir].dst = info.tun_dst;\ndiff --git a/net/netfilter/nf_internals.h b/net/netfilter/nf_internals.h\nindex 25403023060b6..ce5aff2cae718 100644\n--- a/net/netfilter/nf_internals.h\n+++ b/net/netfilter/nf_internals.h\n@@ -24,7 +24,7 @@\n #define CTA_FILTER_FLAG(ctattr) CTA_FILTER_F_ ## ctattr\n \n /* nf_queue.c */\n-void nf_queue_nf_hook_drop(struct net *net);\n+void nf_queue_nf_hook_drop(struct net *net, const struct nf_hook_ops *ops);\n \n /* nf_log.c */\n int __init netfilter_log_init(void);\ndiff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c\nindex 73363ceedebe3..166d05e994ac6 100644\n--- a/net/netfilter/nf_queue.c\n+++ b/net/netfilter/nf_queue.c\n@@ -129,14 +129,14 @@ bool nf_queue_entry_get_refs(struct nf_queue_entry *entry)\n }\n EXPORT_SYMBOL_GPL(nf_queue_entry_get_refs);\n \n-void nf_queue_nf_hook_drop(struct net *net)\n+void nf_queue_nf_hook_drop(struct net *net, const struct nf_hook_ops *ops)\n {\n \tconst struct nf_queue_handler *qh;\n \n \trcu_read_lock();\n \tqh = rcu_dereference(nf_queue_handler);\n \tif (qh)\n-\t\tqh-\u003enf_hook_drop(net);\n+\t\tqh-\u003enf_hook_drop(net, ops);\n \trcu_read_unlock();\n }\n EXPORT_SYMBOL_GPL(nf_queue_nf_hook_drop);\ndiff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c\nindex b59628e6240c1..59ac623e6805f 100644\n--- a/net/netfilter/nf_tables_api.c\n+++ b/net/netfilter/nf_tables_api.c\n@@ -1036,32 +1036,67 @@ static inline u64 nf_tables_alloc_handle(struct nft_table *table)\n \treturn ++table-\u003ehgenerator;\n }\n \n-static const struct nft_chain_type *chain_type[NFPROTO_NUMPROTO][NFT_CHAIN_T_MAX];\n+static const struct nft_chain_type __rcu *chain_type[NFPROTO_NUMPROTO][NFT_CHAIN_T_MAX];\n \n static const struct nft_chain_type *\n-__nft_chain_type_get(u8 family, enum nft_chain_types type)\n+__nft_chain_type_peek(u8 family, enum nft_chain_types type)\n {\n \tif (family \u003e= NFPROTO_NUMPROTO ||\n \t type \u003e= NFT_CHAIN_T_MAX)\n \t\treturn NULL;\n \n-\treturn chain_type[family][type];\n+\treturn rcu_dereference_check(chain_type[family][type],\n+\t\t\t\t lockdep_nfnl_is_held(NFNL_SUBSYS_NFTABLES));\n }\n \n+/**\n+ * nft_chain_type_get - look up a chain type by family and id\n+ * @family: netlink family\n+ * @type: chain type (enum nft_chain_types)\n+ *\n+ * Return: chain type with a held module ref, or NULL.\n+ */\n+static const struct nft_chain_type *\n+nft_chain_type_get(u8 family, enum nft_chain_types type)\n+{\n+\tconst struct nft_chain_type *t;\n+\n+\trcu_read_lock();\n+\tt = __nft_chain_type_peek(family, type);\n+\n+\tif (t \u0026\u0026 !try_module_get(t-\u003eowner))\n+\t\tt = NULL;\n+\n+\trcu_read_unlock();\n+\treturn t;\n+}\n+\n+/**\n+ * __nf_tables_chain_type_lookup - look up a chain type by name\n+ * @nla: nlattr containing the chain-type name\n+ * @family: netlink family\n+ *\n+ * Return: matching chain type with a held module ref, or NULL.\n+ */\n static const struct nft_chain_type *\n __nf_tables_chain_type_lookup(const struct nlattr *nla, u8 family)\n {\n-\tconst struct nft_chain_type *type;\n+\tconst struct nft_chain_type *type = NULL, *t;\n \tint i;\n \n+\trcu_read_lock();\n \tfor (i = 0; i \u003c NFT_CHAIN_T_MAX; i++) {\n-\t\ttype = __nft_chain_type_get(family, i);\n-\t\tif (!type)\n+\t\tt = __nft_chain_type_peek(family, i);\n+\t\tif (!t)\n \t\t\tcontinue;\n-\t\tif (!nla_strcmp(nla, type-\u003ename))\n-\t\t\treturn type;\n+\n+\t\tif (!nla_strcmp(nla, t-\u003ename) \u0026\u0026 try_module_get(t-\u003eowner)) {\n+\t\t\ttype = t;\n+\t\t\tbreak;\n+\t\t}\n \t}\n-\treturn NULL;\n+\trcu_read_unlock();\n+\treturn type;\n }\n \n struct nft_module_request {\n@@ -1837,11 +1872,11 @@ static void nf_tables_table_destroy(struct nft_table *table)\n void nft_register_chain_type(const struct nft_chain_type *ctype)\n {\n \tnfnl_lock(NFNL_SUBSYS_NFTABLES);\n-\tif (WARN_ON(__nft_chain_type_get(ctype-\u003efamily, ctype-\u003etype))) {\n+\tif (WARN_ON(__nft_chain_type_peek(ctype-\u003efamily, ctype-\u003etype))) {\n \t\tnfnl_unlock(NFNL_SUBSYS_NFTABLES);\n \t\treturn;\n \t}\n-\tchain_type[ctype-\u003efamily][ctype-\u003etype] = ctype;\n+\trcu_assign_pointer(chain_type[ctype-\u003efamily][ctype-\u003etype], ctype);\n \tnfnl_unlock(NFNL_SUBSYS_NFTABLES);\n }\n EXPORT_SYMBOL_GPL(nft_register_chain_type);\n@@ -1849,7 +1884,7 @@ EXPORT_SYMBOL_GPL(nft_register_chain_type);\n void nft_unregister_chain_type(const struct nft_chain_type *ctype)\n {\n \tnfnl_lock(NFNL_SUBSYS_NFTABLES);\n-\tchain_type[ctype-\u003efamily][ctype-\u003etype] = NULL;\n+\tRCU_INIT_POINTER(chain_type[ctype-\u003efamily][ctype-\u003etype], NULL);\n \tnfnl_unlock(NFNL_SUBSYS_NFTABLES);\n }\n EXPORT_SYMBOL_GPL(nft_unregister_chain_type);\n@@ -2581,10 +2616,6 @@ static int nft_chain_parse_hook(struct net *net,\n \t\thook-\u003enum = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM]));\n \t\thook-\u003epriority = ntohl(nla_get_be32(ha[NFTA_HOOK_PRIORITY]));\n \n-\t\ttype = __nft_chain_type_get(family, NFT_CHAIN_T_DEFAULT);\n-\t\tif (!type)\n-\t\t\treturn -EOPNOTSUPP;\n-\n \t\tif (nla[NFTA_CHAIN_TYPE]) {\n \t\t\ttype = nf_tables_chain_type_lookup(net, nla[NFTA_CHAIN_TYPE],\n \t\t\t\t\t\t\t family, true);\n@@ -2592,13 +2623,19 @@ static int nft_chain_parse_hook(struct net *net,\n \t\t\t\tNL_SET_BAD_ATTR(extack, nla[NFTA_CHAIN_TYPE]);\n \t\t\t\treturn PTR_ERR(type);\n \t\t\t}\n+\t\t} else {\n+\t\t\ttype = nft_chain_type_get(family, NFT_CHAIN_T_DEFAULT);\n+\t\t\tif (!type)\n+\t\t\t\treturn -EOPNOTSUPP;\n \t\t}\n+\n+\t\terr = -EOPNOTSUPP;\n \t\tif (hook-\u003enum \u003e= NFT_MAX_HOOKS || !(type-\u003ehook_mask \u0026 (1 \u003c\u003c hook-\u003enum)))\n-\t\t\treturn -EOPNOTSUPP;\n+\t\t\tgoto out_put_type;\n \n \t\tif (type-\u003etype == NFT_CHAIN_T_NAT \u0026\u0026\n \t\t hook-\u003epriority \u003c= NF_IP_PRI_CONNTRACK)\n-\t\t\treturn -EOPNOTSUPP;\n+\t\t\tgoto out_put_type;\n \t} else {\n \t\tif (ha[NFTA_HOOK_HOOKNUM]) {\n \t\t\thook-\u003enum = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM]));\n@@ -2620,30 +2657,27 @@ static int nft_chain_parse_hook(struct net *net,\n \t\t\t}\n \t\t} else {\n \t\t\ttype = basechain-\u003etype;\n+\t\t\t__module_get(type-\u003eowner);\n \t\t}\n \t}\n \n-\tif (!try_module_get(type-\u003eowner)) {\n-\t\tif (nla[NFTA_CHAIN_TYPE])\n-\t\t\tNL_SET_BAD_ATTR(extack, nla[NFTA_CHAIN_TYPE]);\n-\t\treturn -ENOENT;\n-\t}\n-\n \thook-\u003etype = type;\n \n \tINIT_LIST_HEAD(\u0026hook-\u003elist);\n \tif (nft_base_chain_netdev(family, hook-\u003enum)) {\n \t\terr = nft_chain_parse_netdev(net, ha, \u0026hook-\u003elist, extack, flags);\n-\t\tif (err \u003c 0) {\n-\t\t\tmodule_put(type-\u003eowner);\n-\t\t\treturn err;\n-\t\t}\n+\t\tif (err \u003c 0)\n+\t\t\tgoto out_put_type;\n \t} else if (ha[NFTA_HOOK_DEV] || ha[NFTA_HOOK_DEVS]) {\n-\t\tmodule_put(type-\u003eowner);\n-\t\treturn -EOPNOTSUPP;\n+\t\terr = -EOPNOTSUPP;\n+\t\tgoto out_put_type;\n \t}\n \n \treturn 0;\n+\n+out_put_type:\n+\tmodule_put(type-\u003eowner);\n+\treturn err;\n }\n \n static void nft_chain_release_hook(struct nft_chain_hook *hook)\ndiff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c\nindex a3bc002800510..00b76c25d55c9 100644\n--- a/net/netfilter/nfnetlink_queue.c\n+++ b/net/netfilter/nfnetlink_queue.c\n@@ -1559,9 +1559,39 @@ static struct notifier_block nfqnl_dev_notifier = {\n \t.notifier_call\t= nfqnl_rcv_dev_event,\n };\n \n-static void nfqnl_nf_hook_drop(struct net *net)\n+/* Match the entries whose hook_index points into the hook array that\n+ * __nf_unregister_net_hook() replaces when it removes @ops.\n+ *\n+ * ops-\u003epf/hooknum name the hook as registered; entry-\u003estate.pf/hook\n+ * name the array actually traversed - they differ only for NFPROTO_INET.\n+ * Every other family uses the array of its own name.\n+ */\n+static int nf_hook_cmp(struct nf_queue_entry *entry, unsigned long ops_ptr)\n+{\n+\tconst struct nf_hook_ops *ops = (const struct nf_hook_ops *)ops_ptr;\n+\tunsigned int hooknum = ops-\u003ehooknum;\n+\tu8 pf = ops-\u003epf;\n+\n+\t/* same translation as nf_static_key_inc() */\n+\tif (pf == NFPROTO_INET \u0026\u0026 hooknum == NF_INET_INGRESS) {\n+\t\tpf = NFPROTO_NETDEV;\n+\t\thooknum = NF_NETDEV_INGRESS;\n+\t}\n+\n+\tif (entry-\u003estate.hook != hooknum)\n+\t\treturn 0;\n+\n+\tif (pf == NFPROTO_INET)\n+\t\treturn entry-\u003estate.pf == NFPROTO_IPV4 ||\n+\t\t entry-\u003estate.pf == NFPROTO_IPV6;\n+\n+\treturn entry-\u003estate.pf == pf;\n+}\n+\n+static void nfqnl_nf_hook_drop(struct net *net, const struct nf_hook_ops *ops)\n {\n \tstruct nfnl_queue_net *q = nfnl_queue_pernet(net);\n+\tnfqnl_cmpfn cmpfn = NULL;\n \tint i;\n \n \t/* This function is also called on net namespace error unwind,\n@@ -1574,12 +1604,19 @@ static void nfqnl_nf_hook_drop(struct net *net)\n \tif (!q)\n \t\treturn;\n \n+\t/* ops is NULL when the caller is not unregistering a hook but\n+\t * removing an object queued packets may reference, every entry\n+\t * has to go\n+\t */\n+\tif (ops)\n+\t\tcmpfn = nf_hook_cmp;\n+\n \tfor (i = 0; i \u003c INSTANCE_BUCKETS; i++) {\n \t\tstruct nfqnl_instance *inst;\n \t\tstruct hlist_head *head = \u0026q-\u003einstance_table[i];\n \n \t\thlist_for_each_entry_rcu(inst, head, hlist)\n-\t\t\tnfqnl_flush(inst, NULL, 0);\n+\t\t\tnfqnl_flush(inst, cmpfn, (unsigned long)ops);\n \t}\n }\n \ndiff --git a/net/netfilter/nft_chain_nat.c b/net/netfilter/nft_chain_nat.c\nindex 40e230d8b712e..1133f79d8d253 100644\n--- a/net/netfilter/nft_chain_nat.c\n+++ b/net/netfilter/nft_chain_nat.c\n@@ -131,6 +131,7 @@ static void __exit nft_chain_nat_exit(void)\n #ifdef CONFIG_NF_TABLES_INET\n \tnft_unregister_chain_type(\u0026nft_chain_nat_inet);\n #endif\n+\tsynchronize_rcu();\n }\n \n module_init(nft_chain_nat_init);\ndiff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c\nindex 63864b9282590..88ddaf47f5879 100644\n--- a/net/netfilter/nft_compat.c\n+++ b/net/netfilter/nft_compat.c\n@@ -14,6 +14,7 @@\n #include \u003clinux/netfilter/nf_tables.h\u003e\n #include \u003clinux/netfilter/nf_tables_compat.h\u003e\n #include \u003clinux/netfilter/x_tables.h\u003e\n+#include \u003clinux/netfilter_ipv4.h\u003e\n #include \u003clinux/netfilter_ipv4/ip_tables.h\u003e\n #include \u003clinux/netfilter_ipv6/ip6_tables.h\u003e\n #include \u003clinux/netfilter_bridge/ebtables.h\u003e\n@@ -45,6 +46,20 @@ static int nft_compat_chain_validate_dependency(const struct nft_ctx *ctx,\n \t\t\ttype = NFT_CHAIN_T_NAT;\n \t\tif (basechain-\u003etype-\u003etype != type)\n \t\t\treturn -EINVAL;\n+\t} else if (strcmp(tablename, \"raw\") == 0) {\n+\t\t/* iptables-nft emulates the legacy raw table, which only\n+\t\t * ever has prerouting/output chains at this exact priority.\n+\t\t */\n+\t\tif (ctx-\u003efamily != NFPROTO_IPV4 \u0026\u0026\n+\t\t ctx-\u003efamily != NFPROTO_IPV6)\n+\t\t\treturn -EINVAL;\n+\n+\t\tif (basechain-\u003eops.hooknum != NF_INET_PRE_ROUTING \u0026\u0026\n+\t\t basechain-\u003eops.hooknum != NF_INET_LOCAL_OUT)\n+\t\t\treturn -EINVAL;\n+\n+\t\tif (basechain-\u003eops.priority != NF_IP_PRI_RAW)\n+\t\t\treturn -EINVAL;\n \t}\n \n \treturn 0;\ndiff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c\nindex 3c4c2faa73984..5a8db55920364 100644\n--- a/net/netfilter/nft_ct.c\n+++ b/net/netfilter/nft_ct.c\n@@ -542,7 +542,7 @@ static void __nft_ct_set_destroy(const struct nft_ctx *ctx, struct nft_ct *priv)\n #endif\n #ifdef CONFIG_NF_CONNTRACK_ZONES\n \tcase NFT_CT_ZONE:\n-\t\tnf_queue_nf_hook_drop(ctx-\u003enet);\n+\t\tnf_queue_nf_hook_drop(ctx-\u003enet, NULL);\n \t\tmutex_lock(\u0026nft_ct_pcpu_mutex);\n \t\tif (--nft_ct_pcpu_template_refcnt == 0)\n \t\t\tnft_ct_tmpl_put_pcpu();\ndiff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c\nindex 8abf8e8145ce5..861bab93f4352 100644\n--- a/net/netfilter/nft_exthdr.c\n+++ b/net/netfilter/nft_exthdr.c\n@@ -233,16 +233,23 @@ static void nft_exthdr_tcp_set_eval(const struct nft_expr *expr,\n \t\t\t\t struct nft_regs *regs,\n \t\t\t\t const struct nft_pktinfo *pkt)\n {\n-\tu8 buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE];\n \tstruct nft_exthdr *priv = nft_expr_priv(expr);\n \tunsigned int i, optl, tcphdr_len, offset;\n-\tstruct tcphdr *tcph;\n+\tstruct tcphdr *tcph, _tcph;\n \tu8 *opt;\n \n-\ttcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff, \u0026tcphdr_len);\n+\tif (pkt-\u003etprot != IPPROTO_TCP || pkt-\u003efragoff)\n+\t\tgoto err;\n+\n+\ttcph = skb_header_pointer(pkt-\u003eskb, nft_thoff(pkt), sizeof(_tcph), \u0026_tcph);\n \tif (!tcph)\n \t\tgoto err;\n \n+\ttcphdr_len = __tcp_hdrlen(tcph);\n+\tif (tcphdr_len \u003c sizeof(*tcph) ||\n+\t tcphdr_len \u003e sizeof(*tcph) + MAX_TCP_OPTION_SPACE)\n+\t\tgoto err;\n+\n \tif (skb_ensure_writable(pkt-\u003eskb, nft_thoff(pkt) + tcphdr_len))\n \t\tgoto err;\n \n@@ -313,16 +320,23 @@ static void nft_exthdr_tcp_strip_eval(const struct nft_expr *expr,\n \t\t\t\t struct nft_regs *regs,\n \t\t\t\t const struct nft_pktinfo *pkt)\n {\n-\tu8 buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE];\n \tstruct nft_exthdr *priv = nft_expr_priv(expr);\n \tunsigned int i, tcphdr_len, optl;\n-\tstruct tcphdr *tcph;\n+\tstruct tcphdr *tcph, _tcph;\n \tu8 *opt;\n \n-\ttcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff, \u0026tcphdr_len);\n+\tif (pkt-\u003etprot != IPPROTO_TCP || pkt-\u003efragoff)\n+\t\tgoto err;\n+\n+\ttcph = skb_header_pointer(pkt-\u003eskb, nft_thoff(pkt), sizeof(_tcph), \u0026_tcph);\n \tif (!tcph)\n \t\tgoto err;\n \n+\ttcphdr_len = __tcp_hdrlen(tcph);\n+\tif (tcphdr_len \u003c sizeof(*tcph) ||\n+\t tcphdr_len \u003e sizeof(*tcph) + MAX_TCP_OPTION_SPACE)\n+\t\tgoto err;\n+\n \tif (skb_ensure_writable(pkt-\u003eskb, nft_thoff(pkt) + tcphdr_len))\n \t\tgoto drop;\n \n@@ -551,7 +565,7 @@ static int nft_exthdr_tcp_set_init(const struct nft_ctx *ctx,\n \t\t\t\t const struct nlattr * const tb[])\n {\n \tstruct nft_exthdr *priv = nft_expr_priv(expr);\n-\tu32 offset, len, flags = 0, op = NFT_EXTHDR_OP_IPV6;\n+\tu32 offset, len, flags = 0;\n \tint err;\n \n \tif (!tb[NFTA_EXTHDR_SREG] ||\n@@ -581,15 +595,11 @@ static int nft_exthdr_tcp_set_init(const struct nft_ctx *ctx,\n \t\treturn -EOPNOTSUPP;\n \t}\n \n-\terr = nft_parse_u32_check(tb[NFTA_EXTHDR_OP], U8_MAX, \u0026op);\n-\tif (err \u003c 0)\n-\t\treturn err;\n-\n \tpriv-\u003etype = nla_get_u8(tb[NFTA_EXTHDR_TYPE]);\n \tpriv-\u003eoffset = offset;\n \tpriv-\u003elen = len;\n \tpriv-\u003eflags = flags;\n-\tpriv-\u003eop = op;\n+\tpriv-\u003eop = NFT_EXTHDR_OP_TCPOPT;\n \n \treturn nft_parse_register_load(ctx, tb[NFTA_EXTHDR_SREG], \u0026priv-\u003esreg,\n \t\t\t\t priv-\u003elen);\ndiff --git a/tools/testing/selftests/net/netfilter/conntrack_vrf.sh b/tools/testing/selftests/net/netfilter/conntrack_vrf.sh\nindex 207b79932d91a..2dbfabaadf4bd 100755\n--- a/tools/testing/selftests/net/netfilter/conntrack_vrf.sh\n+++ b/tools/testing/selftests/net/netfilter/conntrack_vrf.sh\n@@ -84,7 +84,7 @@ busywait $BUSYWAIT_TIMEOUT listener_ready \"$ns1\"\n # as decided by the first iteration of the ruleset.\n test_ct_zone_in()\n {\n-ip netns exec \"$ns0\" nft -f - \u003c\u003cEOF\n+if ! ip netns exec \"$ns0\" nft -f - \u003c\u003cEOF\n table testct {\n \tchain rawpre {\n \t\ttype filter hook prerouting priority raw;\n@@ -105,6 +105,12 @@ table testct {\n \t}\n }\n EOF\n+\tthen\n+\t\techo \"FAIL: cannot load testct nft ruleset\"\n+\t\tret=1\n+\t\treturn\n+\tfi\n+\n \tip netns exec \"$ns1\" ping -W 1 -c 1 -I veth0 \"$IP0\" \u003e /dev/null\n \n \t# should be in zone 1, not zone 2\n@@ -113,6 +119,7 @@ EOF\n \t\techo \"PASS: entry found in conntrack zone 1\"\n \telse\n \t\techo \"FAIL: entry not found in conntrack zone 1\"\n+\t\tret=1\n \t\tcount=$(ip netns exec \"$ns0\" conntrack -L -s $IP1 -d $IP0 -p icmp --zone 2 2\u003e /dev/null | wc -l)\n \t\tif [ \"$count\" -eq 1 ]; then\n \t\t\techo \"FAIL: entry found in zone 2 instead\"\n@@ -137,7 +144,7 @@ test_masquerade_vrf()\n \n \tip netns exec \"$ns0\" conntrack -F 2\u003e/dev/null\n \n-ip netns exec \"$ns0\" nft -f - \u003c\u003cEOF\n+if ! ip netns exec \"$ns0\" nft -f - \u003c\u003cEOF\n flush ruleset\n table ip nat {\n \tchain rawout {\n@@ -158,6 +165,12 @@ table ip nat {\n \t}\n }\n EOF\n+\tthen\n+\t\techo \"FAIL: cannot load ip nat nft ruleset\"\n+\t\tret=1\n+\t\treturn\n+\tfi\n+\n \tif ! ip netns exec \"$ns0\" ip vrf exec tvrf socat -u -4 STDIN TCP:\"$IP1\":55555 \u003c /dev/null \u003e /dev/null;then\n \t\techo \"FAIL: connect failure with masquerade + sport rewrite on vrf device\"\n \t\tret=1\n@@ -184,7 +197,7 @@ EOF\n test_masquerade_veth()\n {\n \tip netns exec \"$ns0\" conntrack -F 2\u003e/dev/null\n-ip netns exec \"$ns0\" nft -f - \u003c\u003cEOF\n+if ! ip netns exec \"$ns0\" nft -f - \u003c\u003cEOF\n flush ruleset\n table ip nat {\n \tchain postrouting {\n@@ -193,6 +206,12 @@ table ip nat {\n \t}\n }\n EOF\n+\tthen\n+\t\techo \"FAIL: cannot load ip nat postrouting ruleset\"\n+\t\tret=1\n+\t\treturn\n+\tfi\n+\n \tif ! ip netns exec \"$ns0\" ip vrf exec tvrf socat -u -4 STDIN TCP:\"$IP1\":55555 \u003c /dev/null \u003e /dev/null;then\n \t\techo \"FAIL: connect failure with masquerade + sport rewrite on veth device\"\n \t\tret=1\ndiff --git a/tools/testing/selftests/net/netfilter/nf_queue.c b/tools/testing/selftests/net/netfilter/nf_queue.c\nindex 8bbec37f53563..cca6c3cfa9e46 100644\n--- a/tools/testing/selftests/net/netfilter/nf_queue.c\n+++ b/tools/testing/selftests/net/netfilter/nf_queue.c\n@@ -1,6 +1,7 @@\n // SPDX-License-Identifier: GPL-2.0\n \n #include \u003cerrno.h\u003e\n+#include \u003csignal.h\u003e\n #include \u003cstdbool.h\u003e\n #include \u003cstdio.h\u003e\n #include \u003cstdint.h\u003e\n@@ -21,6 +22,7 @@ struct options {\n \tbool failopen;\n \tbool out_of_order;\n \tbool bogus_verdict;\n+\tbool hold;\n \tint verbose;\n \tunsigned int queue_num;\n \tunsigned int timeout;\n@@ -34,6 +36,7 @@ static struct options opts;\n static void help(const char *p)\n {\n \tprintf(\"Usage: %s [-c|-v [-vv] ] [-o] [-O] [-b] [-t timeout] [-q queue_num] [-Qdst_queue ] [ -d ms_delay ] [-G]\\n\", p);\n+\tprintf(\" [-H] (hold verdicts until SIGUSR1 is received)\\n\");\n }\n \n static int parse_attr_cb(const struct nlattr *attr, void *data)\n@@ -280,6 +283,7 @@ static int mainloop(void)\n \tuint32_t ooo_ids[16];\n \tunsigned int portid;\n \tint ooo_count = 0;\n+\tsigset_t hold_set;\n \tchar *buf;\n \tint ret;\n \n@@ -289,6 +293,12 @@ static int mainloop(void)\n \t\texit(EXIT_FAILURE);\n \t}\n \n+\t/* -H: keep SIGUSR1 pending until the first packet is read */\n+\tsigemptyset(\u0026hold_set);\n+\tsigaddset(\u0026hold_set, SIGUSR1);\n+\tif (opts.hold)\n+\t\tsigprocmask(SIG_BLOCK, \u0026hold_set, NULL);\n+\n \tnl = open_queue();\n \tportid = mnl_socket_get_portid(nl);\n \n@@ -320,6 +330,13 @@ static int mainloop(void)\n \t\t}\n \n \t\tid = ret - MNL_CB_OK;\n+\t\tif (opts.hold) {\n+\t\t\tint sig;\n+\n+\t\t\tsigwait(\u0026hold_set, \u0026sig);\n+\t\t\topts.hold = false;\n+\t\t}\n+\n \t\tif (opts.delay_ms)\n \t\t\tsleep_ms(opts.delay_ms);\n \n@@ -364,7 +381,7 @@ static void parse_opts(int argc, char **argv)\n {\n \tint c;\n \n-\twhile ((c = getopt(argc, argv, \"chvoObt:q:Q:d:G\")) != -1) {\n+\twhile ((c = getopt(argc, argv, \"chvoObt:q:Q:d:GH\")) != -1) {\n \t\tswitch (c) {\n \t\tcase 'c':\n \t\t\topts.count_packets = true;\n@@ -373,6 +390,9 @@ static void parse_opts(int argc, char **argv)\n \t\t\thelp(argv[0]);\n \t\t\texit(0);\n \t\t\tbreak;\n+\t\tcase 'H':\n+\t\t\topts.hold = true;\n+\t\t\tbreak;\n \t\tcase 'q':\n \t\t\topts.queue_num = atoi(optarg);\n \t\t\tif (opts.queue_num \u003e 0xffff)\ndiff --git a/tools/testing/selftests/net/netfilter/nft_queue.sh b/tools/testing/selftests/net/netfilter/nft_queue.sh\nindex 7c857a2e0f349..a07652813a4b0 100755\n--- a/tools/testing/selftests/net/netfilter/nft_queue.sh\n+++ b/tools/testing/selftests/net/netfilter/nft_queue.sh\n@@ -92,7 +92,7 @@ load_ruleset() {\n ip netns exec \"$nsrouter\" nft -f /dev/stdin \u003c\u003cEOF\n table $family $name {\n \tchain nfq {\n-\t\tip protocol icmp queue bypass\n+\t\ticmp type { \"echo-request\", \"echo-reply\" } queue bypass\n \t\ticmpv6 type { \"echo-request\", \"echo-reply\" } queue num 1 bypass\n \t}\n \tchain pre {\n@@ -861,6 +861,113 @@ test_queue_bridge()\n \ttest_queue 20 \"bridge\"\n }\n \n+hook_drop_delivered()\n+{\n+\tip netns exec \"$ns1\" nft list counter inet nfqscope delivered |\n+\t\tsed -n 's/.*packets \\([0-9]*\\) .*/\\1/p'\n+}\n+\n+hook_drop_queued()\n+{\n+\tip netns exec \"$ns1\" awk '$1 == 1 { print $3 }' /proc/self/net/netfilter/nfnetlink_queue\n+}\n+\n+hook_drop_all_queued()\n+{\n+\t[ \"$(hook_drop_queued)\" = \"$1\" ]\n+}\n+\n+# hook_drop_case \u003ckeep|drop\u003e \u003cdescription\u003e \u003cnft command...\u003e\n+#\n+# 1. The inet output chain sends udp packets to port 12345 to queue 1,\n+# the inet input chain counts them once they are accepted.\n+# 2. nf_queue -H reads the packets but sends no verdict until it gets\n+# SIGUSR1, so the 20 packets sent stay in the kernel queue.\n+# 3. The nft command removes a base chain while they are queued. The\n+# queue length in /proc right after it tells if the kernel dropped\n+# them: still 20, or 0.\n+# 4. SIGUSR1 releases nf_queue. If the kernel kept the packets they are\n+# accepted now and reach the input counter. If it dropped them the\n+# first verdict fails with ENOENT and nothing is counted.\n+hook_drop_case()\n+{\n+\tlocal expect=\"$1\"\n+\tlocal desc=\"$2\"\n+\tlocal packets=20\n+\tlocal delivered queued result\n+\tshift 2\n+\n+ip netns exec \"$ns1\" nft -f /dev/stdin \u003c\u003cEOF\n+flush ruleset\n+table inet nfqscope {\n+\tchain output {\n+\t\ttype filter hook output priority 0; policy accept;\n+\t\tudp dport 12345 queue num 1\n+\t}\n+\tchain input {\n+\t\ttype filter hook input priority 0; policy accept;\n+\t\tudp dport 12345 counter name delivered\n+\t}\n+\tchain output2 {\n+\t\ttype filter hook output priority 100; policy accept;\n+\t}\n+\tcounter delivered {}\n+}\n+table ip6 unrelated {\n+\tchain prerouting {\n+\t\ttype filter hook prerouting priority 0; policy accept;\n+\t}\n+}\n+EOF\n+\tip netns exec \"$ns1\" ./nf_queue -c -q 1 -H -t \"$timeout\" \u003e \"$TMPFILE1\" 2\u003e\u00261 \u0026\n+\tlocal nfqpid=$!\n+\n+\tbusywait \"$BUSYWAIT_TIMEOUT\" nf_queue_wait \"$ns1\" 1\n+\n+\tip netns exec \"$ns1\" bash -c \\\n+\t\t\"for i in \\$(seq $packets); do echo x \u003e /dev/udp/127.0.0.1/12345; done\"\n+\n+\tbusywait \"$BUSYWAIT_TIMEOUT\" hook_drop_all_queued \"$packets\"\n+\n+\tip netns exec \"$ns1\" nft \"$@\"\n+\tqueued=$(hook_drop_queued)\n+\n+\tkill -USR1 \"$nfqpid\"\n+\twait \"$nfqpid\"\n+\tdelivered=$(hook_drop_delivered)\n+\n+\tif [ \"$queued\" = \"$packets\" ] \u0026\u0026 [ \"$delivered\" = \"$packets\" ] \u0026\u0026\n+\t grep -q \"^$packets packets total\" \"$TMPFILE1\"; then\n+\t\tresult=\"keep\"\n+\telif [ \"$queued\" = \"0\" ] \u0026\u0026 [ \"$delivered\" = \"0\" ]; then\n+\t\tresult=\"drop\"\n+\telse\n+\t\tresult=\"inconsistent\"\n+\tfi\n+\n+\tif [ \"$result\" = \"$expect\" ]; then\n+\t\techo \"PASS: $desc: queued packets $result\"\n+\telse\n+\t\techo \"FAIL: $desc: queued packets $result, expected $expect\" 1\u003e\u00262\n+\t\techo \" $queued of $packets queued after the change, $delivered delivered\" 1\u003e\u00262\n+\t\tret=1\n+\tfi\n+}\n+\n+test_hook_drop_scope()\n+{\n+\t# the hook array of another hook point changes, queued packets stay\n+\thook_drop_case keep \"base chain removed at another hook point\" \\\n+\t\tdelete chain ip6 unrelated prerouting\n+\n+\t# the hook array of the queueing hook point changes, the position of\n+\t# the queued packets in it is stale, they are dropped\n+\thook_drop_case drop \"base chain removed at the queueing hook point\" \\\n+\t\tdelete chain inet nfqscope output2\n+\n+\tip netns exec \"$ns1\" nft flush ruleset\n+}\n+\n ip netns exec \"$nsrouter\" sysctl net.ipv6.conf.all.forwarding=1 \u003e /dev/null\n ip netns exec \"$nsrouter\" sysctl net.ipv4.conf.veth0.forwarding=1 \u003e /dev/null\n ip netns exec \"$nsrouter\" sysctl net.ipv4.conf.veth1.forwarding=1 \u003e /dev/null\n@@ -909,6 +1016,7 @@ test_queue_stress\n # should be last, adds vrf device in ns1 and changes routes\n test_icmp_vrf\n test_queue_removal\n+test_hook_drop_scope\n \n # turns router into a bridge\n test_queue_bridge\n"
} |
| 2/1 | 2026/10/18 01:13 | agent | patch-evaluator |
6mModel:core Args: null Results: {
"EnableConfigs": [
"BRIDGE_NETFILTER",
"IP_VS",
"IPV6_TUNNEL",
"NET_IPIP",
"NETFILTER_NETLINK_QUEUE",
"NF_FLOW_TABLE",
"NF_TABLES",
"NFT_COMPAT"
],
"FocusSymbols": [
"brnf_device_event",
"ip_vs_sync_conn",
"nf_flow_tunnel_push",
"nf_queue_nf_hook_drop",
"nfqnl_nf_hook_drop",
"nft_chain_parse_hook",
"nft_compat_chain_validate_dependency",
"nft_exthdr_tcp_set_eval",
"nft_exthdr_tcp_set_init",
"nft_exthdr_tcp_strip_eval"
],
"Reasoning": "The patch series touches several reachable networking and netfilter subsystems, including nf_tables (chain type reference counting and lookup, TCP exthdr validation and initialization), nfnetlink queue (selective hook dropping upon hook unregistration), bridge netfilter (disabling in non-init user namespaces), IPVS (data race fixes in connection sync), and flowtable (tunnel push/pop refactoring and protocol encapsulation for IPIP and IPv6 tunnels). All modified code paths are fully reachable via netlink, network namespaces, and network traffic in standard virtualized environments.",
"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 07d7ac72f49783118181ec85a3e13f4dc7c70a24
Author: syz-cluster <triage@syzkaller.com>
Date: Sun Oct 18 01:13:21 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/networking/bridge.rst b/Documentation/networking/bridge.rst
index c1e6ea52c9e59..dded1a6edd606 100644
--- a/Documentation/networking/bridge.rst
+++ b/Documentation/networking/bridge.rst
@@ -277,8 +277,9 @@ Netfilter
=========
The bridge netfilter module is a legacy feature that allows to filter bridged
-packets with iptables and ip6tables. Its use is discouraged. Users should
-consider using nftables for packet filtering.
+packets with iptables and ip6tables. This feature is scheduled for removal and
+not available in user namespaces. Consider using nftables for packet filtering
+instead.
The older ebtables tool is more feature-limited compared to nftables, but
just like nftables it doesn't need this module either to function.
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index f61c972313095..fee3728ad6663 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -911,6 +911,7 @@ struct net_device_path {
};
u8 inner_proto;
+ u8 encap_proto;
} tun;
struct {
enum {
diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h
index 5b611efaa3cd1..5b22d31cb1407 100644
--- a/include/net/netfilter/nf_flow_table.h
+++ b/include/net/netfilter/nf_flow_table.h
@@ -118,6 +118,7 @@ struct flow_offload_tunnel {
};
u8 inner_proto;
+ u8 encap_proto;
};
struct flow_offload_tuple {
diff --git a/include/net/netfilter/nf_queue.h b/include/net/netfilter/nf_queue.h
index fc3e81c07364f..86021c55ba3ce 100644
--- a/include/net/netfilter/nf_queue.h
+++ b/include/net/netfilter/nf_queue.h
@@ -35,7 +35,8 @@ struct nf_queue_entry {
struct nf_queue_handler {
int (*outfn)(struct nf_queue_entry *entry,
unsigned int queuenum);
- void (*nf_hook_drop)(struct net *net);
+ void (*nf_hook_drop)(struct net *net,
+ const struct nf_hook_ops *ops);
};
void nf_register_queue_handler(const struct nf_queue_handler *qh);
diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c
index 0a394e5f43916..e15355ff215a7 100644
--- a/net/bridge/br_netfilter_hooks.c
+++ b/net/bridge/br_netfilter_hooks.c
@@ -54,6 +54,7 @@ static unsigned int brnf_net_id __read_mostly;
struct brnf_net {
bool enabled;
+ bool in_userns;
#ifdef CONFIG_SYSCTL
struct ctl_table_header *ctl_hdr;
@@ -1100,7 +1101,7 @@ static int brnf_device_event(struct notifier_block *unused, unsigned long event,
net = dev_net(dev);
brnet = net_generic(net, brnf_net_id);
- if (brnet->enabled)
+ if (brnet->enabled || brnet->in_userns)
return NOTIFY_OK;
ret = nf_register_net_hooks(net, br_nf_ops, ARRAY_SIZE(br_nf_ops));
@@ -1229,9 +1230,11 @@ static inline void br_netfilter_sysctl_default(struct brnf_net *brnf)
brnf->filter_pppoe_tagged = 0;
brnf->pass_vlan_indev = 0;
}
+#endif
static int br_netfilter_sysctl_init_net(struct net *net)
{
+#ifdef CONFIG_SYSCTL
struct ctl_table *table = brnf_table;
struct brnf_net *brnet;
@@ -1259,25 +1262,39 @@ static int br_netfilter_sysctl_init_net(struct net *net)
return -ENOMEM;
}
-
+#endif
return 0;
}
static void br_netfilter_sysctl_exit_net(struct net *net,
struct brnf_net *brnet)
{
- const struct ctl_table *table = brnet->ctl_hdr->ctl_table_arg;
+#ifdef CONFIG_SYSCTL
+ struct ctl_table_header *header = brnet->ctl_hdr;
+ const struct ctl_table *table;
- unregister_net_sysctl_table(brnet->ctl_hdr);
+ if (!header)
+ return;
+
+ table = header->ctl_table_arg;
+ unregister_net_sysctl_table(header);
if (!net_eq(net, &init_net))
kfree(table);
+#endif
}
static int __net_init brnf_init_net(struct net *net)
{
+ if (net->user_ns != &init_user_ns) {
+ struct brnf_net *brnet = net_generic(net, brnf_net_id);
+
+ brnet->in_userns = true;
+ pr_warn_once("br_netfilter no longer supported in user namespaces\n");
+ return 0;
+ }
+
return br_netfilter_sysctl_init_net(net);
}
-#endif
static void __net_exit brnf_exit_net(struct net *net)
{
@@ -1289,15 +1306,11 @@ static void __net_exit brnf_exit_net(struct net *net)
brnet->enabled = false;
}
-#ifdef CONFIG_SYSCTL
br_netfilter_sysctl_exit_net(net, brnet);
-#endif
}
static struct pernet_operations brnf_net_ops __read_mostly = {
-#ifdef CONFIG_SYSCTL
.init = brnf_init_net,
-#endif
.exit = brnf_exit_net,
.id = &brnf_net_id,
.size = sizeof(struct brnf_net),
@@ -1318,7 +1331,7 @@ static int __init br_netfilter_init(void)
}
RCU_INIT_POINTER(nf_br_ops, &br_ops);
- printk(KERN_NOTICE "Bridge firewalling registered\n");
+ pr_warn("br_netfilter is slated for removal in 2027.\n");
return 0;
}
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c
index 2ffe64e736e08..15189e1fa0682 100644
--- a/net/ipv4/ipip.c
+++ b/net/ipv4/ipip.c
@@ -379,6 +379,7 @@ static int ipip_fill_forward_path(struct net_device_path_ctx *ctx,
path->tun.src_v4.s_addr = tiph->saddr;
path->tun.dst_v4.s_addr = tiph->daddr;
path->tun.inner_proto = IPPROTO_IPIP;
+ path->tun.encap_proto = AF_INET;
path->tun.dst = &rt->dst;
path->dev = ctx->dev;
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index d5ff50a2ac017..64a184bca4677 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -1868,6 +1868,7 @@ static int ip6_tnl_fill_forward_path(struct net_device_path_ctx *ctx,
path->tun.src_v6 = fl6.saddr;
path->tun.dst_v6 = fl6.daddr;
path->tun.inner_proto = IPPROTO_IPV6;
+ path->tun.encap_proto = AF_INET6;
path->tun.dst = dst;
path->dev = ctx->dev;
ctx->dev = dst->dev;
diff --git a/net/netfilter/core.c b/net/netfilter/core.c
index 940dea2663e95..44c856c1e621c 100644
--- a/net/netfilter/core.c
+++ b/net/netfilter/core.c
@@ -529,7 +529,7 @@ static void __nf_unregister_net_hook(struct net *net, int pf,
if (!p)
return;
- nf_queue_nf_hook_drop(net);
+ nf_queue_nf_hook_drop(net, reg);
nf_hook_entries_free(p);
}
diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c
index 45655cff078cb..157f5c8361ab4 100644
--- a/net/netfilter/ipvs/ip_vs_sync.c
+++ b/net/netfilter/ipvs/ip_vs_sync.c
@@ -543,13 +543,15 @@ static void ip_vs_sync_conn_v0(struct netns_ipvs *ipvs, struct ip_vs_conn *cp,
struct ip_vs_sync_conn_v0 *s;
struct ip_vs_sync_buff *buff;
struct ipvs_master_sync_state *ms;
- int id;
+ u32 flags, seq_mask;
unsigned int len;
+ int id;
if (unlikely(cp->af != AF_INET))
return;
+ flags = READ_ONCE(cp->flags);
/* Do not sync ONE PACKET */
- if (cp->flags & IP_VS_CONN_F_ONE_PACKET)
+ if (flags & IP_VS_CONN_F_ONE_PACKET)
return;
if (!ip_vs_sync_conn_needed(ipvs, cp, pkts))
@@ -564,8 +566,8 @@ static void ip_vs_sync_conn_v0(struct netns_ipvs *ipvs, struct ip_vs_conn *cp,
id = select_master_thread_id(ipvs, cp);
ms = &ipvs->ms[id];
buff = ms->sync_buff;
- len = (cp->flags & IP_VS_CONN_F_SEQ_MASK) ? FULL_CONN_SIZE :
- SIMPLE_CONN_SIZE;
+ seq_mask = flags & IP_VS_CONN_F_SEQ_MASK;
+ len = seq_mask ? FULL_CONN_SIZE : SIMPLE_CONN_SIZE;
if (buff) {
m = (struct ip_vs_sync_mesg_v0 *) buff->mesg;
/* Send buffer if it is for v1 */
@@ -597,9 +599,9 @@ static void ip_vs_sync_conn_v0(struct netns_ipvs *ipvs, struct ip_vs_conn *cp,
s->caddr = cp->caddr.ip;
s->vaddr = cp->vaddr.ip;
s->daddr = cp->daddr.ip;
- s->flags = htons(cp->flags & ~IP_VS_CONN_F_HASHED);
+ s->flags = htons(flags & ~IP_VS_CONN_F_HASHED);
s->state = htons(cp->state);
- if (cp->flags & IP_VS_CONN_F_SEQ_MASK) {
+ if (seq_mask) {
struct ip_vs_sync_conn_options *opt =
(struct ip_vs_sync_conn_options *)&s[1];
memcpy(opt, &cp->sync_conn_opt, sizeof(*opt));
@@ -635,6 +637,7 @@ void ip_vs_sync_conn(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, int pkts)
int id;
__u8 *p;
unsigned int len, pe_name_len, pad;
+ u32 flags, seq_mask;
/* Handle old version of the protocol */
if (sysctl_sync_ver(ipvs) == 0) {
@@ -647,6 +650,7 @@ void ip_vs_sync_conn(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, int pkts)
sloop:
if (!ip_vs_sync_conn_needed(ipvs, cp, pkts))
goto control;
+ flags = READ_ONCE(cp->flags);
/* Sanity checks */
pe_name_len = 0;
@@ -674,7 +678,8 @@ void ip_vs_sync_conn(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, int pkts)
#endif
len = sizeof(struct ip_vs_sync_v4);
- if (cp->flags & IP_VS_CONN_F_SEQ_MASK)
+ seq_mask = flags & IP_VS_CONN_F_SEQ_MASK;
+ if (seq_mask)
len += sizeof(struct ip_vs_sync_conn_options) + 2;
if (cp->pe_data_len)
@@ -720,7 +725,7 @@ void ip_vs_sync_conn(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, int pkts)
/* Set message type & copy members */
s->v4.type = (cp->af == AF_INET6 ? STYPE_F_INET6 : 0);
s->v4.ver_size = htons(len & SVER_MASK); /* Version 0 */
- s->v4.flags = htonl(cp->flags & ~IP_VS_CONN_F_HASHED);
+ s->v4.flags = htonl(flags & ~IP_VS_CONN_F_HASHED);
s->v4.state = htons(cp->state);
s->v4.protocol = cp->protocol;
s->v4.cport = cp->cport;
@@ -744,7 +749,7 @@ void ip_vs_sync_conn(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, int pkts)
s->v4.vaddr = cp->vaddr.ip;
s->v4.daddr = cp->daddr.ip;
}
- if (cp->flags & IP_VS_CONN_F_SEQ_MASK) {
+ if (seq_mask) {
*(p++) = IPVS_OPT_SEQ_DATA;
*(p++) = sizeof(struct ip_vs_sync_conn_options);
hton_seq(&cp->in_seq, (struct ip_vs_seq *)p);
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index f9b8927327ba9..931b76aacd934 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -2410,7 +2410,7 @@ nf_ct_iterate_destroy(int (*iter)(struct nf_conn *i, void *data), void *data)
if (atomic_read(&cnet->count) == 0)
continue;
- nf_queue_nf_hook_drop(net);
+ nf_queue_nf_hook_drop(net, NULL);
}
up_read(&net_rwsem);
@@ -2506,6 +2506,11 @@ void nf_conntrack_cleanup_net_list(struct list_head *net_exit_list)
goto i_see_dead_people;
}
+ /* must wait for current scan loop, worker also looks at
+ * struct nf_conntrack_net, which is about to be free'd.
+ */
+ flush_work(&conntrack_gc_work.dwork.work);
+
list_for_each_entry(net, net_exit_list, exit_list) {
warn_on_keymap_list_leak(net);
nf_conntrack_ecache_pernet_fini(net);
diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c
index c8c29a9a16844..e13f9397e8cbd 100644
--- a/net/netfilter/nf_flow_table_ip.c
+++ b/net/netfilter/nf_flow_table_ip.c
@@ -189,22 +189,24 @@ static void nf_flow_tuple_encap(struct nf_flowtable_ctx *ctx,
break;
}
+ if (ctx->tun.inner_proto != IPPROTO_IPIP &&
+ ctx->tun.inner_proto != IPPROTO_IPV6)
+ return;
+
switch (ctx->ether_type) {
case htons(ETH_P_IP):
iph = (struct iphdr *)(skb_network_header(skb) + offset);
- if (ctx->tun.inner_proto == IPPROTO_IPIP) {
- tuple->tun.dst_v4.s_addr = iph->daddr;
- tuple->tun.src_v4.s_addr = iph->saddr;
- tuple->tun.inner_proto = IPPROTO_IPIP;
- }
+ tuple->tun.dst_v4.s_addr = iph->daddr;
+ tuple->tun.src_v4.s_addr = iph->saddr;
+ tuple->tun.inner_proto = ctx->tun.inner_proto;
+ tuple->tun.encap_proto = AF_INET;
break;
case htons(ETH_P_IPV6):
ip6h = (struct ipv6hdr *)(skb_network_header(skb) + offset);
- if (ctx->tun.inner_proto == IPPROTO_IPV6) {
- tuple->tun.dst_v6 = ip6h->daddr;
- tuple->tun.src_v6 = ip6h->saddr;
- tuple->tun.inner_proto = IPPROTO_IPV6;
- }
+ tuple->tun.dst_v6 = ip6h->daddr;
+ tuple->tun.src_v6 = ip6h->saddr;
+ tuple->tun.inner_proto = ctx->tun.inner_proto;
+ tuple->tun.encap_proto = AF_INET6;
break;
default:
break;
@@ -372,6 +374,11 @@ static void nf_flow_ip_tunnel_pop(struct nf_flowtable_ctx *ctx,
skb_pull(skb, ctx->tun.hdr_size);
skb_reset_network_header(skb);
+
+ if (ctx->tun.inner_proto == IPPROTO_IPIP)
+ skb->protocol = htons(ETH_P_IP);
+ else
+ skb->protocol = htons(ETH_P_IPV6);
}
static bool nf_flow_skb_encap_protocol(struct nf_flowtable_ctx *ctx,
@@ -580,23 +587,44 @@ static int nf_flow_pppoe_push(struct sk_buff *skb, u16 id,
return 0;
}
-static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb,
- struct flow_offload_tuple *tuple,
- struct dst_entry *dst, __be32 *ip_daddr)
+static int nf_flow_tunnel_ip_push(struct net *net, struct sk_buff *skb,
+ struct flow_offload_tuple *tuple,
+ struct dst_entry *dst,
+ union nf_inet_addr **ip_addr)
{
- struct iphdr *iph = (struct iphdr *)skb_network_header(skb);
- struct rtable *rt = dst_rtable(dst);
- u8 tos = iph->tos, ttl = iph->ttl;
- __be16 frag_off = iph->frag_off;
- u32 headroom = sizeof(*iph);
+ __be16 frag_off = 0;
+ struct iphdr *iph;
+ u8 tos = 0, ttl;
+ u32 headroom;
int err;
+ switch (tuple->tun.inner_proto) {
+ case IPPROTO_IPV6: {
+ struct ipv6hdr *ip6h;
+
+ ip6h = (struct ipv6hdr *)skb_network_header(skb);
+ tos = ipv6_get_dsfield(ip6h);
+ ttl = ip6h->hop_limit;
+ frag_off = htons(IP_DF);
+ break;
+ }
+ case IPPROTO_IPIP:
+ iph = (struct iphdr *)skb_network_header(skb);
+ frag_off = iph->frag_off;
+ tos = iph->tos;
+ ttl = iph->ttl;
+ break;
+ default:
+ return 0;
+ }
+
err = iptunnel_handle_offloads(skb, SKB_GSO_IPXIP4);
if (err)
return err;
- skb_set_inner_ipproto(skb, IPPROTO_IPIP);
- headroom += LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len;
+ skb_set_inner_ipproto(skb, tuple->tun.inner_proto);
+ headroom = sizeof(*iph) + LL_RESERVED_SPACE(dst->dev) +
+ dst->header_len;
err = skb_cow_head(skb, headroom);
if (err)
return err;
@@ -607,11 +635,12 @@ static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb,
/* Push down and install the IP header. */
skb_push(skb, sizeof(*iph));
skb_reset_network_header(skb);
+ skb->protocol = htons(ETH_P_IP);
iph = ip_hdr(skb);
iph->version = 4;
iph->ihl = sizeof(*iph) >> 2;
- iph->frag_off = ip_mtu_locked(&rt->dst) ? 0 : frag_off;
+ iph->frag_off = ip_mtu_locked(dst) ? 0 : frag_off;
iph->protocol = tuple->tun.inner_proto;
iph->tos = tos;
iph->daddr = tuple->tun.src_v4.s_addr;
@@ -621,81 +650,91 @@ static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb,
__ip_select_ident(net, iph, skb_shinfo(skb)->gso_segs ?: 1);
ip_send_check(iph);
- *ip_daddr = tuple->tun.src_v4.s_addr;
-
- return 0;
-}
-
-static int nf_flow_tunnel_v4_push(struct net *net, struct sk_buff *skb,
- struct flow_offload_tuple *tuple,
- struct dst_entry *dst, __be32 *ip_daddr)
-{
- if (tuple->tun_num)
- return nf_flow_tunnel_ipip_push(net, skb, tuple, dst, ip_daddr);
+ *ip_addr = (union nf_inet_addr *)&tuple->tun.src_v4;
return 0;
}
-static int nf_flow_tunnel_ip6ip6_push(struct net *net, struct sk_buff *skb,
- struct flow_offload_tuple *tuple,
- struct dst_entry *dst,
- struct in6_addr **ip6_daddr)
+static int nf_flow_tunnel_ip6_push(struct net *net, struct sk_buff *skb,
+ struct flow_offload_tuple *tuple,
+ struct dst_entry *dst,
+ union nf_inet_addr **ip_addr)
{
- struct ipv6hdr *ip6h = (struct ipv6hdr *)skb_network_header(skb);
- __u8 dsfield = ipv6_get_dsfield(ip6h);
- struct rtable *rt = dst_rtable(dst);
struct flowi6 fl6 = {
.daddr = tuple->tun.src_v6,
.saddr = tuple->tun.dst_v6,
- .flowi6_proto = IPPROTO_IPV6,
+ .flowi6_proto = tuple->tun.inner_proto,
};
- u8 hop_limit = ip6h->hop_limit;
+ u8 hop_limit, dsfield;
+ struct ipv6hdr *ip6h;
int err, mtu;
u32 headroom;
+ switch (tuple->tun.inner_proto) {
+ case IPPROTO_IPIP: {
+ struct iphdr *iph = (struct iphdr *)skb_network_header(skb);
+
+ dsfield = ipv4_get_dsfield(iph);
+ hop_limit = iph->ttl;
+ break;
+ }
+ case IPPROTO_IPV6:
+ ip6h = (struct ipv6hdr *)skb_network_header(skb);
+ dsfield = ipv6_get_dsfield(ip6h);
+ hop_limit = ip6h->hop_limit;
+ break;
+ default:
+ return 0;
+ }
+
err = iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6);
if (err)
return err;
- skb_set_inner_ipproto(skb, IPPROTO_IPV6);
- headroom = sizeof(*ip6h) + LL_RESERVED_SPACE(rt->dst.dev) +
- rt->dst.header_len;
+ skb_set_inner_ipproto(skb, tuple->tun.inner_proto);
+ headroom = sizeof(*ip6h) + LL_RESERVED_SPACE(dst->dev) +
+ dst->header_len;
err = skb_cow_head(skb, headroom);
if (err)
return err;
skb_scrub_packet(skb, true);
- mtu = dst_mtu(&rt->dst) - sizeof(*ip6h);
+ mtu = dst_mtu(dst) - sizeof(*ip6h);
mtu = max(mtu, IPV6_MIN_MTU);
skb_dst_update_pmtu_no_confirm(skb, mtu);
skb_push(skb, sizeof(*ip6h));
skb_reset_network_header(skb);
+ skb->protocol = htons(ETH_P_IPV6);
ip6h = ipv6_hdr(skb);
ip6_flow_hdr(ip6h, dsfield,
ip6_make_flowlabel(net, skb, fl6.flowlabel, true, &fl6));
ip6h->hop_limit = hop_limit;
- ip6h->nexthdr = IPPROTO_IPV6;
+ ip6h->nexthdr = tuple->tun.inner_proto;
ip6h->daddr = tuple->tun.src_v6;
ip6h->saddr = tuple->tun.dst_v6;
ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(*ip6h));
IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr);
- *ip6_daddr = &tuple->tun.src_v6;
+ *ip_addr = (union nf_inet_addr *)&tuple->tun.src_v6;
return 0;
}
-static int nf_flow_tunnel_v6_push(struct net *net, struct sk_buff *skb,
- struct flow_offload_tuple *tuple,
- struct dst_entry *dst,
- struct in6_addr **ip6_daddr)
+static int nf_flow_tunnel_push(struct net *net, struct sk_buff *skb,
+ struct flow_offload_tuple *tuple,
+ struct dst_entry *dst,
+ union nf_inet_addr **ip_addr)
{
- if (tuple->tun_num)
- return nf_flow_tunnel_ip6ip6_push(net, skb, tuple, dst, ip6_daddr);
-
- return 0;
+ switch (tuple->tun.encap_proto) {
+ case AF_INET:
+ return nf_flow_tunnel_ip_push(net, skb, tuple, dst, ip_addr);
+ case AF_INET6:
+ return nf_flow_tunnel_ip6_push(net, skb, tuple, dst, ip_addr);
+ default:
+ return 0;
+ }
}
static int nf_flow_encap_push(struct sk_buff *skb,
@@ -802,10 +841,10 @@ static int nf_flow_queue_xmit4(struct sk_buff *skb,
struct flow_offload_tuple *other_tuple;
enum flow_offload_tuple_dir dir;
struct nf_flow_xmit xmit = {};
+ union nf_inet_addr *ip_addr;
struct flow_offload *flow;
struct neighbour *neigh;
struct rtable *rt;
- __be32 ip_daddr;
if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) {
rt = dst_rtable(tuplehash->tuple.dst_cache);
@@ -818,10 +857,10 @@ static int nf_flow_queue_xmit4(struct sk_buff *skb,
dir = tuplehash->tuple.dir;
flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
other_tuple = &flow->tuplehash[!dir].tuple;
- ip_daddr = other_tuple->src_v4.s_addr;
+ ip_addr = (union nf_inet_addr *)&other_tuple->src_v4;
- if (nf_flow_tunnel_v4_push(state->net, skb, other_tuple,
- tuplehash->tuple.dst_cache, &ip_daddr) < 0)
+ if (nf_flow_tunnel_push(state->net, skb, other_tuple,
+ tuplehash->tuple.dst_cache, &ip_addr) < 0)
return NF_DROP;
switch (tuplehash->tuple.xmit_type) {
@@ -832,7 +871,7 @@ static int nf_flow_queue_xmit4(struct sk_buff *skb,
flow_offload_teardown(flow);
return NF_DROP;
}
- neigh = ip_neigh_gw4(rt->dst.dev, rt_nexthop(rt, ip_daddr));
+ neigh = ip_neigh_gw4(rt->dst.dev, rt_nexthop(rt, ip_addr->ip));
if (IS_ERR(neigh)) {
flow_offload_teardown(flow);
return NF_DROP;
@@ -1130,7 +1169,7 @@ static int nf_flow_queue_xmit6(struct sk_buff *skb,
struct flow_offload_tuple *other_tuple;
enum flow_offload_tuple_dir dir;
struct nf_flow_xmit xmit = {};
- struct in6_addr *ip6_daddr;
+ union nf_inet_addr *ip_addr;
struct flow_offload *flow;
struct neighbour *neigh;
struct rt6_info *rt;
@@ -1146,11 +1185,10 @@ static int nf_flow_queue_xmit6(struct sk_buff *skb,
dir = tuplehash->tuple.dir;
flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
other_tuple = &flow->tuplehash[!dir].tuple;
- ip6_daddr = &other_tuple->src_v6;
+ ip_addr = (union nf_inet_addr *)&other_tuple->src_v6;
- if (nf_flow_tunnel_v6_push(state->net, skb, other_tuple,
- tuplehash->tuple.dst_cache,
- &ip6_daddr) < 0)
+ if (nf_flow_tunnel_push(state->net, skb, other_tuple,
+ tuplehash->tuple.dst_cache, &ip_addr) < 0)
return NF_DROP;
switch (tuplehash->tuple.xmit_type) {
@@ -1161,7 +1199,7 @@ static int nf_flow_queue_xmit6(struct sk_buff *skb,
flow_offload_teardown(flow);
return NF_DROP;
}
- neigh = ip_neigh_gw6(rt->dst.dev, rt6_nexthop(rt, ip6_daddr));
+ neigh = ip_neigh_gw6(rt->dst.dev, rt6_nexthop(rt, &ip_addr->in6));
if (IS_ERR(neigh)) {
flow_offload_teardown(flow);
return NF_DROP;
diff --git a/net/netfilter/nf_flow_table_path.c b/net/netfilter/nf_flow_table_path.c
index d90013685bf12..841f099434d98 100644
--- a/net/netfilter/nf_flow_table_path.c
+++ b/net/netfilter/nf_flow_table_path.c
@@ -138,6 +138,7 @@ static int nft_dev_path_info(struct net_device_path_stack *stack,
info->tun.dst_v6 = path->tun.dst_v6;
info->tun.inner_proto = path->tun.inner_proto;
info->tun_dst = path->tun.dst;
+ info->tun.encap_proto = path->tun.encap_proto;
info->num_tuns++;
} else {
if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX)
@@ -249,6 +250,7 @@ static int nft_dev_forward_path(const struct nft_pktinfo *pkt,
route->tuple[!dir].in.tun.src_v6 = info.tun.dst_v6;
route->tuple[!dir].in.tun.dst_v6 = info.tun.src_v6;
route->tuple[!dir].in.tun.inner_proto = info.tun.inner_proto;
+ route->tuple[!dir].in.tun.encap_proto = info.tun.encap_proto;
route->tuple[!dir].in.num_tuns = info.num_tuns;
dst_release(route->tuple[dir].dst);
route->tuple[dir].dst = info.tun_dst;
diff --git a/net/netfilter/nf_internals.h b/net/netfilter/nf_internals.h
index 25403023060b6..ce5aff2cae718 100644
--- a/net/netfilter/nf_internals.h
+++ b/net/netfilter/nf_internals.h
@@ -24,7 +24,7 @@
#define CTA_FILTER_FLAG(ctattr) CTA_FILTER_F_ ## ctattr
/* nf_queue.c */
-void nf_queue_nf_hook_drop(struct net *net);
+void nf_queue_nf_hook_drop(struct net *net, const struct nf_hook_ops *ops);
/* nf_log.c */
int __init netfilter_log_init(void);
diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c
index 73363ceedebe3..166d05e994ac6 100644
--- a/net/netfilter/nf_queue.c
+++ b/net/netfilter/nf_queue.c
@@ -129,14 +129,14 @@ bool nf_queue_entry_get_refs(struct nf_queue_entry *entry)
}
EXPORT_SYMBOL_GPL(nf_queue_entry_get_refs);
-void nf_queue_nf_hook_drop(struct net *net)
+void nf_queue_nf_hook_drop(struct net *net, const struct nf_hook_ops *ops)
{
const struct nf_queue_handler *qh;
rcu_read_lock();
qh = rcu_dereference(nf_queue_handler);
if (qh)
- qh->nf_hook_drop(net);
+ qh->nf_hook_drop(net, ops);
rcu_read_unlock();
}
EXPORT_SYMBOL_GPL(nf_queue_nf_hook_drop);
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index b59628e6240c1..59ac623e6805f 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -1036,32 +1036,67 @@ static inline u64 nf_tables_alloc_handle(struct nft_table *table)
return ++table->hgenerator;
}
-static const struct nft_chain_type *chain_type[NFPROTO_NUMPROTO][NFT_CHAIN_T_MAX];
+static const struct nft_chain_type __rcu *chain_type[NFPROTO_NUMPROTO][NFT_CHAIN_T_MAX];
static const struct nft_chain_type *
-__nft_chain_type_get(u8 family, enum nft_chain_types type)
+__nft_chain_type_peek(u8 family, enum nft_chain_types type)
{
if (family >= NFPROTO_NUMPROTO ||
type >= NFT_CHAIN_T_MAX)
return NULL;
- return chain_type[family][type];
+ return rcu_dereference_check(chain_type[family][type],
+ lockdep_nfnl_is_held(NFNL_SUBSYS_NFTABLES));
}
+/**
+ * nft_chain_type_get - look up a chain type by family and id
+ * @family: netlink family
+ * @type: chain type (enum nft_chain_types)
+ *
+ * Return: chain type with a held module ref, or NULL.
+ */
+static const struct nft_chain_type *
+nft_chain_type_get(u8 family, enum nft_chain_types type)
+{
+ const struct nft_chain_type *t;
+
+ rcu_read_lock();
+ t = __nft_chain_type_peek(family, type);
+
+ if (t && !try_module_get(t->owner))
+ t = NULL;
+
+ rcu_read_unlock();
+ return t;
+}
+
+/**
+ * __nf_tables_chain_type_lookup - look up a chain type by name
+ * @nla: nlattr containing the chain-type name
+ * @family: netlink family
+ *
+ * Return: matching chain type with a held module ref, or NULL.
+ */
static const struct nft_chain_type *
__nf_tables_chain_type_lookup(const struct nlattr *nla, u8 family)
{
- const struct nft_chain_type *type;
+ const struct nft_chain_type *type = NULL, *t;
int i;
+ rcu_read_lock();
for (i = 0; i < NFT_CHAIN_T_MAX; i++) {
- type = __nft_chain_type_get(family, i);
- if (!type)
+ t = __nft_chain_type_peek(family, i);
+ if (!t)
continue;
- if (!nla_strcmp(nla, type->name))
- return type;
+
+ if (!nla_strcmp(nla, t->name) && try_module_get(t->owner)) {
+ type = t;
+ break;
+ }
}
- return NULL;
+ rcu_read_unlock();
+ return type;
}
struct nft_module_request {
@@ -1837,11 +1872,11 @@ static void nf_tables_table_destroy(struct nft_table *table)
void nft_register_chain_type(const struct nft_chain_type *ctype)
{
nfnl_lock(NFNL_SUBSYS_NFTABLES);
- if (WARN_ON(__nft_chain_type_get(ctype->family, ctype->type))) {
+ if (WARN_ON(__nft_chain_type_peek(ctype->family, ctype->type))) {
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
return;
}
- chain_type[ctype->family][ctype->type] = ctype;
+ rcu_assign_pointer(chain_type[ctype->family][ctype->type], ctype);
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
}
EXPORT_SYMBOL_GPL(nft_register_chain_type);
@@ -1849,7 +1884,7 @@ EXPORT_SYMBOL_GPL(nft_register_chain_type);
void nft_unregister_chain_type(const struct nft_chain_type *ctype)
{
nfnl_lock(NFNL_SUBSYS_NFTABLES);
- chain_type[ctype->family][ctype->type] = NULL;
+ RCU_INIT_POINTER(chain_type[ctype->family][ctype->type], NULL);
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
}
EXPORT_SYMBOL_GPL(nft_unregister_chain_type);
@@ -2581,10 +2616,6 @@ static int nft_chain_parse_hook(struct net *net,
hook->num = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM]));
hook->priority = ntohl(nla_get_be32(ha[NFTA_HOOK_PRIORITY]));
- type = __nft_chain_type_get(family, NFT_CHAIN_T_DEFAULT);
- if (!type)
- return -EOPNOTSUPP;
-
if (nla[NFTA_CHAIN_TYPE]) {
type = nf_tables_chain_type_lookup(net, nla[NFTA_CHAIN_TYPE],
family, true);
@@ -2592,13 +2623,19 @@ static int nft_chain_parse_hook(struct net *net,
NL_SET_BAD_ATTR(extack, nla[NFTA_CHAIN_TYPE]);
return PTR_ERR(type);
}
+ } else {
+ type = nft_chain_type_get(family, NFT_CHAIN_T_DEFAULT);
+ if (!type)
+ return -EOPNOTSUPP;
}
+
+ err = -EOPNOTSUPP;
if (hook->num >= NFT_MAX_HOOKS || !(type->hook_mask & (1 << hook->num)))
- return -EOPNOTSUPP;
+ goto out_put_type;
if (type->type == NFT_CHAIN_T_NAT &&
hook->priority <= NF_IP_PRI_CONNTRACK)
- return -EOPNOTSUPP;
+ goto out_put_type;
} else {
if (ha[NFTA_HOOK_HOOKNUM]) {
hook->num = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM]));
@@ -2620,30 +2657,27 @@ static int nft_chain_parse_hook(struct net *net,
}
} else {
type = basechain->type;
+ __module_get(type->owner);
}
}
- if (!try_module_get(type->owner)) {
- if (nla[NFTA_CHAIN_TYPE])
- NL_SET_BAD_ATTR(extack, nla[NFTA_CHAIN_TYPE]);
- return -ENOENT;
- }
-
hook->type = type;
INIT_LIST_HEAD(&hook->list);
if (nft_base_chain_netdev(family, hook->num)) {
err = nft_chain_parse_netdev(net, ha, &hook->list, extack, flags);
- if (err < 0) {
- module_put(type->owner);
- return err;
- }
+ if (err < 0)
+ goto out_put_type;
} else if (ha[NFTA_HOOK_DEV] || ha[NFTA_HOOK_DEVS]) {
- module_put(type->owner);
- return -EOPNOTSUPP;
+ err = -EOPNOTSUPP;
+ goto out_put_type;
}
return 0;
+
+out_put_type:
+ module_put(type->owner);
+ return err;
}
static void nft_chain_release_hook(struct nft_chain_hook *hook)
diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c
index a3bc002800510..00b76c25d55c9 100644
--- a/net/netfilter/nfnetlink_queue.c
+++ b/net/netfilter/nfnetlink_queue.c
@@ -1559,9 +1559,39 @@ static struct notifier_block nfqnl_dev_notifier = {
.notifier_call = nfqnl_rcv_dev_event,
};
-static void nfqnl_nf_hook_drop(struct net *net)
+/* Match the entries whose hook_index points into the hook array that
+ * __nf_unregister_net_hook() replaces when it removes @ops.
+ *
+ * ops->pf/hooknum name the hook as registered; entry->state.pf/hook
+ * name the array actually traversed - they differ only for NFPROTO_INET.
+ * Every other family uses the array of its own name.
+ */
+static int nf_hook_cmp(struct nf_queue_entry *entry, unsigned long ops_ptr)
+{
+ const struct nf_hook_ops *ops = (const struct nf_hook_ops *)ops_ptr;
+ unsigned int hooknum = ops->hooknum;
+ u8 pf = ops->pf;
+
+ /* same translation as nf_static_key_inc() */
+ if (pf == NFPROTO_INET && hooknum == NF_INET_INGRESS) {
+ pf = NFPROTO_NETDEV;
+ hooknum = NF_NETDEV_INGRESS;
+ }
+
+ if (entry->state.hook != hooknum)
+ return 0;
+
+ if (pf == NFPROTO_INET)
+ return entry->state.pf == NFPROTO_IPV4 ||
+ entry->state.pf == NFPROTO_IPV6;
+
+ return entry->state.pf == pf;
+}
+
+static void nfqnl_nf_hook_drop(struct net *net, const struct nf_hook_ops *ops)
{
struct nfnl_queue_net *q = nfnl_queue_pernet(net);
+ nfqnl_cmpfn cmpfn = NULL;
int i;
/* This function is also called on net namespace error unwind,
@@ -1574,12 +1604,19 @@ static void nfqnl_nf_hook_drop(struct net *net)
if (!q)
return;
+ /* ops is NULL when the caller is not unregistering a hook but
+ * removing an object queued packets may reference, every entry
+ * has to go
+ */
+ if (ops)
+ cmpfn = nf_hook_cmp;
+
for (i = 0; i < INSTANCE_BUCKETS; i++) {
struct nfqnl_instance *inst;
struct hlist_head *head = &q->instance_table[i];
hlist_for_each_entry_rcu(inst, head, hlist)
- nfqnl_flush(inst, NULL, 0);
+ nfqnl_flush(inst, cmpfn, (unsigned long)ops);
}
}
diff --git a/net/netfilter/nft_chain_nat.c b/net/netfilter/nft_chain_nat.c
index 40e230d8b712e..1133f79d8d253 100644
--- a/net/netfilter/nft_chain_nat.c
+++ b/net/netfilter/nft_chain_nat.c
@@ -131,6 +131,7 @@ static void __exit nft_chain_nat_exit(void)
#ifdef CONFIG_NF_TABLES_INET
nft_unregister_chain_type(&nft_chain_nat_inet);
#endif
+ synchronize_rcu();
}
module_init(nft_chain_nat_init);
diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c
index 63864b9282590..88ddaf47f5879 100644
--- a/net/netfilter/nft_compat.c
+++ b/net/netfilter/nft_compat.c
@@ -14,6 +14,7 @@
#include <linux/netfilter/nf_tables.h>
#include <linux/netfilter/nf_tables_compat.h>
#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter_ipv6/ip6_tables.h>
#include <linux/netfilter_bridge/ebtables.h>
@@ -45,6 +46,20 @@ static int nft_compat_chain_validate_dependency(const struct nft_ctx *ctx,
type = NFT_CHAIN_T_NAT;
if (basechain->type->type != type)
return -EINVAL;
+ } else if (strcmp(tablename, "raw") == 0) {
+ /* iptables-nft emulates the legacy raw table, which only
+ * ever has prerouting/output chains at this exact priority.
+ */
+ if (ctx->family != NFPROTO_IPV4 &&
+ ctx->family != NFPROTO_IPV6)
+ return -EINVAL;
+
+ if (basechain->ops.hooknum != NF_INET_PRE_ROUTING &&
+ basechain->ops.hooknum != NF_INET_LOCAL_OUT)
+ return -EINVAL;
+
+ if (basechain->ops.priority != NF_IP_PRI_RAW)
+ return -EINVAL;
}
return 0;
diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c
index 3c4c2faa73984..5a8db55920364 100644
--- a/net/netfilter/nft_ct.c
+++ b/net/netfilter/nft_ct.c
@@ -542,7 +542,7 @@ static void __nft_ct_set_destroy(const struct nft_ctx *ctx, struct nft_ct *priv)
#endif
#ifdef CONFIG_NF_CONNTRACK_ZONES
case NFT_CT_ZONE:
- nf_queue_nf_hook_drop(ctx->net);
+ nf_queue_nf_hook_drop(ctx->net, NULL);
mutex_lock(&nft_ct_pcpu_mutex);
if (--nft_ct_pcpu_template_refcnt == 0)
nft_ct_tmpl_put_pcpu();
diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c
index 8abf8e8145ce5..861bab93f4352 100644
--- a/net/netfilter/nft_exthdr.c
+++ b/net/netfilter/nft_exthdr.c
@@ -233,16 +233,23 @@ static void nft_exthdr_tcp_set_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
- u8 buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE];
struct nft_exthdr *priv = nft_expr_priv(expr);
unsigned int i, optl, tcphdr_len, offset;
- struct tcphdr *tcph;
+ struct tcphdr *tcph, _tcph;
u8 *opt;
- tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff, &tcphdr_len);
+ if (pkt->tprot != IPPROTO_TCP || pkt->fragoff)
+ goto err;
+
+ tcph = skb_header_pointer(pkt->skb, nft_thoff(pkt), sizeof(_tcph), &_tcph);
if (!tcph)
goto err;
+ tcphdr_len = __tcp_hdrlen(tcph);
+ if (tcphdr_len < sizeof(*tcph) ||
+ tcphdr_len > sizeof(*tcph) + MAX_TCP_OPTION_SPACE)
+ goto err;
+
if (skb_ensure_writable(pkt->skb, nft_thoff(pkt) + tcphdr_len))
goto err;
@@ -313,16 +320,23 @@ static void nft_exthdr_tcp_strip_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
- u8 buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE];
struct nft_exthdr *priv = nft_expr_priv(expr);
unsigned int i, tcphdr_len, optl;
- struct tcphdr *tcph;
+ struct tcphdr *tcph, _tcph;
u8 *opt;
- tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff, &tcphdr_len);
+ if (pkt->tprot != IPPROTO_TCP || pkt->fragoff)
+ goto err;
+
+ tcph = skb_header_pointer(pkt->skb, nft_thoff(pkt), sizeof(_tcph), &_tcph);
if (!tcph)
goto err;
+ tcphdr_len = __tcp_hdrlen(tcph);
+ if (tcphdr_len < sizeof(*tcph) ||
+ tcphdr_len > sizeof(*tcph) + MAX_TCP_OPTION_SPACE)
+ goto err;
+
if (skb_ensure_writable(pkt->skb, nft_thoff(pkt) + tcphdr_len))
goto drop;
@@ -551,7 +565,7 @@ static int nft_exthdr_tcp_set_init(const struct nft_ctx *ctx,
const struct nlattr * const tb[])
{
struct nft_exthdr *priv = nft_expr_priv(expr);
- u32 offset, len, flags = 0, op = NFT_EXTHDR_OP_IPV6;
+ u32 offset, len, flags = 0;
int err;
if (!tb[NFTA_EXTHDR_SREG] ||
@@ -581,15 +595,11 @@ static int nft_exthdr_tcp_set_init(const struct nft_ctx *ctx,
return -EOPNOTSUPP;
}
- err = nft_parse_u32_check(tb[NFTA_EXTHDR_OP], U8_MAX, &op);
- if (err < 0)
- return err;
-
priv->type = nla_get_u8(tb[NFTA_EXTHDR_TYPE]);
priv->offset = offset;
priv->len = len;
priv->flags = flags;
- priv->op = op;
+ priv->op = NFT_EXTHDR_OP_TCPOPT;
return nft_parse_register_load(ctx, tb[NFTA_EXTHDR_SREG], &priv->sreg,
priv->len);
diff --git a/tools/testing/selftests/net/netfilter/conntrack_vrf.sh b/tools/testing/selftests/net/netfilter/conntrack_vrf.sh
index 207b79932d91a..2dbfabaadf4bd 100755
--- a/tools/testing/selftests/net/netfilter/conntrack_vrf.sh
+++ b/tools/testing/selftests/net/netfilter/conntrack_vrf.sh
@@ -84,7 +84,7 @@ busywait $BUSYWAIT_TIMEOUT listener_ready "$ns1"
# as decided by the first iteration of the ruleset.
test_ct_zone_in()
{
-ip netns exec "$ns0" nft -f - <<EOF
+if ! ip netns exec "$ns0" nft -f - <<EOF
table testct {
chain rawpre {
type filter hook prerouting priority raw;
@@ -105,6 +105,12 @@ table testct {
}
}
EOF
+ then
+ echo "FAIL: cannot load testct nft ruleset"
+ ret=1
+ return
+ fi
+
ip netns exec "$ns1" ping -W 1 -c 1 -I veth0 "$IP0" > /dev/null
# should be in zone 1, not zone 2
@@ -113,6 +119,7 @@ EOF
echo "PASS: entry found in conntrack zone 1"
else
echo "FAIL: entry not found in conntrack zone 1"
+ ret=1
count=$(ip netns exec "$ns0" conntrack -L -s $IP1 -d $IP0 -p icmp --zone 2 2> /dev/null | wc -l)
if [ "$count" -eq 1 ]; then
echo "FAIL: entry found in zone 2 instead"
@@ -137,7 +144,7 @@ test_masquerade_vrf()
ip netns exec "$ns0" conntrack -F 2>/dev/null
-ip netns exec "$ns0" nft -f - <<EOF
+if ! ip netns exec "$ns0" nft -f - <<EOF
flush ruleset
table ip nat {
chain rawout {
@@ -158,6 +165,12 @@ table ip nat {
}
}
EOF
+ then
+ echo "FAIL: cannot load ip nat nft ruleset"
+ ret=1
+ return
+ fi
+
if ! ip netns exec "$ns0" ip vrf exec tvrf socat -u -4 STDIN TCP:"$IP1":55555 < /dev/null > /dev/null;then
echo "FAIL: connect failure with masquerade + sport rewrite on vrf device"
ret=1
@@ -184,7 +197,7 @@ EOF
test_masquerade_veth()
{
ip netns exec "$ns0" conntrack -F 2>/dev/null
-ip netns exec "$ns0" nft -f - <<EOF
+if ! ip netns exec "$ns0" nft -f - <<EOF
flush ruleset
table ip nat {
chain postrouting {
@@ -193,6 +206,12 @@ table ip nat {
}
}
EOF
+ then
+ echo "FAIL: cannot load ip nat postrouting ruleset"
+ ret=1
+ return
+ fi
+
if ! ip netns exec "$ns0" ip vrf exec tvrf socat -u -4 STDIN TCP:"$IP1":55555 < /dev/null > /dev/null;then
echo "FAIL: connect failure with masquerade + sport rewrite on veth device"
ret=1
diff --git a/tools/testing/selftests/net/netfilter/nf_queue.c b/tools/testing/selftests/net/netfilter/nf_queue.c
index 8bbec37f53563..cca6c3cfa9e46 100644
--- a/tools/testing/selftests/net/netfilter/nf_queue.c
+++ b/tools/testing/selftests/net/netfilter/nf_queue.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <errno.h>
+#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
@@ -21,6 +22,7 @@ struct options {
bool failopen;
bool out_of_order;
bool bogus_verdict;
+ bool hold;
int verbose;
unsigned int queue_num;
unsigned int timeout;
@@ -34,6 +36,7 @@ static struct options opts;
static void help(const char *p)
{
printf("Usage: %s [-c|-v [-vv] ] [-o] [-O] [-b] [-t timeout] [-q queue_num] [-Qdst_queue ] [ -d ms_delay ] [-G]\n", p);
+ printf(" [-H] (hold verdicts until SIGUSR1 is received)\n");
}
static int parse_attr_cb(const struct nlattr *attr, void *data)
@@ -280,6 +283,7 @@ static int mainloop(void)
uint32_t ooo_ids[16];
unsigned int portid;
int ooo_count = 0;
+ sigset_t hold_set;
char *buf;
int ret;
@@ -289,6 +293,12 @@ static int mainloop(void)
exit(EXIT_FAILURE);
}
+ /* -H: keep SIGUSR1 pending until the first packet is read */
+ sigemptyset(&hold_set);
+ sigaddset(&hold_set, SIGUSR1);
+ if (opts.hold)
+ sigprocmask(SIG_BLOCK, &hold_set, NULL);
+
nl = open_queue();
portid = mnl_socket_get_portid(nl);
@@ -320,6 +330,13 @@ static int mainloop(void)
}
id = ret - MNL_CB_OK;
+ if (opts.hold) {
+ int sig;
+
+ sigwait(&hold_set, &sig);
+ opts.hold = false;
+ }
+
if (opts.delay_ms)
sleep_ms(opts.delay_ms);
@@ -364,7 +381,7 @@ static void parse_opts(int argc, char **argv)
{
int c;
- while ((c = getopt(argc, argv, "chvoObt:q:Q:d:G")) != -1) {
+ while ((c = getopt(argc, argv, "chvoObt:q:Q:d:GH")) != -1) {
switch (c) {
case 'c':
opts.count_packets = true;
@@ -373,6 +390,9 @@ static void parse_opts(int argc, char **argv)
help(argv[0]);
exit(0);
break;
+ case 'H':
+ opts.hold = true;
+ break;
case 'q':
opts.queue_num = atoi(optarg);
if (opts.queue_num > 0xffff)
diff --git a/tools/testing/selftests/net/netfilter/nft_queue.sh b/tools/testing/selftests/net/netfilter/nft_queue.sh
index 7c857a2e0f349..a07652813a4b0 100755
--- a/tools/testing/selftests/net/netfilter/nft_queue.sh
+++ b/tools/testing/selftests/net/netfilter/nft_queue.sh
@@ -92,7 +92,7 @@ load_ruleset() {
ip netns exec "$nsrouter" nft -f /dev/stdin <<EOF
table $family $name {
chain nfq {
- ip protocol icmp queue bypass
+ icmp type { "echo-request", "echo-reply" } queue bypass
icmpv6 type { "echo-request", "echo-reply" } queue num 1 bypass
}
chain pre {
@@ -861,6 +861,113 @@ test_queue_bridge()
test_queue 20 "bridge"
}
+hook_drop_delivered()
+{
+ ip netns exec "$ns1" nft list counter inet nfqscope delivered |
+ sed -n 's/.*packets \([0-9]*\) .*/\1/p'
+}
+
+hook_drop_queued()
+{
+ ip netns exec "$ns1" awk '$1 == 1 { print $3 }' /proc/self/net/netfilter/nfnetlink_queue
+}
+
+hook_drop_all_queued()
+{
+ [ "$(hook_drop_queued)" = "$1" ]
+}
+
+# hook_drop_case <keep|drop> <description> <nft command...>
+#
+# 1. The inet output chain sends udp packets to port 12345 to queue 1,
+# the inet input chain counts them once they are accepted.
+# 2. nf_queue -H reads the packets but sends no verdict until it gets
+# SIGUSR1, so the 20 packets sent stay in the kernel queue.
+# 3. The nft command removes a base chain while they are queued. The
+# queue length in /proc right after it tells if the kernel dropped
+# them: still 20, or 0.
+# 4. SIGUSR1 releases nf_queue. If the kernel kept the packets they are
+# accepted now and reach the input counter. If it dropped them the
+# first verdict fails with ENOENT and nothing is counted.
+hook_drop_case()
+{
+ local expect="$1"
+ local desc="$2"
+ local packets=20
+ local delivered queued result
+ shift 2
+
+ip netns exec "$ns1" nft -f /dev/stdin <<EOF
+flush ruleset
+table inet nfqscope {
+ chain output {
+ type filter hook output priority 0; policy accept;
+ udp dport 12345 queue num 1
+ }
+ chain input {
+ type filter hook input priority 0; policy accept;
+ udp dport 12345 counter name delivered
+ }
+ chain output2 {
+ type filter hook output priority 100; policy accept;
+ }
+ counter delivered {}
+}
+table ip6 unrelated {
+ chain prerouting {
+ type filter hook prerouting priority 0; policy accept;
+ }
+}
+EOF
+ ip netns exec "$ns1" ./nf_queue -c -q 1 -H -t "$timeout" > "$TMPFILE1" 2>&1 &
+ local nfqpid=$!
+
+ busywait "$BUSYWAIT_TIMEOUT" nf_queue_wait "$ns1" 1
+
+ ip netns exec "$ns1" bash -c \
+ "for i in \$(seq $packets); do echo x > /dev/udp/127.0.0.1/12345; done"
+
+ busywait "$BUSYWAIT_TIMEOUT" hook_drop_all_queued "$packets"
+
+ ip netns exec "$ns1" nft "$@"
+ queued=$(hook_drop_queued)
+
+ kill -USR1 "$nfqpid"
+ wait "$nfqpid"
+ delivered=$(hook_drop_delivered)
+
+ if [ "$queued" = "$packets" ] && [ "$delivered" = "$packets" ] &&
+ grep -q "^$packets packets total" "$TMPFILE1"; then
+ result="keep"
+ elif [ "$queued" = "0" ] && [ "$delivered" = "0" ]; then
+ result="drop"
+ else
+ result="inconsistent"
+ fi
+
+ if [ "$result" = "$expect" ]; then
+ echo "PASS: $desc: queued packets $result"
+ else
+ echo "FAIL: $desc: queued packets $result, expected $expect" 1>&2
+ echo " $queued of $packets queued after the change, $delivered delivered" 1>&2
+ ret=1
+ fi
+}
+
+test_hook_drop_scope()
+{
+ # the hook array of another hook point changes, queued packets stay
+ hook_drop_case keep "base chain removed at another hook point" \
+ delete chain ip6 unrelated prerouting
+
+ # the hook array of the queueing hook point changes, the position of
+ # the queued packets in it is stale, they are dropped
+ hook_drop_case drop "base chain removed at the queueing hook point" \
+ delete chain inet nfqscope output2
+
+ ip netns exec "$ns1" nft flush ruleset
+}
+
ip netns exec "$nsrouter" sysctl net.ipv6.conf.all.forwarding=1 > /dev/null
ip netns exec "$nsrouter" sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null
ip netns exec "$nsrouter" sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null
@@ -909,6 +1016,7 @@ test_queue_stress
# should be last, adds vrf device in ns1 and changes routes
test_icmp_vrf
test_queue_removal
+test_hook_drop_scope
# turns router into a bridge
test_queue_bridge
|
| 3/2 | 2026/10/18 01:13 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 21576 output: 33 thoughts: 441 Thoughts: **Analyzing the Netfilter and Networking Patch Series** Okay, so I'm looking at this patch series and immediately my radar is up. It spans quite a bit of ground within the networking and netfilter subsystems: changes in documentation, header files, and several core implementations. I see modifications to bridge netfilter, various netfilter components (like `nf_flow_table`, `nf_queue`, `nft_chain_nat`, and connection tracking), IP tunneling, and even some IPVS interaction. My initial thought is to quickly assess the impact. Are these changes in reachable code? Given the components involved, yes, absolutely. We're talking about code that's readily accessible through netlink sockets (both unprivileged and privileged), network namespaces, packet sockets – standard stuff for a virtual environment, especially within a QEMU setup, which is something I'm very familiar with. This isn't some obscure corner of the kernel; this is stuff that's practically begging to be interacted with. Now, the real work begins. I need to dive into each of these changes individually. I need to meticulously determine the `FocusSymbols` and the appropriate `EnableConfigs`. The goal is to fully understand exactly what's being altered and what configurations are required for this code to be used. I'll need to dig into the git log for the commits if possible, or search the codebase for the context. I'll likely use tools like `grepper` or `read-file` to get the necessary context for each individual modification. This is going to require a careful, methodical approach, but that's just fine because it's precisely what I like to do. |
| 4/2 | 2026/10/18 01:13 | tool | grepper |
0mArgs:{
"Expression": "nft_chain_type_get",
"PathPrefixes": [
"net/netfilter/"
]
}Results: {
"Output": "net/netfilter/nf_tables_api.c=1042=__nft_chain_type_peek(u8 family, enum nft_chain_types type)\n--\nnet/netfilter/nf_tables_api.c-1052-/**\nnet/netfilter/nf_tables_api.c:1053: * nft_chain_type_get - look up a chain type by family and id\nnet/netfilter/nf_tables_api.c-1054- * @family: netlink family\n--\nnet/netfilter/nf_tables_api.c=1059=static const struct nft_chain_type *\nnet/netfilter/nf_tables_api.c:1060:nft_chain_type_get(u8 family, enum nft_chain_types type)\nnet/netfilter/nf_tables_api.c-1061-{\n--\nnet/netfilter/nf_tables_api.c=2589=static int nft_chain_parse_hook(struct net *net,\n--\nnet/netfilter/nf_tables_api.c-2626-\t\t} else {\nnet/netfilter/nf_tables_api.c:2627:\t\t\ttype = nft_chain_type_get(family, NFT_CHAIN_T_DEFAULT);\nnet/netfilter/nf_tables_api.c-2628-\t\t\tif (!type)\n"
} |
| 5/2 | 2026/10/18 01:13 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 22747 output: 60 thoughts: 2369 Thoughts: **My Analysis of Recent Kernel Network/Netfilter Changes** Okay, let's break down this diff piece by piece from a network/netfilter expert's perspective. It looks like a great opportunity for fuzzing, given the scope of changes spanning core functionalities and user-space interaction. I'm focusing on identifying the most relevant, non-ubiquitous entry points and functions. First, I'm noting the changes in `bridge.rst`, and the addition of `encap_proto` within several critical structs (`net_device_path`, `flow_offload_tunnel`), indicating a move towards more refined tunnel handling. Then, there's a significant refinement in the Netfilter queueing mechanism. Instead of a blunt flush of all queued packets on hook unregistration (`nf_queue_nf_hook_drop`), it's now selective, dropping only packets matching the *unregistered* hook. This is a subtle but important change, and I see a targeted test case (`test_hook_drop_scope()`) which should be checked to see how it can be used from userspace. The critical function here is `nf_queue_nf_hook_drop` (non-static, exported), and the static helper function `nfqnl_nf_hook_drop` in `nfnetlink_queue.c` should also be analyzed. Next, there is a good chunk of work in bridging (`br_netfilter_hooks.c`). The introduction of `in_userns` and the conditional disabling of `br_netfilter` within user namespaces is particularly interesting. I'll need to look at `brnf_device_event`, `brnf_init_net`, `brnf_exit_net`, `br_netfilter_sysctl_init_net`, and `br_netfilter_sysctl_exit_net` to determine if there are interesting user-space interaction points or race conditions. This clearly means bridging and netfilter now has a new scope of attack surface, and should be checked for how it interacts with the user namespace. Tunnel handling has been refactored and unified. The addition of the `encap_proto` field is key here again. I'm noting `nf_flow_tuple_encap`, `nf_flow_ip_tunnel_pop`, `nf_flow_tunnel_ip_push`, `nf_flow_tunnel_ip6_push`, `nf_flow_tunnel_push`, `nf_flow_queue_xmit4`, and `nf_flow_queue_xmit6` for closer inspection. These are responsible for pushing tunnel headers, popping tunnel headers, and the associated queuing of those packets, and how they interact with the flow table. I'm also including `ipip_fill_forward_path`, `ip6_tnl_fill_forward_path`, `nft_dev_path_info`, and `nft_dev_forward_path`. These new functions define paths and fill in data related to tunnel encapsulation and forward tunnel packets in the kernel. A simple fix for a data race in `ipvs_sync` (`ip_vs_sync_conn_v0`, `ip_vs_sync_conn`) is also noted. The conntrack cleanup change (`nf_conntrack_cleanup_net_list`) is relatively straightforward. The major feature is that the conntrack cleanup now has the worker flush before looping to ensure that all work is completed. Now, let's get into the `nf_tables` changes. This includes an `__rcu` for `chain_type`, RCU and refcounting fixes: `nft_chain_type_get`, `__nf_tables_chain_type_lookup`, `nft_register_chain_type`, `nft_unregister_chain_type` and `nft_chain_parse_hook`. The changes surrounding the parsing and validation of hooks within `nf_tables` are important and these functions directly implement the logic altered. `nft_compat_chain_validate_dependency` which handles the `"raw"` table is also interesting. Finally, the `nft_exthdr` changes seem to be primarily focused on improved TCP header handling. `nft_exthdr_tcp_set_eval`, `nft_exthdr_tcp_strip_eval`, and `nft_exthdr_tcp_set_init` are crucial here, particularly the bounds checks and packet validation. Given my knowledge, it's pretty clear that these changes are fuzz-worthy. Specifically, I should target functions like: * `nf_queue_nf_hook_drop` and it's callers. * `nft_chain_type_get`, `__nf_tables_chain_type_lookup`, `nft_register_chain_type`, ` |
| 6/2 | 2026/10/18 01:13 | tool | grepper |
0mArgs:{
"Expression": "(nft_exthdr_tcp_set_eval|nf_flow_tunnel_push|nft_chain_parse_hook|ip_vs_sync_conn)",
"PathPrefixes": [
"net/"
]
}Results: {
"Output": "\nFull output is too long, showing 200 out of 231 lines.\nUse more precise expression or PathPrefixes if possible.\n\nNumber of matching lines per file (6 files in total):\nnet/netfilter/ipvs/ip_vs_conn.c:1\nnet/netfilter/ipvs/ip_vs_core.c:3\nnet/netfilter/ipvs/ip_vs_sync.c:40\nnet/netfilter/nf_flow_table_ip.c:3\nnet/netfilter/nf_tables_api.c:4\nnet/netfilter/nft_exthdr.c:2\n\nnet/netfilter/ipvs/ip_vs_conn.c=1350=static void ip_vs_conn_expire(struct timer_list *t)\n--\nnet/netfilter/ipvs/ip_vs_conn.c-1419-\tif (ipvs-\u003esync_state \u0026 IP_VS_STATE_MASTER)\nnet/netfilter/ipvs/ip_vs_conn.c:1420:\t\tip_vs_sync_conn(ipvs, cp, sysctl_sync_threshold(ipvs));\nnet/netfilter/ipvs/ip_vs_conn.c-1421-\n--\nnet/netfilter/ipvs/ip_vs_core.c=2102=ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)\n--\nnet/netfilter/ipvs/ip_vs_core.c-2272-\t *\nnet/netfilter/ipvs/ip_vs_core.c:2273:\t * For ONE_PKT let ip_vs_sync_conn() do the filter work.\nnet/netfilter/ipvs/ip_vs_core.c-2274-\t */\n--\nnet/netfilter/ipvs/ip_vs_core.c-2281-\tif (ipvs-\u003esync_state \u0026 IP_VS_STATE_MASTER)\nnet/netfilter/ipvs/ip_vs_core.c:2282:\t\tip_vs_sync_conn(ipvs, cp, pkts);\nnet/netfilter/ipvs/ip_vs_core.c-2283-\telse if ((cp-\u003eflags \u0026 IP_VS_CONN_F_ONE_PACKET) \u0026\u0026 cp-\u003econtrol)\nnet/netfilter/ipvs/ip_vs_core.c:2284:\t\t/* increment is done inside ip_vs_sync_conn too */\nnet/netfilter/ipvs/ip_vs_core.c-2285-\t\tatomic_inc(\u0026cp-\u003econtrol-\u003ein_pkts);\n--\nnet/netfilter/ipvs/ip_vs_sync.c-13- * time-outs, and firewall marks.\nnet/netfilter/ipvs/ip_vs_sync.c:14: * In ver.1 \"ip_vs_sync_conn_options\" will be sent in netw. order.\nnet/netfilter/ipvs/ip_vs_sync.c-15- * Ver. 0 can be turned on by sysctl -w net.ipv4.vs.sync_version=0\n--\nnet/netfilter/ipvs/ip_vs_sync.c=65=static struct lock_class_key __ipvs_sync_key;\n--\nnet/netfilter/ipvs/ip_vs_sync.c-69- */\nnet/netfilter/ipvs/ip_vs_sync.c:70:struct ip_vs_sync_conn_v0 {\nnet/netfilter/ipvs/ip_vs_sync.c-71-\t__u8\t\t\treserved;\n--\nnet/netfilter/ipvs/ip_vs_sync.c-88-\nnet/netfilter/ipvs/ip_vs_sync.c:89:struct ip_vs_sync_conn_options {\nnet/netfilter/ipvs/ip_vs_sync.c-90-\tstruct ip_vs_seq in_seq; /* incoming seq. struct */\n--\nnet/netfilter/ipvs/ip_vs_sync.c=154=struct ip_vs_sync_v6 {\n--\nnet/netfilter/ipvs/ip_vs_sync.c-173-\nnet/netfilter/ipvs/ip_vs_sync.c:174:union ip_vs_sync_conn {\nnet/netfilter/ipvs/ip_vs_sync.c-175-\tstruct ip_vs_sync_v4\tv4;\n--\nnet/netfilter/ipvs/ip_vs_sync.c=196=struct ip_vs_sync_thread_data {\n--\nnet/netfilter/ipvs/ip_vs_sync.c-204-/* Version 0 definition of packet sizes */\nnet/netfilter/ipvs/ip_vs_sync.c:205:#define SIMPLE_CONN_SIZE (sizeof(struct ip_vs_sync_conn_v0))\nnet/netfilter/ipvs/ip_vs_sync.c-206-#define FULL_CONN_SIZE \\\nnet/netfilter/ipvs/ip_vs_sync.c:207:(sizeof(struct ip_vs_sync_conn_v0) + sizeof(struct ip_vs_sync_conn_options))\nnet/netfilter/ipvs/ip_vs_sync.c-208-\n--\nnet/netfilter/ipvs/ip_vs_sync.c=245=struct ip_vs_sync_mesg_v0 {\n--\nnet/netfilter/ipvs/ip_vs_sync.c-249-\nnet/netfilter/ipvs/ip_vs_sync.c:250:\t/* ip_vs_sync_conn entries start here */\nnet/netfilter/ipvs/ip_vs_sync.c-251-};\n--\nnet/netfilter/ipvs/ip_vs_sync.c=254=struct ip_vs_sync_mesg {\n--\nnet/netfilter/ipvs/ip_vs_sync.c-260-\t__u16\t\t\tspare;\nnet/netfilter/ipvs/ip_vs_sync.c:261:\t/* ip_vs_sync_conn entries start here */\nnet/netfilter/ipvs/ip_vs_sync.c-262-};\n--\nnet/netfilter/ipvs/ip_vs_sync.c=441=static inline bool in_persistence(struct ip_vs_conn *cp)\n--\nnet/netfilter/ipvs/ip_vs_sync.c-458- */\nnet/netfilter/ipvs/ip_vs_sync.c:459:static int ip_vs_sync_conn_needed(struct netns_ipvs *ipvs,\nnet/netfilter/ipvs/ip_vs_sync.c-460-\t\t\t\t struct ip_vs_conn *cp, int pkts)\n--\nnet/netfilter/ipvs/ip_vs_sync.c-538- */\nnet/netfilter/ipvs/ip_vs_sync.c:539:static void ip_vs_sync_conn_v0(struct netns_ipvs *ipvs, struct ip_vs_conn *cp,\nnet/netfilter/ipvs/ip_vs_sync.c-540-\t\t\t int pkts)\n--\nnet/netfilter/ipvs/ip_vs_sync.c-542-\tstruct ip_vs_sync_mesg_v0 *m;\nnet/netfilter/ipvs/ip_vs_sync.c:543:\tstruct ip_vs_sync_conn_v0 *s;\nnet/netfilter/ipvs/ip_vs_sync.c-544-\tstruct ip_vs_sync_buff *buff;\n--\nnet/netfilter/ipvs/ip_vs_sync.c-556-\nnet/netfilter/ipvs/ip_vs_sync.c:557:\tif (!ip_vs_sync_conn_needed(ipvs, cp, pkts))\nnet/netfilter/ipvs/ip_vs_sync.c-558-\t\treturn;\n--\nnet/netfilter/ipvs/ip_vs_sync.c-590-\tm = (struct ip_vs_sync_mesg_v0 *) buff-\u003emesg;\nnet/netfilter/ipvs/ip_vs_sync.c:591:\ts = (struct ip_vs_sync_conn_v0 *) buff-\u003ehead;\nnet/netfilter/ipvs/ip_vs_sync.c-592-\n--\nnet/netfilter/ipvs/ip_vs_sync.c-604-\tif (seq_mask) {\nnet/netfilter/ipvs/ip_vs_sync.c:605:\t\tstruct ip_vs_sync_conn_options *opt =\nnet/netfilter/ipvs/ip_vs_sync.c:606:\t\t\t(struct ip_vs_sync_conn_options *)\u0026s[1];\nnet/netfilter/ipvs/ip_vs_sync.c-607-\t\tmemcpy(opt, \u0026cp-\u003esync_conn_opt, sizeof(*opt));\n--\nnet/netfilter/ipvs/ip_vs_sync.c-621-\t\t\tpkts = sysctl_sync_threshold(ipvs);\nnet/netfilter/ipvs/ip_vs_sync.c:622:\t\tip_vs_sync_conn(ipvs, cp, pkts);\nnet/netfilter/ipvs/ip_vs_sync.c-623-\t}\n--\nnet/netfilter/ipvs/ip_vs_sync.c-630- */\nnet/netfilter/ipvs/ip_vs_sync.c:631:void ip_vs_sync_conn(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, int pkts)\nnet/netfilter/ipvs/ip_vs_sync.c-632-{\nnet/netfilter/ipvs/ip_vs_sync.c-633-\tstruct ip_vs_sync_mesg *m;\nnet/netfilter/ipvs/ip_vs_sync.c:634:\tunion ip_vs_sync_conn *s;\nnet/netfilter/ipvs/ip_vs_sync.c-635-\tstruct ip_vs_sync_buff *buff;\n--\nnet/netfilter/ipvs/ip_vs_sync.c-643-\tif (sysctl_sync_ver(ipvs) == 0) {\nnet/netfilter/ipvs/ip_vs_sync.c:644:\t\tip_vs_sync_conn_v0(ipvs, cp, pkts);\nnet/netfilter/ipvs/ip_vs_sync.c-645-\t\treturn;\n--\nnet/netfilter/ipvs/ip_vs_sync.c-650-sloop:\nnet/netfilter/ipvs/ip_vs_sync.c:651:\tif (!ip_vs_sync_conn_needed(ipvs, cp, pkts))\nnet/netfilter/ipvs/ip_vs_sync.c-652-\t\tgoto control;\n--\nnet/netfilter/ipvs/ip_vs_sync.c-682-\tif (seq_mask)\nnet/netfilter/ipvs/ip_vs_sync.c:683:\t\tlen += sizeof(struct ip_vs_sync_conn_options) + 2;\nnet/netfilter/ipvs/ip_vs_sync.c-684-\n--\nnet/netfilter/ipvs/ip_vs_sync.c-722-\nnet/netfilter/ipvs/ip_vs_sync.c:723:\ts = (union ip_vs_sync_conn *)p;\nnet/netfilter/ipvs/ip_vs_sync.c-724-\n--\nnet/netfilter/ipvs/ip_vs_sync.c-753-\t\t*(p++) = IPVS_OPT_SEQ_DATA;\nnet/netfilter/ipvs/ip_vs_sync.c:754:\t\t*(p++) = sizeof(struct ip_vs_sync_conn_options);\nnet/netfilter/ipvs/ip_vs_sync.c-755-\t\thton_seq(\u0026cp-\u003ein_seq, (struct ip_vs_seq *)p);\n--\nnet/netfilter/ipvs/ip_vs_sync.c=792=static inline int\nnet/netfilter/ipvs/ip_vs_sync.c:793:ip_vs_conn_fill_param_sync(struct netns_ipvs *ipvs, int af, union ip_vs_sync_conn *sc,\nnet/netfilter/ipvs/ip_vs_sync.c-794-\t\t\t struct ip_vs_conn_param *p,\n--\nnet/netfilter/ipvs/ip_vs_sync.c=846=static void ip_vs_proc_conn(struct netns_ipvs *ipvs, struct ip_vs_conn_param *param,\n--\nnet/netfilter/ipvs/ip_vs_sync.c-850-\t\t\t unsigned long timeout, __u32 fwmark,\nnet/netfilter/ipvs/ip_vs_sync.c:851:\t\t\t struct ip_vs_sync_conn_options *opt)\nnet/netfilter/ipvs/ip_vs_sync.c-852-{\n--\nnet/netfilter/ipvs/ip_vs_sync.c=975=static void ip_vs_process_message_v0(struct netns_ipvs *ipvs, const char *buffer,\n--\nnet/netfilter/ipvs/ip_vs_sync.c-978-\tstruct ip_vs_sync_mesg_v0 *m = (struct ip_vs_sync_mesg_v0 *)buffer;\nnet/netfilter/ipvs/ip_vs_sync.c:979:\tstruct ip_vs_sync_conn_v0 *s;\nnet/netfilter/ipvs/ip_vs_sync.c:980:\tstruct ip_vs_sync_conn_options *opt;\nnet/netfilter/ipvs/ip_vs_sync.c-981-\tstruct ip_vs_protocol *pp;\n--\nnet/netfilter/ipvs/ip_vs_sync.c-993-\t\t}\nnet/netfilter/ipvs/ip_vs_sync.c:994:\t\ts = (struct ip_vs_sync_conn_v0 *) p;\nnet/netfilter/ipvs/ip_vs_sync.c-995-\t\tflags = ntohs(s-\u003eflags);\nnet/netfilter/ipvs/ip_vs_sync.c-996-\t\tif (flags \u0026 IP_VS_CONN_F_SEQ_MASK) {\nnet/netfilter/ipvs/ip_vs_sync.c:997:\t\t\topt = (struct ip_vs_sync_conn_options *)\u0026s[1];\nnet/netfilter/ipvs/ip_vs_sync.c-998-\t\t\tp += FULL_CONN_SIZE;\n--\nnet/netfilter/ipvs/ip_vs_sync.c=1050=static inline int ip_vs_proc_seqopt(__u8 *p, unsigned int plen,\nnet/netfilter/ipvs/ip_vs_sync.c-1051-\t\t\t\t __u32 *opt_flags,\nnet/netfilter/ipvs/ip_vs_sync.c:1052:\t\t\t\t struct ip_vs_sync_conn_options *opt)\nnet/netfilter/ipvs/ip_vs_sync.c-1053-{\nnet/netfilter/ipvs/ip_vs_sync.c:1054:\tstruct ip_vs_sync_conn_options *topt;\nnet/netfilter/ipvs/ip_vs_sync.c-1055-\nnet/netfilter/ipvs/ip_vs_sync.c:1056:\ttopt = (struct ip_vs_sync_conn_options *)p;\nnet/netfilter/ipvs/ip_vs_sync.c-1057-\nnet/netfilter/ipvs/ip_vs_sync.c:1058:\tif (plen != sizeof(struct ip_vs_sync_conn_options)) {\nnet/netfilter/ipvs/ip_vs_sync.c-1059-\t\tIP_VS_DBG(2, \"BACKUP, bogus conn options length\\n\");\n--\nnet/netfilter/ipvs/ip_vs_sync.c=1092=static inline int ip_vs_proc_sync_conn(struct netns_ipvs *ipvs, __u8 *p, __u8 *msg_end)\nnet/netfilter/ipvs/ip_vs_sync.c-1093-{\nnet/netfilter/ipvs/ip_vs_sync.c:1094:\tstruct ip_vs_sync_conn_options opt;\nnet/netfilter/ipvs/ip_vs_sync.c:1095:\tunion ip_vs_sync_conn *s;\nnet/netfilter/ipvs/ip_vs_sync.c-1096-\tstruct ip_vs_protocol *pp;\n--\nnet/netfilter/ipvs/ip_vs_sync.c-1103-\nnet/netfilter/ipvs/ip_vs_sync.c:1104:\ts = (union ip_vs_sync_conn *) p;\nnet/netfilter/ipvs/ip_vs_sync.c-1105-\n--\nnet/netfilter/ipvs/ip_vs_sync.c=1232=static void ip_vs_process_message(struct netns_ipvs *ipvs, __u8 *buffer,\n--\nnet/netfilter/ipvs/ip_vs_sync.c-1260-\t\tfor (i=0; i\u003cnr_conns; i++) {\nnet/netfilter/ipvs/ip_vs_sync.c:1261:\t\t\tunion ip_vs_sync_conn *s;\nnet/netfilter/ipvs/ip_vs_sync.c-1262-\t\t\tunsigned int size;\n--\nnet/netfilter/ipvs/ip_vs_sync.c-1269-\t\t\t}\nnet/netfilter/ipvs/ip_vs_sync.c:1270:\t\t\ts = (union ip_vs_sync_conn *)p;\nnet/netfilter/ipvs/ip_vs_sync.c-1271-\t\t\tsize = ntohs(s-\u003ev4.ver_size) \u0026 SVER_MASK;\n--\nnet/netfilter/ipvs/ip_vs_sync.c=1767=int start_sync_thread(struct netns_ipvs *ipvs, struct ipvs_sync_daemon_cfg *c,\n--\nnet/netfilter/ipvs/ip_vs_sync.c-1779-\tIP_VS_DBG(7, \"%s(): pid %d\\n\", __func__, task_pid_nr(current));\nnet/netfilter/ipvs/ip_vs_sync.c:1780:\tIP_VS_DBG(7, \"Each ip_vs_sync_conn entry needs %zd bytes\\n\",\nnet/netfilter/ipvs/ip_vs_sync.c:1781:\t\t sizeof(struct ip_vs_sync_conn_v0));\nnet/netfilter/ipvs/ip_vs_sync.c-1782-\n--\nnet/netfilter/nf_flow_table_ip.c=658=static int nf_flow_tunnel_ip6_push(struct net *net, struct sk_buff *skb,\n--\nnet/netfilter/nf_flow_table_ip.c-724-\nnet/netfilter/nf_flow_table_ip.c:725:static int nf_flow_tunnel_push(struct net *net, struct sk_buff *skb,\nnet/netfilter/nf_flow_table_ip.c-726-\t\t\t struct flow_offload_tuple *tuple,\n--\nnet/netfilter/nf_flow_table_ip.c=837=static int nf_flow_queue_xmit4(struct sk_buff *skb,\n--\nnet/netfilter/nf_flow_table_ip.c-861-\nnet/netfilter/nf_flow_table_ip.c:862:\tif (nf_flow_tunnel_push(state-\u003enet, skb, other_tuple,\nnet/netfilter/nf_flow_table_ip.c-863-\t\t\t\ttuplehash-\u003etuple.dst_cache, \u0026ip_addr) \u003c 0)\n--\nnet/netfilter/nf_flow_table_ip.c=1165=static int nf_flow_queue_xmit6(struct sk_buff *skb,\n--\nnet/netfilter/nf_flow_table_ip.c-1189-\nnet/netfilter/nf_flow_table_ip.c:1190:\tif (nf_flow_tunnel_push(state-\u003enet, skb, other_tuple,\nnet/netfilter/nf_flow_table_ip.c-1191-\t\t\t\ttuplehash-\u003etuple.dst_cache, \u0026ip_addr) \u003c 0)\n--\nnet/netfilter/nf_tables_api.c=2559=static int nft_chain_parse_netdev(struct net *net, struct nlattr *tb[],\n--\nnet/netfilter/nf_tables_api.c-2588-\nnet/netfilter/nf_tables_api.c:2589:static int nft_chain_parse_hook(struct net *net,\n\n"
} |
| 7/2 | 2026/10/18 01:13 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 31532 output: 68 thoughts: 24 Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person: **Analyzing the Function Call Landscape** Alright, my internal checklist is pinging. I see `nft_exthdr_tcp_set_eval` in the context, and my immediate thought is to trace its usage. I need a comprehensive overview. The next step, naturally, is to examine all the remaining lines that match that function call. This isn't just a casual scan; I want a methodical and thorough sweep. It's about building a robust understanding of *where* and *how* `nft_exthdr_tcp_set_eval` is being invoked within the codebase. I need to understand its relationship to other components, data structures, and any potential side effects. This investigation is crucial for understanding its overall purpose and ensuring there aren't any potential unintended consequences. Let's dig in. |
| 8/2 | 2026/10/18 01:13 | tool | grepper |
0mArgs:{
"Expression": "nft_exthdr_tcp_set_eval|nft_exthdr_tcp_strip_eval|nft_exthdr_tcp_set_init",
"PathPrefixes": [
"net/netfilter/nft_exthdr.c"
]
}Results: {
"Output": "net/netfilter/nft_exthdr.c=188=static void nft_exthdr_tcp_eval(const struct nft_expr *expr,\n--\nnet/netfilter/nft_exthdr.c-231-\nnet/netfilter/nft_exthdr.c:232:static void nft_exthdr_tcp_set_eval(const struct nft_expr *expr,\nnet/netfilter/nft_exthdr.c-233-\t\t\t\t struct nft_regs *regs,\n--\nnet/netfilter/nft_exthdr.c-318-\nnet/netfilter/nft_exthdr.c:319:static void nft_exthdr_tcp_strip_eval(const struct nft_expr *expr,\nnet/netfilter/nft_exthdr.c-320-\t\t\t\t struct nft_regs *regs,\n--\nnet/netfilter/nft_exthdr.c=512=static int nft_exthdr_init(const struct nft_ctx *ctx,\n--\nnet/netfilter/nft_exthdr.c-562-\nnet/netfilter/nft_exthdr.c:563:static int nft_exthdr_tcp_set_init(const struct nft_ctx *ctx,\nnet/netfilter/nft_exthdr.c-564-\t\t\t\t const struct nft_expr *expr,\n--\nnet/netfilter/nft_exthdr.c=742=static const struct nft_expr_ops nft_exthdr_tcp_set_ops = {\n--\nnet/netfilter/nft_exthdr.c-744-\t.size\t\t= NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),\nnet/netfilter/nft_exthdr.c:745:\t.eval\t\t= nft_exthdr_tcp_set_eval,\nnet/netfilter/nft_exthdr.c:746:\t.init\t\t= nft_exthdr_tcp_set_init,\nnet/netfilter/nft_exthdr.c-747-\t.dump\t\t= nft_exthdr_dump_set,\n--\nnet/netfilter/nft_exthdr.c=750=static const struct nft_expr_ops nft_exthdr_tcp_strip_ops = {\n--\nnet/netfilter/nft_exthdr.c-752-\t.size\t\t= NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),\nnet/netfilter/nft_exthdr.c:753:\t.eval\t\t= nft_exthdr_tcp_strip_eval,\nnet/netfilter/nft_exthdr.c-754-\t.init\t\t= nft_exthdr_tcp_strip_init,\n"
} |
| 9/2 | 2026/10/18 01:13 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 32507 output: 79 thoughts: 466 Thoughts: **Investigating nft_chain_type and nft_tables Dependencies** Okay, let's dive into this `nft_chain_type` and `nft_tables` situation. My first thought is to trace the core functions involved in their registration and use. I see a cluster of them in `net/netfilter/nf_tables_api.c`: `nft_register_chain_type`, `nft_unregister_chain_type`, `nft_chain_parse_hook`, `nft_chain_type_get`, and `__nf_tables_chain_type_lookup`. That's the heart of the matter, likely where the architecture is defined. Next, I'm noting potential interactions with other subsystems. `nfnetlink_queue` is always interesting, so I'll check `net/netfilter/nfnetlink_queue.c` and `nf_queue.c` for `nf_queue_nf_hook_drop` and `nfqnl_nf_hook_drop`. Could be important for packet handling and potential drop behavior. Then there's the flow table stuff in `net/netfilter/nf_flow_table_ip.c`. Functions like `nf_flow_tunnel_push`, and its callers like `nf_flow_queue_xmit4` and `nf_flow_queue_xmit6`, plus `nf_flow_ip_tunnel_pop` and `nf_flow_tuple_encap`, suggest some relationship with tunneling or packet encapsulation, which might be critical. Now, let's consider broader network interactions. `net/bridge/br_netfilter_hooks.c` contains functions like `brnf_device_event`, `brnf_init_net`, and `brnf_exit_net`. This points to bridge integration, which could be relevant depending on the nature of the issue. Finally, `net/netfilter/ipvs/ip_vs_sync.c` with its `ip_vs_sync_conn` suggests interactions with IP Virtual Server synchronization. Again, depending on the problem, this could be on the critical path or a tangential concern. To get a clearer picture, I need to check the configurations related to these files. I'm going to look for Kconfig entries for all the files I've identified, specifically: `net/bridge/br_netfilter_hooks.c`, `net/netfilter/nfnetlink_queue.c`, `net/netfilter/nf_tables_api.c`, `net/netfilter/nft_exthdr.c`, `net/netfilter/nf_flow_table_ip.c`, `net/netfilter/ipvs/ip_vs_sync.c`, and potentially related areas like `net/ipv4/ipip.c` and `net/ipv6/ip6_tunnel.c`. I need to understand what's *enabled* and *disabled* during build time to form a complete picture. Finally, I'll leverage `grepper` to search through the `Makefile` entries and `Kconfig` definitions. I need a quick and dirty way to identify any compile-time or build-time dependencies and options. That should give me a complete overview. |
| 10/2 | 2026/10/18 01:14 | tool | grepper |
0mArgs:{
"Expression": "(br_netfilter|nfnetlink_queue|nf_flow_table_ip|nft_exthdr|ip_vs_sync)",
"PathPrefixes": [
"net/bridge/Makefile",
"net/netfilter/Makefile",
"net/netfilter/ipvs/Makefile"
]
}Results: {
"Output": "net/bridge/Makefile=15=bridge-$(subst m,y,$(CONFIG_BRIDGE_NETFILTER)) += br_nf_core.o\nnet/bridge/Makefile-16-\nnet/bridge/Makefile:17:br_netfilter-y := br_netfilter_hooks.o\nnet/bridge/Makefile:18:br_netfilter-$(subst m,y,$(CONFIG_IPV6)) += br_netfilter_ipv6.o\nnet/bridge/Makefile:19:obj-$(CONFIG_BRIDGE_NETFILTER) += br_netfilter.o\nnet/bridge/Makefile-20-\n--\nnet/bridge/Makefile=34=GCOV_PROFILE_br_nf_core.o := y\nnet/bridge/Makefile:35:GCOV_PROFILE_br_netfilter_hooks.o := y\nnet/bridge/Makefile:36:GCOV_PROFILE_br_netfilter_ipv6.o := y\nnet/bridge/Makefile-37-endif\n--\nnet/netfilter/Makefile=27=obj-$(CONFIG_NETFILTER_NETLINK_ACCT) += nfnetlink_acct.o\nnet/netfilter/Makefile:28:obj-$(CONFIG_NETFILTER_NETLINK_QUEUE) += nfnetlink_queue.o\nnet/netfilter/Makefile-29-obj-$(CONFIG_NETFILTER_NETLINK_LOG) += nfnetlink_log.o\n--\nnet/netfilter/Makefile=87=nf_tables-objs := nf_tables_core.o nf_tables_api.o nft_chain_filter.o \\\n--\nnet/netfilter/Makefile-89-\t\t nft_bitwise.o nft_byteorder.o nft_payload.o nft_lookup.o \\\nnet/netfilter/Makefile:90:\t\t nft_dynset.o nft_meta.o nft_rt.o nft_exthdr.o nft_last.o \\\nnet/netfilter/Makefile-91-\t\t nft_counter.o nft_objref.o nft_inner.o \\\n--\nnet/netfilter/Makefile=142=obj-$(CONFIG_NF_FLOW_TABLE)\t+= nf_flow_table.o\nnet/netfilter/Makefile:143:nf_flow_table-objs\t\t:= nf_flow_table_core.o nf_flow_table_ip.o \\\nnet/netfilter/Makefile-144-\t\t\t\t nf_flow_table_path.o \\\n--\nnet/netfilter/ipvs/Makefile=16=ip_vs-objs :=\tip_vs_conn.o ip_vs_core.o ip_vs_ctl.o ip_vs_sched.o\t \\\nnet/netfilter/ipvs/Makefile:17:\t\tip_vs_xmit.o ip_vs_app.o ip_vs_sync.o\t \t\t \\\nnet/netfilter/ipvs/Makefile-18-\t\tip_vs_est.o ip_vs_proto.o ip_vs_pe.o\t\t\t \\\n"
} |
| 11/2 | 2026/10/18 01:14 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 34475 output: 53 thoughts: 96 Thoughts: Okay, here's my thought process on this task: **Kconfig Dependency Deep Dive** Alright, time to get down to brass tacks. I need to understand the configuration landscape surrounding these networking features. Let me take a look at the Kconfig definitions for `CONFIG_BRIDGE_NETFILTER`, `CONFIG_NETFILTER_NETLINK_QUEUE`, `CONFIG_NF_TABLES`, `CONFIG_NF_FLOW_TABLE`, `CONFIG_IP_VS`, `CONFIG_NET_IPIP`, `CONFIG_IPV6_TUNNEL`, and `CONFIG_NFT_COMPAT`. I know these are crucial building blocks. I'm especially interested in dependencies here - what other options enable or disable these? Grasping the relationships between these features is key to understanding how they fit into the bigger picture. After I find the configurations, I will use `grep` to figure out the dependencies. |
| 12/2 | 2026/10/18 01:14 | tool | grepper |
0mArgs:{
"Expression": "config (BRIDGE_NETFILTER|NETFILTER_NETLINK_QUEUE|NF_FLOW_TABLE|IP_VS|NFT_COMPAT)",
"PathPrefixes": [
"net/"
]
}Results: {
"Output": "net/Kconfig=219=config NETFILTER_ADVANCED\n--\nnet/Kconfig-229-\nnet/Kconfig:230:config BRIDGE_NETFILTER\nnet/Kconfig-231-\ttristate \"Bridged IP/ARP packets filtering\"\n--\nnet/ipv4/netfilter/Kconfig=327=config IP_NF_ARPTABLES\n--\nnet/ipv4/netfilter/Kconfig-336-\nnet/ipv4/netfilter/Kconfig:337:config NFT_COMPAT_ARP\nnet/ipv4/netfilter/Kconfig-338-\ttristate\n--\nnet/netfilter/Kconfig=46=config NETFILTER_NETLINK_ACCT\n--\nnet/netfilter/Kconfig-53-\nnet/netfilter/Kconfig:54:config NETFILTER_NETLINK_QUEUE\nnet/netfilter/Kconfig-55-\ttristate \"Netfilter NFQUEUE over NFNETLINK interface\"\n--\nnet/netfilter/Kconfig=586=config NFT_REJECT_INET\n--\nnet/netfilter/Kconfig-590-\nnet/netfilter/Kconfig:591:config NFT_COMPAT\nnet/netfilter/Kconfig-592-\tdepends on NETFILTER_XTABLES\n--\nnet/netfilter/Kconfig=701=endif # NF_TABLES\nnet/netfilter/Kconfig-702-\nnet/netfilter/Kconfig:703:config NF_FLOW_TABLE_INET\nnet/netfilter/Kconfig-704-\ttristate \"Netfilter flow table mixed IPv4/IPv6 module\"\n--\nnet/netfilter/Kconfig-710-\nnet/netfilter/Kconfig:711:config NF_FLOW_TABLE\nnet/netfilter/Kconfig-712-\ttristate \"Netfilter flow table module\"\n--\nnet/netfilter/Kconfig-720-\nnet/netfilter/Kconfig:721:config NF_FLOW_TABLE_PROCFS\nnet/netfilter/Kconfig-722-\tbool \"Supply flow table statistics in procfs\"\n--\nnet/netfilter/ipvs/Kconfig-4-#\nnet/netfilter/ipvs/Kconfig:5:menuconfig IP_VS\nnet/netfilter/ipvs/Kconfig-6-\ttristate \"IP virtual server support\"\n--\nnet/netfilter/ipvs/Kconfig=286=comment 'IPVS SH scheduler'\nnet/netfilter/ipvs/Kconfig-287-\nnet/netfilter/ipvs/Kconfig:288:config IP_VS_SH_TAB_BITS\nnet/netfilter/ipvs/Kconfig-289-\tint \"IPVS source hashing table size (the Nth power of 2)\"\n--\nnet/netfilter/ipvs/Kconfig=301=comment 'IPVS MH scheduler'\nnet/netfilter/ipvs/Kconfig-302-\nnet/netfilter/ipvs/Kconfig:303:config IP_VS_MH_TAB_INDEX\nnet/netfilter/ipvs/Kconfig-304-\tint \"IPVS maglev hashing table index of size (the prime numbers)\"\n"
} |
| 13/2 | 2026/10/18 01:14 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 35602 output: 32 thoughts: 72 Thoughts: Here's my analysis of those kernel config options: **Kernel Configuration Dependencies Deep Dive** Okay, so I need to get a clear picture of how these network filtering components interrelate. My first step is to explicitly trace the dependencies for `BRIDGE_NETFILTER`, `NETFILTER_NETLINK_QUEUE`, `NF_FLOW_TABLE`, `IP_VS`, `NFT_COMPAT`, and `NF_TABLES`. This isn't just about knowing *if* they're dependent, but *how* and to what degree. I want to understand the exact conditions and requirements for each one to be enabled. To do this efficiently, I'll need to go directly to the source: the `net/Kconfig` and `net/netfilter/Kconfig` files. I need to zero in on the relevant sections for each of those symbols, reading the surrounding lines to understand the broader context. I'm looking for clues about the feature interactions and dependencies – *especially* any "depends on" or "select" statements. This should reveal the build-time conditions and how the various options are meant to be configured in relation to each other. Ultimately, I'm aiming for a comprehensive dependency graph to fully grasp these options. |
| 14/2 | 2026/10/18 01:14 | tool | read-file |
0mArgs:{
"File": "net/Kconfig",
"FirstLine": 225,
"LineCount": 25
}Results: {
"Contents": " 225:\t\t If you say N the more unusual ones will not be shown and the\n 226:\t\t basic ones needed by most people will default to 'M'.\n 227:\t\n 228:\t\t If unsure, say Y.\n 229:\t\n 230:\tconfig BRIDGE_NETFILTER\n 231:\t\ttristate \"Bridged IP/ARP packets filtering\"\n 232:\t\tdepends on BRIDGE\n 233:\t\tdepends on NETFILTER \u0026\u0026 INET\n 234:\t\tdepends on NETFILTER_ADVANCED\n 235:\t\tselect NETFILTER_FAMILY_BRIDGE\n 236:\t\tselect SKB_EXTENSIONS\n 237:\t\thelp\n 238:\t\t Enabling this option will let arptables resp. iptables see bridged\n 239:\t\t ARP resp. IP traffic. If you want a bridging firewall, you probably\n 240:\t\t want this option enabled.\n 241:\t\t Enabling or disabling this option doesn't enable or disable\n 242:\t\t ebtables.\n 243:\t\n 244:\t\t If unsure, say N.\n 245:\t\n 246:\tsource \"net/netfilter/Kconfig\"\n 247:\tsource \"net/ipv4/netfilter/Kconfig\"\n 248:\tsource \"net/ipv6/netfilter/Kconfig\"\n 249:\tsource \"net/bridge/netfilter/Kconfig\"\n"
} |
| 15/2 | 2026/10/18 01:14 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 36347 output: 34 thoughts: 29 Thoughts: Here's a summary of my thought process, as requested: **Navigating the Netfilter Configuration Landscape** Okay, I need to get a handle on how something is configured within the netfilter framework. Given my deep knowledge of the kernel's networking stack, I'm thinking the place to start is the configuration file, specifically `net/netfilter/Kconfig`. That's where all the build-time options related to netfilter are defined. I have a hunch that the options I'm interested in are located around the following line numbers. I'll need to check the Kconfig file in detail, but my first stop will be to investigate what is located around lines 54, 586 and 711. Those numbers seem significant given my understanding of how netfilter is structured. Let's dig in and see what's there. |
| 16/2 | 2026/10/18 01:14 | tool | read-file |
0mArgs:{
"File": "net/netfilter/Kconfig",
"FirstLine": 50,
"LineCount": 25
}Results: {
"Contents": " 50:\t\thelp\n 51:\t\t If this option is enabled, the kernel will include support\n 52:\t\t for extended accounting via NFNETLINK.\n 53:\t\n 54:\tconfig NETFILTER_NETLINK_QUEUE\n 55:\t\ttristate \"Netfilter NFQUEUE over NFNETLINK interface\"\n 56:\t\tdepends on NETFILTER_ADVANCED\n 57:\t\tselect NETFILTER_NETLINK\n 58:\t\thelp\n 59:\t\t If this option is enabled, the kernel will include support\n 60:\t\t for queueing packets via NFNETLINK.\n 61:\t\n 62:\tconfig NETFILTER_NETLINK_LOG\n 63:\t\ttristate \"Netfilter LOG over NFNETLINK interface\"\n 64:\t\tdefault m if NETFILTER_ADVANCED=n\n 65:\t\tselect NETFILTER_NETLINK\n 66:\t\thelp\n 67:\t\t If this option is enabled, the kernel will include support\n 68:\t\t for logging packets via NFNETLINK.\n 69:\t\n 70:\t\t This obsoletes the existing ipt_ULOG and ebg_ulog mechanisms,\n 71:\t\t and is also scheduled to replace the old syslog-based ipt_LOG\n 72:\t\t and ip6t_LOG modules.\n 73:\t\n 74:\tconfig NETFILTER_NETLINK_OSF\n"
} |
| 17/2 | 2026/10/18 01:14 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 36945 output: 35 thoughts: 24 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Examining Kconfig for NFT Compatibility** Right, let's dive straight in. My immediate thought is to check `net/netfilter/Kconfig`. This is where the core configuration options for Netfilter are defined, and specifically, I need to look closely at `NFT_COMPAT` and `NF_FLOW_TABLE`. These are the crucial elements. I’m thinking, given the context, that understanding the relationships between these configurations will be key to understanding the state and functionality of the Netfilter implementation. I'm assuming there might be some dependencies or compatibility considerations I need to quickly grasp. I want to build a mental map of how these components are enabled, disabled, or conditionally compiled. Knowing this is the first step towards getting a comprehensive understanding of what’s going on at this level, and how I can control it. So, let's see what the Kconfig files reveal. |
| 18/2 | 2026/10/18 01:14 | tool | read-file |
0mArgs:{
"File": "net/netfilter/Kconfig",
"FirstLine": 585,
"LineCount": 35
}Results: {
"Contents": " 585:\t\n 586:\tconfig NFT_REJECT_INET\n 587:\t\tdepends on NF_TABLES_INET\n 588:\t\tdefault NFT_REJECT\n 589:\t\ttristate\n 590:\t\n 591:\tconfig NFT_COMPAT\n 592:\t\tdepends on NETFILTER_XTABLES\n 593:\t\ttristate \"Netfilter x_tables over nf_tables module\"\n 594:\t\thelp\n 595:\t\t This is required if you intend to use any of existing\n 596:\t\t x_tables match/target extensions over the nf_tables\n 597:\t\t framework.\n 598:\t\n 599:\tconfig NFT_HASH\n 600:\t\ttristate \"Netfilter nf_tables hash module\"\n 601:\t\thelp\n 602:\t\t This option adds the \"hash\" expression that you can use to perform\n 603:\t\t a hash operation on registers.\n 604:\t\n 605:\tconfig NFT_FIB\n 606:\t\ttristate\n 607:\t\n 608:\tconfig NFT_FIB_INET\n 609:\t\tdepends on NF_TABLES_INET\n 610:\t\tdepends on NFT_FIB_IPV4\n 611:\t\tdepends on NFT_FIB_IPV6\n 612:\t\ttristate \"Netfilter nf_tables fib inet support\"\n 613:\t\thelp\n 614:\t\t This option allows using the FIB expression from the inet table.\n 615:\t\t The lookup will be delegated to the IPv4 or IPv6 FIB depending\n 616:\t\t on the protocol of the packet.\n 617:\t\n 618:\tconfig NFT_XFRM\n 619:\t\ttristate \"Netfilter nf_tables xfrm/IPSec security association matching\"\n"
} |
| 19/2 | 2026/10/18 01:14 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 37701 output: 35 thoughts: 15 Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind the expert user: **Deep Dive into NF_FLOW_TABLE** Alright, time to get down to brass tacks. I'm thinking, let's zero in on the `NF_FLOW_TABLE` module, specifically around line 705. Given my experience, I'm already anticipating what might be going on there. It could be related to packet processing, flow identification, maybe some key lookup or update logic within the data structures. I've got a mental map of the surrounding code, so focusing on that area gives me a good starting point to dig into a potential issue or to understand a specific implementation detail. No point in wandering aimlessly. Let's see what the code is doing. |
| 20/2 | 2026/10/18 01:14 | tool | read-file |
0mArgs:{
"File": "net/netfilter/Kconfig",
"FirstLine": 705,
"LineCount": 35
}Results: {
"Contents": " 705:\t\tdepends on NF_FLOW_TABLE\n 706:\t\thelp\n 707:\t\t This option adds the flow table mixed IPv4/IPv6 support.\n 708:\t\n 709:\t\t To compile it as a module, choose M here.\n 710:\t\n 711:\tconfig NF_FLOW_TABLE\n 712:\t\ttristate \"Netfilter flow table module\"\n 713:\t\tdepends on NETFILTER_INGRESS\n 714:\t\tdepends on NF_CONNTRACK\n 715:\t\tdepends on NF_TABLES\n 716:\t\thelp\n 717:\t\t This option adds the flow table core infrastructure.\n 718:\t\n 719:\t\t To compile it as a module, choose M here.\n 720:\t\n 721:\tconfig NF_FLOW_TABLE_PROCFS\n 722:\t\tbool \"Supply flow table statistics in procfs\"\n 723:\t\tdepends on NF_FLOW_TABLE\n 724:\t\tdepends on PROC_FS\n 725:\t\thelp\n 726:\t\t This option enables for the flow table offload statistics\n 727:\t\t to be shown in procfs under net/netfilter/nf_flowtable.\n 728:\t\n 729:\tconfig NETFILTER_XTABLES\n 730:\t\ttristate \"Netfilter Xtables support (required for ip_tables)\"\n 731:\t\tdefault m if NETFILTER_ADVANCED=n\n 732:\t\thelp\n 733:\t\t This is required if you intend to use any of ip_tables,\n 734:\t\t ip6_tables or arp_tables.\n 735:\t\n 736:\tif NETFILTER_XTABLES\n 737:\t\n 738:\tconfig NETFILTER_XTABLES_COMPAT\n 739:\t\tbool \"Netfilter Xtables 32bit support\"\n"
} |
| 21/2 | 2026/10/18 01:14 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 38418 output: 36 thoughts: 11 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Analyzing IPVS Kconfig Dependencies** Alright, let's dive right in. My initial thought process is geared toward a very specific area: the interdependencies within the Kconfig configuration system as it relates to IP Virtual Server (IPVS). I'm not just looking at the obvious options, but the granular details. I need to refresh my mental map of how these components are linked, which Kconfig symbols directly influence IPVS functionality, and, importantly, what the impact of *those* dependencies is. I'm thinking about the potential ripple effects of enabling or disabling certain options, and if I need to do some code archaeology to check for potentially missing dependencies or, conversely, over-dependencies that might be overly restrictive. Time to go spelunking in the Kconfig files! |
| 22/2 | 2026/10/18 01:14 | tool | read-file |
0mArgs:{
"File": "net/netfilter/ipvs/Kconfig",
"FirstLine": 1,
"LineCount": 25
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: GPL-2.0-only\n 2:\t#\n 3:\t# IP Virtual Server configuration\n 4:\t#\n 5:\tmenuconfig IP_VS\n 6:\t\ttristate \"IP virtual server support\"\n 7:\t\tdepends on INET \u0026\u0026 NETFILTER\n 8:\t\tdepends on (NF_CONNTRACK || NF_CONNTRACK=n)\n 9:\t\thelp\n 10:\t\t IP Virtual Server support will let you build a high-performance\n 11:\t\t virtual server based on cluster of two or more real servers. This\n 12:\t\t option must be enabled for at least one of the clustered computers\n 13:\t\t that will take care of intercepting incoming connections to a\n 14:\t\t single IP address and scheduling them to real servers.\n 15:\t\n 16:\t\t Three request dispatching techniques are implemented, they are\n 17:\t\t virtual server via NAT, virtual server via tunneling and virtual\n 18:\t\t server via direct routing. The several scheduling algorithms can\n 19:\t\t be used to choose which server the connection is directed to,\n 20:\t\t thus load balancing can be achieved among the servers. For more\n 21:\t\t information and its administration program, please visit the\n 22:\t\t following URL: \u003chttp://www.linuxvirtualserver.org/\u003e.\n 23:\t\n 24:\t\t If you want to compile it in kernel, say Y. To compile it as a\n 25:\t\t module, choose M here. If unsure, say N.\n"
} |
| 23/2 | 2026/10/18 01:14 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 39037 output: 32 thoughts: 46 Thoughts: Okay, here's my assessment of the situation: **Investigating the NAT Chain in nftables** Right, my first instinct is to zero in on the relevant code for the NAT chain within `nftables`. I'll start by checking the `NFT_NAT` and `NF_TABLES_INET` configurations to see if I can pinpoint the specific source file responsible for the NAT functionality. Specifically, I'm most interested in understanding where the code for `nft_chain_nat.c` resides. Once I've identified the configuration options that enable it, my next move will be to determine precisely where the object file `nft_chain_nat.o` gets built during the compilation process. Understanding the build process helps to determine what conditions need to be met. |
| 24/2 | 2026/10/18 01:14 | tool | grepper |
0mArgs:{
"Expression": "nft_chain_nat",
"PathPrefixes": [
"net/netfilter/Makefile"
]
}Results: {
"Output": "net/netfilter/Makefile=133=obj-$(CONFIG_NFT_SYNPROXY)\t+= nft_synproxy.o\nnet/netfilter/Makefile-134-\nnet/netfilter/Makefile:135:obj-$(CONFIG_NFT_NAT)\t\t+= nft_chain_nat.o\nnet/netfilter/Makefile-136-\n"
} |
| 25/2 | 2026/10/18 01:14 | llm | patch-evaluator |
1mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 39374 output: 32 thoughts: 21 Thoughts: Here's my thought process, as requested: **Checking `CONFIG_NFT_NAT` in `net/netfilter/Kconfig`** Okay, so I need to verify something related to `CONFIG_NFT_NAT`, which, from memory, is part of the Netfilter subsystem. My initial thought is to go straight to the source. The Kconfig files are the standard configuration files in the Linux kernel and contain all the kernel's configuration options. I'm assuming this is to determine something about the Netfilter NAT (Network Address Translation) implementation using nftables, rather than legacy iptables, probably related to its status or features. I'm likely trying to determine if it is enabled, if it depends on anything specific, or perhaps understand the dependencies it might have. Or, I might be looking for its help text to gain a better understanding of what the option actually does. Time to dive into the `net/netfilter/Kconfig` file and find the relevant section. This should be straightforward. |
| 26/2 | 2026/10/18 01:16 | tool | grepper |
0mArgs:{
"Expression": "config NFT_NAT",
"PathPrefixes": [
"net/netfilter/Kconfig"
]
}Results: {
"Output": "net/netfilter/Kconfig=541=config NFT_REDIR\n--\nnet/netfilter/Kconfig-549-\nnet/netfilter/Kconfig:550:config NFT_NAT\nnet/netfilter/Kconfig-551-\tdepends on NF_CONNTRACK\n"
} |
| 27/2 | 2026/10/18 01:16 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 39719 output: 35 thoughts: 20 Thoughts: Here's my take on the thought process, summarized as you requested: **Navigating the Kconfig Labyrinth** Okay, so I'm digging into something netfilter-related, and my mental map immediately points me towards `net/netfilter/Kconfig`. It's almost automatic, that's where I know the configuration options are likely defined for netfilter. Now, let's see, what specifically am I looking for... I'm thinking around line 550, that's a good starting point. That general area usually holds the stuff that's relevant to the current investigation. Time to go hunting! |
| 28/2 | 2026/10/18 01:16 | tool | read-file |
0mArgs:{
"File": "net/netfilter/Kconfig",
"FirstLine": 545,
"LineCount": 20
}Results: {
"Contents": " 545:\t\tselect NF_NAT_REDIRECT\n 546:\t\thelp\n 547:\t\t This options adds the \"redirect\" expression that you can use\n 548:\t\t to perform NAT in the redirect flavour.\n 549:\t\n 550:\tconfig NFT_NAT\n 551:\t\tdepends on NF_CONNTRACK\n 552:\t\tselect NF_NAT\n 553:\t\tdepends on NF_TABLES_IPV4 || NF_TABLES_IPV6\n 554:\t\ttristate \"Netfilter nf_tables nat module\"\n 555:\t\thelp\n 556:\t\t This option adds the \"nat\" expression that you can use to perform\n 557:\t\t typical Network Address Translation (NAT) packet transformations.\n 558:\t\n 559:\tconfig NFT_TUNNEL\n 560:\t\ttristate \"Netfilter nf_tables tunnel module\"\n 561:\t\thelp\n 562:\t\t This option adds the \"tunnel\" expression that you can use to set\n 563:\t\t tunneling policies.\n 564:\t\n"
} |
| 29/2 | 2026/10/18 01:16 | llm | patch-evaluator |
2mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 40198 output: 45 thoughts: 57 Thoughts: Here's my summary, as the expert: **Investigating Tunnel Module Compilation** Okay, so I need to figure out how these IP-in-IP and IPv6-in-IPv6 tunnel modules are getting built. My initial instinct is to dig into the core code, so I'm thinking, "Let's start by looking at the source code itself." Specifically, I'll examine `net/ipv4/ipip.c` and `net/ipv6/ip6_tunnel.c`. Those seem like the logical places to find the actual tunnel implementation. Once I understand the implementation, I need to understand the build process. The natural next step is to trace back the build process. I'll need to know which Makefiles handle the compilation. So, my next thought is, "Where are these modules actually *built*?" I'll check the associated `Makefile` entries. I'll need to review `net/ipv4/Makefile` and `net/ipv6/Makefile` to see how those specific source files are included in the overall build. That should give me a solid understanding of the compilation steps. |
| 30/2 | 2026/10/18 01:19 | tool | grepper |
0mArgs:{
"Expression": "(ipip|ip6_tunnel)",
"PathPrefixes": [
"net/ipv4/Makefile",
"net/ipv6/Makefile"
]
}Results: {
"Output": "net/ipv4/Makefile=24=obj-$(CONFIG_IP_MROUTE_COMMON) += ipmr_base.o\nnet/ipv4/Makefile:25:obj-$(CONFIG_NET_IPIP) += ipip.o\nnet/ipv4/Makefile-26-gre-y := gre_demux.o\n--\nnet/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\n"
} |
| 31/2 | 2026/10/18 01:19 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 40714 output: 40 thoughts: 1379 Thoughts: **Analyzing Changes for Fuzzing Focus** Okay, let's break down these changes to determine the right `FocusSymbols` and `EnableConfigs` for effective fuzzing. We need to identify specific, non-ubiquitous functions that represent the new or altered logic. First, looking at `nfnetlink_queue.c`, the change to `nfqnl_nf_hook_drop` to accept `const struct nf_hook_ops *ops` looks interesting. Using `cmpfn` to flush only matching queue entries is definitely specific. The linked `nf_queue_nf_hook_drop` in `nf_queue.c` should also be considered. These seem like good candidates for `FocusSymbols` related to netfilter queue management, particularly when flushing based on hook registrations/unregistrations. `nf_tables_api.c` is next. The `__rcu` for the `chain_type` array is a core change, and while the related functions `__nft_chain_type_peek`, `nft_chain_type_get`, `__nf_tables_chain_type_lookup` might be relevant, the more interesting entrypoint appears to be `nft_chain_parse_hook`, especially with the calls to `nft_chain_type_get` and the module handling. This looks like a key entry point for nftables chain creation and parsing, but let me check if it is static. *Checks `nft_chain_parse_hook` in the code...* Yes, it's static! But, according to the documentation, we should still identify the non-static, feature-specific caller functions that exercise it, which means that we want to look at `nf_tables_addchain`. We will need to keep this in mind as we decide on `EnableConfigs` to exercise the relevant functionality. The changes in `nft_exthdr.c`, specifically the TCP option checks in `nft_exthdr_tcp_set_eval`, `nft_exthdr_tcp_strip_eval`, and the hardcoded `NFT_EXTHDR_OP_TCPOPT` in `nft_exthdr_tcp_set_init`, are also quite specific. This focuses on nftables' handling of TCP options, and look to be good candidates for `FocusSymbols`. The refactoring in `nf_flow_table_ip.c` related to tunnel pushing, with functions like `nf_flow_tunnel_ip_push`, `nf_flow_tunnel_ip6_push`, `nf_flow_tunnel_push`, `nf_flow_ip_tunnel_pop`, `nf_flow_tuple_encap`, `nf_flow_queue_xmit4`, and `nf_flow_queue_xmit6`, is a pretty clear indication of core changes in the flow table tunnel logic. This also looks like it warrants `FocusSymbols`. The `br_netfilter_hooks.c` changes, with the `brnf_device_event`, `brnf_init_net`, and `br_netfilter_sysctl_exit_net` checks and usage of `brnet->in_userns`, likely pertain to bridge netfilter initialization and event handling. These are feature specific, and should go into the `FocusSymbols`. Changes in `ip_vs_sync.c`, specifically `READ_ONCE(cp->flags)` in `ip_vs_sync_conn` and `ip_vs_sync_conn_v0`, are likely related to synchronization/atomicity in IPVS connection synchronization. Let's include `ip_vs_sync_conn` in the `FocusSymbols`. The `nft_compat_chain_validate_dependency` change in `nft_compat.c` to handle `"raw"` tablename is specifically handling compatibility with raw chains in nftables, so it is a good candidate for `FocusSymbols`. Finally, the `nf_conntrack_core.c` change with the `flush_work(&conntrack_gc_work.dwork.work);` inside `nf_conntrack_cleanup_net_list` likely pertains to conntrack garbage collection. While this is important, it might be a lower priority than other, more feature-specific functions and we'll leave it for now. So, the potential `FocusSymbols` look like: * `nfqnl_nf_hook_drop` * `nf_queue_nf_hook_drop` * `nft_exthdr_tcp_set_eval` * `nft_exthdr_tcp_strip_eval` * `nft_exthdr_tcp_set_init` * `nf_flow_tunnel_push` * `nf_ |
| 32/2 | 2026/10/18 01:19 | tool | grepper |
0mArgs:{
"Expression": "nft_chain_parse_hook",
"PathPrefixes": [
"net/netfilter/nf_tables_api.c"
]
}Results: {
"Output": "net/netfilter/nf_tables_api.c=2559=static int nft_chain_parse_netdev(struct net *net, struct nlattr *tb[],\n--\nnet/netfilter/nf_tables_api.c-2588-\nnet/netfilter/nf_tables_api.c:2589:static int nft_chain_parse_hook(struct net *net,\nnet/netfilter/nf_tables_api.c-2590-\t\t\t\tstruct nft_base_chain *basechain,\n--\nnet/netfilter/nf_tables_api.c=2787=static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 policy,\n--\nnet/netfilter/nf_tables_api.c-2809-\nnet/netfilter/nf_tables_api.c:2810:\t\terr = nft_chain_parse_hook(net, NULL, nla, \u0026hook, family, flags,\nnet/netfilter/nf_tables_api.c-2811-\t\t\t\t\t extack);\n--\nnet/netfilter/nf_tables_api.c=2933=static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,\n--\nnet/netfilter/nf_tables_api.c-2961-\t\tbasechain = nft_base_chain(chain);\nnet/netfilter/nf_tables_api.c:2962:\t\terr = nft_chain_parse_hook(ctx-\u003enet, basechain, nla, \u0026hook,\nnet/netfilter/nf_tables_api.c-2963-\t\t\t\t\t ctx-\u003efamily, flags, extack);\n--\nnet/netfilter/nf_tables_api.c=3277=static int nft_delchain_hook(struct nft_ctx *ctx,\n--\nnet/netfilter/nf_tables_api.c-3291-\nnet/netfilter/nf_tables_api.c:3292:\terr = nft_chain_parse_hook(ctx-\u003enet, basechain, nla, \u0026chain_hook,\nnet/netfilter/nf_tables_api.c-3293-\t\t\t\t ctx-\u003efamily, chain-\u003eflags, extack);\n"
} |
| 33/2 | 2026/10/18 01:19 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 43718 output: 34 thoughts: 296 Thoughts: **Analyzing Netfilter Symbol Exports** Alright, let's dive into this. First, I'll examine the exported symbols from `nf_tables_api.c`. I need to confirm the presence of `nft_register_chain_type` and `nft_unregister_chain_type`. Those are critical for managing chain types. I'll also double-check the static functions `nft_chain_parse_hook`, `__nf_tables_chain_type_lookup`, and `nft_chain_type_get` to see if they're handled as I expect, even though they're not exported directly. Next, I'll move on to `nft_exthdr.c`. I'm especially interested in `nft_exthdr_tcp_set_eval`, `nft_exthdr_tcp_strip_eval`, and `nft_exthdr_tcp_set_init`. Ah, yes, these are function pointers within `nft_exthdr_tcp_set_ops` and `nft_exthdr_tcp_strip_ops`. The compiler *definitely* won't inline these because they are indirect function calls via the `ops` struct. I can be certain they're present in the symbol table. That's good to keep in mind for optimization or analysis. Finally, I'll look at `nf_flow_table_ip.c`. I'm going to quickly review: `nf_flow_tunnel_push`, `nf_flow_ip_tunnel_pop`, `nf_flow_tuple_encap`, `nf_flow_queue_xmit4`, and `nf_flow_queue_xmit6`. My main concern here is `nf_flow_queue_xmit4` and `nf_flow_queue_xmit6`. Are these targets of function pointers *or* are they exported directly? I need to check to be sure, it will affect how I analyze the code. |
| 34/2 | 2026/10/18 01:19 | tool | grepper |
0mArgs:{
"Expression": "nf_flow_queue_xmit",
"PathPrefixes": [
"net/netfilter/"
]
}Results: {
"Output": "net/netfilter/nf_flow_table_ip.c=767=struct nf_flow_xmit {\n--\nnet/netfilter/nf_flow_table_ip.c-774-\nnet/netfilter/nf_flow_table_ip.c:775:static void __nf_flow_queue_xmit(struct net *net, struct sk_buff *skb,\nnet/netfilter/nf_flow_table_ip.c-776-\t\t\t\t struct nf_flow_xmit *xmit)\n--\nnet/netfilter/nf_flow_table_ip.c=793=static unsigned int nf_flow_encap_gso_xmit(struct net *net, struct sk_buff *skb,\n--\nnet/netfilter/nf_flow_table_ip.c-814-\t\t}\nnet/netfilter/nf_flow_table_ip.c:815:\t\t__nf_flow_queue_xmit(net, segs, xmit);\nnet/netfilter/nf_flow_table_ip.c-816-\t}\n--\nnet/netfilter/nf_flow_table_ip.c-820-\nnet/netfilter/nf_flow_table_ip.c:821:static unsigned int nf_flow_queue_xmit(struct net *net, struct sk_buff *skb,\nnet/netfilter/nf_flow_table_ip.c-822-\t\t\t\t struct nf_flow_xmit *xmit)\n--\nnet/netfilter/nf_flow_table_ip.c-831-\nnet/netfilter/nf_flow_table_ip.c:832:\t__nf_flow_queue_xmit(net, skb, xmit);\nnet/netfilter/nf_flow_table_ip.c-833-\n--\nnet/netfilter/nf_flow_table_ip.c-836-\nnet/netfilter/nf_flow_table_ip.c:837:static int nf_flow_queue_xmit4(struct sk_buff *skb,\nnet/netfilter/nf_flow_table_ip.c-838-\t\t\t struct flow_offload_tuple_rhash *tuplehash,\n--\nnet/netfilter/nf_flow_table_ip.c-898-\nnet/netfilter/nf_flow_table_ip.c:899:\treturn nf_flow_queue_xmit(state-\u003enet, skb, \u0026xmit);\nnet/netfilter/nf_flow_table_ip.c-900-}\n--\nnet/netfilter/nf_flow_table_ip.c=903=nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,\n--\nnet/netfilter/nf_flow_table_ip.c-928-\nnet/netfilter/nf_flow_table_ip.c:929:\treturn nf_flow_queue_xmit4(skb, tuplehash, state);\nnet/netfilter/nf_flow_table_ip.c-930-}\n--\nnet/netfilter/nf_flow_table_ip.c=1153=nf_flow_offload_ipv6_lookup(struct nf_flowtable_ctx *ctx,\n--\nnet/netfilter/nf_flow_table_ip.c-1164-\nnet/netfilter/nf_flow_table_ip.c:1165:static int nf_flow_queue_xmit6(struct sk_buff *skb,\nnet/netfilter/nf_flow_table_ip.c-1166-\t\t\t struct flow_offload_tuple_rhash *tuplehash,\n--\nnet/netfilter/nf_flow_table_ip.c-1226-\nnet/netfilter/nf_flow_table_ip.c:1227:\treturn nf_flow_queue_xmit(state-\u003enet, skb, \u0026xmit);\nnet/netfilter/nf_flow_table_ip.c-1228-}\n--\nnet/netfilter/nf_flow_table_ip.c=1231=nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,\n--\nnet/netfilter/nf_flow_table_ip.c-1256-\nnet/netfilter/nf_flow_table_ip.c:1257:\treturn nf_flow_queue_xmit6(skb, tuplehash, state);\nnet/netfilter/nf_flow_table_ip.c-1258-}\n"
} |
| 35/2 | 2026/10/18 01:19 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 45599 output: 36 thoughts: 99 Thoughts: Here's my understanding, as an expert in this area: **Analyzing the Netfilter Flow Offload Path** Okay, so I'm looking at how packets are handled with Netfilter flow offload, specifically around IP and IPv6. From the code, it's clear that `nf_flow_offload_ip_hook` and `nf_flow_offload_ipv6_hook` are central. These hooks ultimately funnel packets to `nf_flow_queue_xmit4` and `nf_flow_queue_xmit6`, which, in turn, are responsible for calling `nf_flow_tunnel_push`. This makes perfect sense; those `xmit` functions likely handle queuing packets for transmission after flow table lookup, and `nf_flow_tunnel_push` would handle any tunnel encapsulation or decapsulation involved. The `nf_flow_offload_ip_hook` is of particular interest as it's exported and used in operations, so it's a key function. I need to dig deeper into the actual workings of `nf_flow_offload_ip_hook` now. I need to confirm its role in the overall process. |
| 36/2 | 2026/10/18 01:19 | tool | grepper |
0mArgs:{
"Expression": "nf_flow_offload_ip_hook",
"PathPrefixes": [
"net/netfilter/"
]
}Results: {
"Output": "net/netfilter/nf_flow_table_inet.c=12=nf_flow_offload_inet_hook(void *priv, struct sk_buff *skb,\n--\nnet/netfilter/nf_flow_table_inet.c-36-\tcase htons(ETH_P_IP):\nnet/netfilter/nf_flow_table_inet.c:37:\t\treturn nf_flow_offload_ip_hook(priv, skb, state);\nnet/netfilter/nf_flow_table_inet.c-38-\tcase htons(ETH_P_IPV6):\n--\nnet/netfilter/nf_flow_table_inet.c=78=static struct nf_flowtable_type flowtable_ipv4 = {\n--\nnet/netfilter/nf_flow_table_inet.c-83-\t.free\t\t= nf_flow_table_free,\nnet/netfilter/nf_flow_table_inet.c:84:\t.hook\t\t= nf_flow_offload_ip_hook,\nnet/netfilter/nf_flow_table_inet.c-85-\t.owner\t\t= THIS_MODULE,\n--\nnet/netfilter/nf_flow_table_ip.c=902=unsigned int\nnet/netfilter/nf_flow_table_ip.c:903:nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,\nnet/netfilter/nf_flow_table_ip.c-904-\t\t\tconst struct nf_hook_state *state)\n--\nnet/netfilter/nf_flow_table_ip.c-930-}\nnet/netfilter/nf_flow_table_ip.c:931:EXPORT_SYMBOL_GPL(nf_flow_offload_ip_hook);\nnet/netfilter/nf_flow_table_ip.c-932-\n"
} |
| 37/2 | 2026/10/18 01:19 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 46449 output: 318 thoughts: 1216 Thoughts: **Analysis of Netfilter Code and Fuzzing Suitability** Okay, so `nf_flow_offload_ip_hook` is GPL-exported. I need to make sure that `nf_queue_nf_hook_drop` and other functions are handled correctly. I've confirmed that `nf_queue_nf_hook_drop` *is* also exported as GPL. Next, I looked at `nf_conntrack_cleanup_net_list` for ubiquity, but that's tied to network namespace teardown, which is not universally present. I'm focusing on functions *specific* to particular netfilter modules or functionalities, not core infrastructure. That includes things like `nf_queue_nf_hook_drop`, the `nft_exthdr_tcp_*` functions (specific to nftables tcp extended headers), `nft_chain_parse_hook`, `ip_vs_sync_conn`, `brnf_device_event` and `nft_compat_chain_validate_dependency`. Now, I'm checking the relevant kernel configurations: `NF_TABLES`, `NF_FLOW_TABLE`, `NETFILTER_NETLINK_QUEUE`, `BRIDGE_NETFILTER`, `IP_VS`, `NFT_COMPAT`, `NET_IPIP`, and `IPV6_TUNNEL`. I'm also making sure I understand any dependencies between these configurations. I've confirmed dependencies for each config. Then, I scanned the list of changed files. I see a pretty comprehensive set of changes covering headers, core netfilter code (`core.c`, `nf_conntrack_core.c`), flow tables, queue, bridge netfilter, IPVS, nftables, and even some tunnel code (`ipip.c`, `ip6_tunnel.c`). I'm also seeing changes in selftests, which is good. I need to make sure I haven't missed anything, and it seems the files are all covered. Now, I'm trying to determine the focus symbols that represent these changes and their significance. I've compiled a list: `nf_queue_nf_hook_drop`, `nfqnl_nf_hook_drop`, `nft_chain_parse_hook`, `nft_exthdr_tcp_set_eval`, `nft_exthdr_tcp_strip_eval`, `nft_exthdr_tcp_set_init`, `nf_flow_tunnel_push`, `nft_compat_chain_validate_dependency`, `brnf_device_event`, and `ip_vs_sync_conn`. These are all fairly targeted functions. Crucially, none of these are generic core functions like memory management, locking primitives, or socket lifecycle handlers. Finally, I'm assessing if the changes are worth fuzzing. The patch series *does* modify executable code in key networking and netfilter subsystems (flow table tunneling encapsulation, nftables chain parsing and module refcounts, tcp exthdr validation, nfnetlink queue selective flush, bridge netfilter user namespace restriction, IPVS sync data race fix). This looks like code that is reachable in standard QEMU/KVM amd64 test environments via netlink and packet sockets. The code in question is worth fuzzing. |
| 38/2 | 2026/10/18 01:20 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"BRIDGE_NETFILTER",
"IP_VS",
"IPV6_TUNNEL",
"NET_IPIP",
"NETFILTER_NETLINK_QUEUE",
"NF_FLOW_TABLE",
"NF_TABLES",
"NFT_COMPAT"
],
"FocusSymbols": [
"brnf_device_event",
"ip_vs_sync_conn",
"nf_flow_tunnel_push",
"nf_queue_nf_hook_drop",
"nfqnl_nf_hook_drop",
"nft_chain_parse_hook",
"nft_compat_chain_validate_dependency",
"nft_exthdr_tcp_set_eval",
"nft_exthdr_tcp_set_init",
"nft_exthdr_tcp_strip_eval"
],
"Reasoning": "The patch series touches several reachable networking and netfilter subsystems, including nf_tables (chain type reference counting and lookup, TCP exthdr validation and initialization), nfnetlink queue (selective hook dropping upon hook unregistration), bridge netfilter (disabling in non-init user namespaces), IPVS (data race fixes in connection sync), and flowtable (tunnel push/pop refactoring and protocol encapsulation for IPIP and IPv6 tunnels). All modified code paths are fully reachable via netlink, network namespaces, and network traffic in standard virtualized environments.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"BRIDGE_NETFILTER",
"IP_VS",
"IPV6_TUNNEL",
"NET_IPIP",
"NETFILTER_NETLINK_QUEUE",
"NF_FLOW_TABLE",
"NF_TABLES",
"NFT_COMPAT"
],
"FocusSymbols": [
"brnf_device_event",
"ip_vs_sync_conn",
"nf_flow_tunnel_push",
"nf_queue_nf_hook_drop",
"nfqnl_nf_hook_drop",
"nft_chain_parse_hook",
"nft_compat_chain_validate_dependency",
"nft_exthdr_tcp_set_eval",
"nft_exthdr_tcp_set_init",
"nft_exthdr_tcp_strip_eval"
],
"Reasoning": "The patch series touches several reachable networking and netfilter subsystems, including nf_tables (chain type reference counting and lookup, TCP exthdr validation and initialization), nfnetlink queue (selective hook dropping upon hook unregistration), bridge netfilter (disabling in non-init user namespaces), IPVS (data race fixes in connection sync), and flowtable (tunnel push/pop refactoring and protocol encapsulation for IPIP and IPv6 tunnels). All modified code paths are fully reachable via netlink, network namespaces, and network traffic in standard virtualized environments.",
"WorthFuzzing": true
} |
| 39/1 | 2026/10/18 01:20 | action | If |
2mArgs:{
"WorthFuzzing": true
}Results: null |
| 40/2 | 2026/10/18 01:20 | agent | kmsan-evaluator |
2mModel:core Args: null Results: {
"KMSANReasoning": "The patch series touches several netfilter and networking subsystems:\n1. `br_netfilter`: Disables bridge netfilter in non-init user namespaces, adding an `in_userns` flag to `struct brnf_net`. The pernet data structure is zero-allocated via standard netns allocation (`kzalloc`).\n2. `nft_exthdr`: Refactors `nft_exthdr_tcp_set_eval` and `nft_exthdr_tcp_strip_eval` to remove temporary local stack buffers (`buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE]`), inspecting the TCP header directly with `skb_header_pointer` and linearizing with `skb_ensure_writable`. No uninitialized stack memory is left or accessed.\n3. `ip_vs_sync`: Fixes a data race where concurrent modification of `cp-\u003eflags` could cause mismatch between message length calculation and copied options by snapshotting `flags` with `READ_ONCE()`.\n4. `nf_flow_table`: Adds an `encap_proto` field to `struct flow_offload_tunnel` and `struct net_device_path` to unify IPv4/IPv6 tunnel offloading. Flow offload structures are zero-initialized via `kmem_cache_zalloc`, and device paths are zero-initialized with `memset()`. The variables in `nf_flow_tunnel_ip_push` and `nf_flow_tunnel_ip6_push` are fully initialized before use.\n5. `nf_tables`, `nf_queue`, `nf_conntrack`: Implements RCU lookups and correct module refcounting for chain types, selective queue entry flushing in `nfqnl_nf_hook_drop`, and flushes conntrack GC work during netns teardown to prevent use-after-free.\n\nNone of the changes introduce uninitialized memory reads, info-leaks across the kernel-user boundary, or conditional branches on uninitialized variables. Any risks in these changes relate to object lifetimes, concurrency, or out-of-bounds access, which are already covered by KASAN, KCSAN, and LOCKDEP in standard test configurations. Spawning a dedicated KMSAN fuzzing session 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 07d7ac72f49783118181ec85a3e13f4dc7c70a24
Author: syz-cluster <triage@syzkaller.com>
Date: Sun Oct 18 01:13:21 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/networking/bridge.rst b/Documentation/networking/bridge.rst
index c1e6ea52c9e59..dded1a6edd606 100644
--- a/Documentation/networking/bridge.rst
+++ b/Documentation/networking/bridge.rst
@@ -277,8 +277,9 @@ Netfilter
=========
The bridge netfilter module is a legacy feature that allows to filter bridged
-packets with iptables and ip6tables. Its use is discouraged. Users should
-consider using nftables for packet filtering.
+packets with iptables and ip6tables. This feature is scheduled for removal and
+not available in user namespaces. Consider using nftables for packet filtering
+instead.
The older ebtables tool is more feature-limited compared to nftables, but
just like nftables it doesn't need this module either to function.
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index f61c972313095..fee3728ad6663 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -911,6 +911,7 @@ struct net_device_path {
};
u8 inner_proto;
+ u8 encap_proto;
} tun;
struct {
enum {
diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h
index 5b611efaa3cd1..5b22d31cb1407 100644
--- a/include/net/netfilter/nf_flow_table.h
+++ b/include/net/netfilter/nf_flow_table.h
@@ -118,6 +118,7 @@ struct flow_offload_tunnel {
};
u8 inner_proto;
+ u8 encap_proto;
};
struct flow_offload_tuple {
diff --git a/include/net/netfilter/nf_queue.h b/include/net/netfilter/nf_queue.h
index fc3e81c07364f..86021c55ba3ce 100644
--- a/include/net/netfilter/nf_queue.h
+++ b/include/net/netfilter/nf_queue.h
@@ -35,7 +35,8 @@ struct nf_queue_entry {
struct nf_queue_handler {
int (*outfn)(struct nf_queue_entry *entry,
unsigned int queuenum);
- void (*nf_hook_drop)(struct net *net);
+ void (*nf_hook_drop)(struct net *net,
+ const struct nf_hook_ops *ops);
};
void nf_register_queue_handler(const struct nf_queue_handler *qh);
diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c
index 0a394e5f43916..e15355ff215a7 100644
--- a/net/bridge/br_netfilter_hooks.c
+++ b/net/bridge/br_netfilter_hooks.c
@@ -54,6 +54,7 @@ static unsigned int brnf_net_id __read_mostly;
struct brnf_net {
bool enabled;
+ bool in_userns;
#ifdef CONFIG_SYSCTL
struct ctl_table_header *ctl_hdr;
@@ -1100,7 +1101,7 @@ static int brnf_device_event(struct notifier_block *unused, unsigned long event,
net = dev_net(dev);
brnet = net_generic(net, brnf_net_id);
- if (brnet->enabled)
+ if (brnet->enabled || brnet->in_userns)
return NOTIFY_OK;
ret = nf_register_net_hooks(net, br_nf_ops, ARRAY_SIZE(br_nf_ops));
@@ -1229,9 +1230,11 @@ static inline void br_netfilter_sysctl_default(struct brnf_net *brnf)
brnf->filter_pppoe_tagged = 0;
brnf->pass_vlan_indev = 0;
}
+#endif
static int br_netfilter_sysctl_init_net(struct net *net)
{
+#ifdef CONFIG_SYSCTL
struct ctl_table *table = brnf_table;
struct brnf_net *brnet;
@@ -1259,25 +1262,39 @@ static int br_netfilter_sysctl_init_net(struct net *net)
return -ENOMEM;
}
-
+#endif
return 0;
}
static void br_netfilter_sysctl_exit_net(struct net *net,
struct brnf_net *brnet)
{
- const struct ctl_table *table = brnet->ctl_hdr->ctl_table_arg;
+#ifdef CONFIG_SYSCTL
+ struct ctl_table_header *header = brnet->ctl_hdr;
+ const struct ctl_table *table;
- unregister_net_sysctl_table(brnet->ctl_hdr);
+ if (!header)
+ return;
+
+ table = header->ctl_table_arg;
+ unregister_net_sysctl_table(header);
if (!net_eq(net, &init_net))
kfree(table);
+#endif
}
static int __net_init brnf_init_net(struct net *net)
{
+ if (net->user_ns != &init_user_ns) {
+ struct brnf_net *brnet = net_generic(net, brnf_net_id);
+
+ brnet->in_userns = true;
+ pr_warn_once("br_netfilter no longer supported in user namespaces\n");
+ return 0;
+ }
+
return br_netfilter_sysctl_init_net(net);
}
-#endif
static void __net_exit brnf_exit_net(struct net *net)
{
@@ -1289,15 +1306,11 @@ static void __net_exit brnf_exit_net(struct net *net)
brnet->enabled = false;
}
-#ifdef CONFIG_SYSCTL
br_netfilter_sysctl_exit_net(net, brnet);
-#endif
}
static struct pernet_operations brnf_net_ops __read_mostly = {
-#ifdef CONFIG_SYSCTL
.init = brnf_init_net,
-#endif
.exit = brnf_exit_net,
.id = &brnf_net_id,
.size = sizeof(struct brnf_net),
@@ -1318,7 +1331,7 @@ static int __init br_netfilter_init(void)
}
RCU_INIT_POINTER(nf_br_ops, &br_ops);
- printk(KERN_NOTICE "Bridge firewalling registered\n");
+ pr_warn("br_netfilter is slated for removal in 2027.\n");
return 0;
}
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c
index 2ffe64e736e08..15189e1fa0682 100644
--- a/net/ipv4/ipip.c
+++ b/net/ipv4/ipip.c
@@ -379,6 +379,7 @@ static int ipip_fill_forward_path(struct net_device_path_ctx *ctx,
path->tun.src_v4.s_addr = tiph->saddr;
path->tun.dst_v4.s_addr = tiph->daddr;
path->tun.inner_proto = IPPROTO_IPIP;
+ path->tun.encap_proto = AF_INET;
path->tun.dst = &rt->dst;
path->dev = ctx->dev;
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index d5ff50a2ac017..64a184bca4677 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -1868,6 +1868,7 @@ static int ip6_tnl_fill_forward_path(struct net_device_path_ctx *ctx,
path->tun.src_v6 = fl6.saddr;
path->tun.dst_v6 = fl6.daddr;
path->tun.inner_proto = IPPROTO_IPV6;
+ path->tun.encap_proto = AF_INET6;
path->tun.dst = dst;
path->dev = ctx->dev;
ctx->dev = dst->dev;
diff --git a/net/netfilter/core.c b/net/netfilter/core.c
index 940dea2663e95..44c856c1e621c 100644
--- a/net/netfilter/core.c
+++ b/net/netfilter/core.c
@@ -529,7 +529,7 @@ static void __nf_unregister_net_hook(struct net *net, int pf,
if (!p)
return;
- nf_queue_nf_hook_drop(net);
+ nf_queue_nf_hook_drop(net, reg);
nf_hook_entries_free(p);
}
diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c
index 45655cff078cb..157f5c8361ab4 100644
--- a/net/netfilter/ipvs/ip_vs_sync.c
+++ b/net/netfilter/ipvs/ip_vs_sync.c
@@ -543,13 +543,15 @@ static void ip_vs_sync_conn_v0(struct netns_ipvs *ipvs, struct ip_vs_conn *cp,
struct ip_vs_sync_conn_v0 *s;
struct ip_vs_sync_buff *buff;
struct ipvs_master_sync_state *ms;
- int id;
+ u32 flags, seq_mask;
unsigned int len;
+ int id;
if (unlikely(cp->af != AF_INET))
return;
+ flags = READ_ONCE(cp->flags);
/* Do not sync ONE PACKET */
- if (cp->flags & IP_VS_CONN_F_ONE_PACKET)
+ if (flags & IP_VS_CONN_F_ONE_PACKET)
return;
if (!ip_vs_sync_conn_needed(ipvs, cp, pkts))
@@ -564,8 +566,8 @@ static void ip_vs_sync_conn_v0(struct netns_ipvs *ipvs, struct ip_vs_conn *cp,
id = select_master_thread_id(ipvs, cp);
ms = &ipvs->ms[id];
buff = ms->sync_buff;
- len = (cp->flags & IP_VS_CONN_F_SEQ_MASK) ? FULL_CONN_SIZE :
- SIMPLE_CONN_SIZE;
+ seq_mask = flags & IP_VS_CONN_F_SEQ_MASK;
+ len = seq_mask ? FULL_CONN_SIZE : SIMPLE_CONN_SIZE;
if (buff) {
m = (struct ip_vs_sync_mesg_v0 *) buff->mesg;
/* Send buffer if it is for v1 */
@@ -597,9 +599,9 @@ static void ip_vs_sync_conn_v0(struct netns_ipvs *ipvs, struct ip_vs_conn *cp,
s->caddr = cp->caddr.ip;
s->vaddr = cp->vaddr.ip;
s->daddr = cp->daddr.ip;
- s->flags = htons(cp->flags & ~IP_VS_CONN_F_HASHED);
+ s->flags = htons(flags & ~IP_VS_CONN_F_HASHED);
s->state = htons(cp->state);
- if (cp->flags & IP_VS_CONN_F_SEQ_MASK) {
+ if (seq_mask) {
struct ip_vs_sync_conn_options *opt =
(struct ip_vs_sync_conn_options *)&s[1];
memcpy(opt, &cp->sync_conn_opt, sizeof(*opt));
@@ -635,6 +637,7 @@ void ip_vs_sync_conn(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, int pkts)
int id;
__u8 *p;
unsigned int len, pe_name_len, pad;
+ u32 flags, seq_mask;
/* Handle old version of the protocol */
if (sysctl_sync_ver(ipvs) == 0) {
@@ -647,6 +650,7 @@ void ip_vs_sync_conn(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, int pkts)
sloop:
if (!ip_vs_sync_conn_needed(ipvs, cp, pkts))
goto control;
+ flags = READ_ONCE(cp->flags);
/* Sanity checks */
pe_name_len = 0;
@@ -674,7 +678,8 @@ void ip_vs_sync_conn(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, int pkts)
#endif
len = sizeof(struct ip_vs_sync_v4);
- if (cp->flags & IP_VS_CONN_F_SEQ_MASK)
+ seq_mask = flags & IP_VS_CONN_F_SEQ_MASK;
+ if (seq_mask)
len += sizeof(struct ip_vs_sync_conn_options) + 2;
if (cp->pe_data_len)
@@ -720,7 +725,7 @@ void ip_vs_sync_conn(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, int pkts)
/* Set message type & copy members */
s->v4.type = (cp->af == AF_INET6 ? STYPE_F_INET6 : 0);
s->v4.ver_size = htons(len & SVER_MASK); /* Version 0 */
- s->v4.flags = htonl(cp->flags & ~IP_VS_CONN_F_HASHED);
+ s->v4.flags = htonl(flags & ~IP_VS_CONN_F_HASHED);
s->v4.state = htons(cp->state);
s->v4.protocol = cp->protocol;
s->v4.cport = cp->cport;
@@ -744,7 +749,7 @@ void ip_vs_sync_conn(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, int pkts)
s->v4.vaddr = cp->vaddr.ip;
s->v4.daddr = cp->daddr.ip;
}
- if (cp->flags & IP_VS_CONN_F_SEQ_MASK) {
+ if (seq_mask) {
*(p++) = IPVS_OPT_SEQ_DATA;
*(p++) = sizeof(struct ip_vs_sync_conn_options);
hton_seq(&cp->in_seq, (struct ip_vs_seq *)p);
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index f9b8927327ba9..931b76aacd934 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -2410,7 +2410,7 @@ nf_ct_iterate_destroy(int (*iter)(struct nf_conn *i, void *data), void *data)
if (atomic_read(&cnet->count) == 0)
continue;
- nf_queue_nf_hook_drop(net);
+ nf_queue_nf_hook_drop(net, NULL);
}
up_read(&net_rwsem);
@@ -2506,6 +2506,11 @@ void nf_conntrack_cleanup_net_list(struct list_head *net_exit_list)
goto i_see_dead_people;
}
+ /* must wait for current scan loop, worker also looks at
+ * struct nf_conntrack_net, which is about to be free'd.
+ */
+ flush_work(&conntrack_gc_work.dwork.work);
+
list_for_each_entry(net, net_exit_list, exit_list) {
warn_on_keymap_list_leak(net);
nf_conntrack_ecache_pernet_fini(net);
diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c
index c8c29a9a16844..e13f9397e8cbd 100644
--- a/net/netfilter/nf_flow_table_ip.c
+++ b/net/netfilter/nf_flow_table_ip.c
@@ -189,22 +189,24 @@ static void nf_flow_tuple_encap(struct nf_flowtable_ctx *ctx,
break;
}
+ if (ctx->tun.inner_proto != IPPROTO_IPIP &&
+ ctx->tun.inner_proto != IPPROTO_IPV6)
+ return;
+
switch (ctx->ether_type) {
case htons(ETH_P_IP):
iph = (struct iphdr *)(skb_network_header(skb) + offset);
- if (ctx->tun.inner_proto == IPPROTO_IPIP) {
- tuple->tun.dst_v4.s_addr = iph->daddr;
- tuple->tun.src_v4.s_addr = iph->saddr;
- tuple->tun.inner_proto = IPPROTO_IPIP;
- }
+ tuple->tun.dst_v4.s_addr = iph->daddr;
+ tuple->tun.src_v4.s_addr = iph->saddr;
+ tuple->tun.inner_proto = ctx->tun.inner_proto;
+ tuple->tun.encap_proto = AF_INET;
break;
case htons(ETH_P_IPV6):
ip6h = (struct ipv6hdr *)(skb_network_header(skb) + offset);
- if (ctx->tun.inner_proto == IPPROTO_IPV6) {
- tuple->tun.dst_v6 = ip6h->daddr;
- tuple->tun.src_v6 = ip6h->saddr;
- tuple->tun.inner_proto = IPPROTO_IPV6;
- }
+ tuple->tun.dst_v6 = ip6h->daddr;
+ tuple->tun.src_v6 = ip6h->saddr;
+ tuple->tun.inner_proto = ctx->tun.inner_proto;
+ tuple->tun.encap_proto = AF_INET6;
break;
default:
break;
@@ -372,6 +374,11 @@ static void nf_flow_ip_tunnel_pop(struct nf_flowtable_ctx *ctx,
skb_pull(skb, ctx->tun.hdr_size);
skb_reset_network_header(skb);
+
+ if (ctx->tun.inner_proto == IPPROTO_IPIP)
+ skb->protocol = htons(ETH_P_IP);
+ else
+ skb->protocol = htons(ETH_P_IPV6);
}
static bool nf_flow_skb_encap_protocol(struct nf_flowtable_ctx *ctx,
@@ -580,23 +587,44 @@ static int nf_flow_pppoe_push(struct sk_buff *skb, u16 id,
return 0;
}
-static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb,
- struct flow_offload_tuple *tuple,
- struct dst_entry *dst, __be32 *ip_daddr)
+static int nf_flow_tunnel_ip_push(struct net *net, struct sk_buff *skb,
+ struct flow_offload_tuple *tuple,
+ struct dst_entry *dst,
+ union nf_inet_addr **ip_addr)
{
- struct iphdr *iph = (struct iphdr *)skb_network_header(skb);
- struct rtable *rt = dst_rtable(dst);
- u8 tos = iph->tos, ttl = iph->ttl;
- __be16 frag_off = iph->frag_off;
- u32 headroom = sizeof(*iph);
+ __be16 frag_off = 0;
+ struct iphdr *iph;
+ u8 tos = 0, ttl;
+ u32 headroom;
int err;
+ switch (tuple->tun.inner_proto) {
+ case IPPROTO_IPV6: {
+ struct ipv6hdr *ip6h;
+
+ ip6h = (struct ipv6hdr *)skb_network_header(skb);
+ tos = ipv6_get_dsfield(ip6h);
+ ttl = ip6h->hop_limit;
+ frag_off = htons(IP_DF);
+ break;
+ }
+ case IPPROTO_IPIP:
+ iph = (struct iphdr *)skb_network_header(skb);
+ frag_off = iph->frag_off;
+ tos = iph->tos;
+ ttl = iph->ttl;
+ break;
+ default:
+ return 0;
+ }
+
err = iptunnel_handle_offloads(skb, SKB_GSO_IPXIP4);
if (err)
return err;
- skb_set_inner_ipproto(skb, IPPROTO_IPIP);
- headroom += LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len;
+ skb_set_inner_ipproto(skb, tuple->tun.inner_proto);
+ headroom = sizeof(*iph) + LL_RESERVED_SPACE(dst->dev) +
+ dst->header_len;
err = skb_cow_head(skb, headroom);
if (err)
return err;
@@ -607,11 +635,12 @@ static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb,
/* Push down and install the IP header. */
skb_push(skb, sizeof(*iph));
skb_reset_network_header(skb);
+ skb->protocol = htons(ETH_P_IP);
iph = ip_hdr(skb);
iph->version = 4;
iph->ihl = sizeof(*iph) >> 2;
- iph->frag_off = ip_mtu_locked(&rt->dst) ? 0 : frag_off;
+ iph->frag_off = ip_mtu_locked(dst) ? 0 : frag_off;
iph->protocol = tuple->tun.inner_proto;
iph->tos = tos;
iph->daddr = tuple->tun.src_v4.s_addr;
@@ -621,81 +650,91 @@ static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb,
__ip_select_ident(net, iph, skb_shinfo(skb)->gso_segs ?: 1);
ip_send_check(iph);
- *ip_daddr = tuple->tun.src_v4.s_addr;
-
- return 0;
-}
-
-static int nf_flow_tunnel_v4_push(struct net *net, struct sk_buff *skb,
- struct flow_offload_tuple *tuple,
- struct dst_entry *dst, __be32 *ip_daddr)
-{
- if (tuple->tun_num)
- return nf_flow_tunnel_ipip_push(net, skb, tuple, dst, ip_daddr);
+ *ip_addr = (union nf_inet_addr *)&tuple->tun.src_v4;
return 0;
}
-static int nf_flow_tunnel_ip6ip6_push(struct net *net, struct sk_buff *skb,
- struct flow_offload_tuple *tuple,
- struct dst_entry *dst,
- struct in6_addr **ip6_daddr)
+static int nf_flow_tunnel_ip6_push(struct net *net, struct sk_buff *skb,
+ struct flow_offload_tuple *tuple,
+ struct dst_entry *dst,
+ union nf_inet_addr **ip_addr)
{
- struct ipv6hdr *ip6h = (struct ipv6hdr *)skb_network_header(skb);
- __u8 dsfield = ipv6_get_dsfield(ip6h);
- struct rtable *rt = dst_rtable(dst);
struct flowi6 fl6 = {
.daddr = tuple->tun.src_v6,
.saddr = tuple->tun.dst_v6,
- .flowi6_proto = IPPROTO_IPV6,
+ .flowi6_proto = tuple->tun.inner_proto,
};
- u8 hop_limit = ip6h->hop_limit;
+ u8 hop_limit, dsfield;
+ struct ipv6hdr *ip6h;
int err, mtu;
u32 headroom;
+ switch (tuple->tun.inner_proto) {
+ case IPPROTO_IPIP: {
+ struct iphdr *iph = (struct iphdr *)skb_network_header(skb);
+
+ dsfield = ipv4_get_dsfield(iph);
+ hop_limit = iph->ttl;
+ break;
+ }
+ case IPPROTO_IPV6:
+ ip6h = (struct ipv6hdr *)skb_network_header(skb);
+ dsfield = ipv6_get_dsfield(ip6h);
+ hop_limit = ip6h->hop_limit;
+ break;
+ default:
+ return 0;
+ }
+
err = iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6);
if (err)
return err;
- skb_set_inner_ipproto(skb, IPPROTO_IPV6);
- headroom = sizeof(*ip6h) + LL_RESERVED_SPACE(rt->dst.dev) +
- rt->dst.header_len;
+ skb_set_inner_ipproto(skb, tuple->tun.inner_proto);
+ headroom = sizeof(*ip6h) + LL_RESERVED_SPACE(dst->dev) +
+ dst->header_len;
err = skb_cow_head(skb, headroom);
if (err)
return err;
skb_scrub_packet(skb, true);
- mtu = dst_mtu(&rt->dst) - sizeof(*ip6h);
+ mtu = dst_mtu(dst) - sizeof(*ip6h);
mtu = max(mtu, IPV6_MIN_MTU);
skb_dst_update_pmtu_no_confirm(skb, mtu);
skb_push(skb, sizeof(*ip6h));
skb_reset_network_header(skb);
+ skb->protocol = htons(ETH_P_IPV6);
ip6h = ipv6_hdr(skb);
ip6_flow_hdr(ip6h, dsfield,
ip6_make_flowlabel(net, skb, fl6.flowlabel, true, &fl6));
ip6h->hop_limit = hop_limit;
- ip6h->nexthdr = IPPROTO_IPV6;
+ ip6h->nexthdr = tuple->tun.inner_proto;
ip6h->daddr = tuple->tun.src_v6;
ip6h->saddr = tuple->tun.dst_v6;
ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(*ip6h));
IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr);
- *ip6_daddr = &tuple->tun.src_v6;
+ *ip_addr = (union nf_inet_addr *)&tuple->tun.src_v6;
return 0;
}
-static int nf_flow_tunnel_v6_push(struct net *net, struct sk_buff *skb,
- struct flow_offload_tuple *tuple,
- struct dst_entry *dst,
- struct in6_addr **ip6_daddr)
+static int nf_flow_tunnel_push(struct net *net, struct sk_buff *skb,
+ struct flow_offload_tuple *tuple,
+ struct dst_entry *dst,
+ union nf_inet_addr **ip_addr)
{
- if (tuple->tun_num)
- return nf_flow_tunnel_ip6ip6_push(net, skb, tuple, dst, ip6_daddr);
-
- return 0;
+ switch (tuple->tun.encap_proto) {
+ case AF_INET:
+ return nf_flow_tunnel_ip_push(net, skb, tuple, dst, ip_addr);
+ case AF_INET6:
+ return nf_flow_tunnel_ip6_push(net, skb, tuple, dst, ip_addr);
+ default:
+ return 0;
+ }
}
static int nf_flow_encap_push(struct sk_buff *skb,
@@ -802,10 +841,10 @@ static int nf_flow_queue_xmit4(struct sk_buff *skb,
struct flow_offload_tuple *other_tuple;
enum flow_offload_tuple_dir dir;
struct nf_flow_xmit xmit = {};
+ union nf_inet_addr *ip_addr;
struct flow_offload *flow;
struct neighbour *neigh;
struct rtable *rt;
- __be32 ip_daddr;
if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) {
rt = dst_rtable(tuplehash->tuple.dst_cache);
@@ -818,10 +857,10 @@ static int nf_flow_queue_xmit4(struct sk_buff *skb,
dir = tuplehash->tuple.dir;
flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
other_tuple = &flow->tuplehash[!dir].tuple;
- ip_daddr = other_tuple->src_v4.s_addr;
+ ip_addr = (union nf_inet_addr *)&other_tuple->src_v4;
- if (nf_flow_tunnel_v4_push(state->net, skb, other_tuple,
- tuplehash->tuple.dst_cache, &ip_daddr) < 0)
+ if (nf_flow_tunnel_push(state->net, skb, other_tuple,
+ tuplehash->tuple.dst_cache, &ip_addr) < 0)
return NF_DROP;
switch (tuplehash->tuple.xmit_type) {
@@ -832,7 +871,7 @@ static int nf_flow_queue_xmit4(struct sk_buff *skb,
flow_offload_teardown(flow);
return NF_DROP;
}
- neigh = ip_neigh_gw4(rt->dst.dev, rt_nexthop(rt, ip_daddr));
+ neigh = ip_neigh_gw4(rt->dst.dev, rt_nexthop(rt, ip_addr->ip));
if (IS_ERR(neigh)) {
flow_offload_teardown(flow);
return NF_DROP;
@@ -1130,7 +1169,7 @@ static int nf_flow_queue_xmit6(struct sk_buff *skb,
struct flow_offload_tuple *other_tuple;
enum flow_offload_tuple_dir dir;
struct nf_flow_xmit xmit = {};
- struct in6_addr *ip6_daddr;
+ union nf_inet_addr *ip_addr;
struct flow_offload *flow;
struct neighbour *neigh;
struct rt6_info *rt;
@@ -1146,11 +1185,10 @@ static int nf_flow_queue_xmit6(struct sk_buff *skb,
dir = tuplehash->tuple.dir;
flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
other_tuple = &flow->tuplehash[!dir].tuple;
- ip6_daddr = &other_tuple->src_v6;
+ ip_addr = (union nf_inet_addr *)&other_tuple->src_v6;
- if (nf_flow_tunnel_v6_push(state->net, skb, other_tuple,
- tuplehash->tuple.dst_cache,
- &ip6_daddr) < 0)
+ if (nf_flow_tunnel_push(state->net, skb, other_tuple,
+ tuplehash->tuple.dst_cache, &ip_addr) < 0)
return NF_DROP;
switch (tuplehash->tuple.xmit_type) {
@@ -1161,7 +1199,7 @@ static int nf_flow_queue_xmit6(struct sk_buff *skb,
flow_offload_teardown(flow);
return NF_DROP;
}
- neigh = ip_neigh_gw6(rt->dst.dev, rt6_nexthop(rt, ip6_daddr));
+ neigh = ip_neigh_gw6(rt->dst.dev, rt6_nexthop(rt, &ip_addr->in6));
if (IS_ERR(neigh)) {
flow_offload_teardown(flow);
return NF_DROP;
diff --git a/net/netfilter/nf_flow_table_path.c b/net/netfilter/nf_flow_table_path.c
index d90013685bf12..841f099434d98 100644
--- a/net/netfilter/nf_flow_table_path.c
+++ b/net/netfilter/nf_flow_table_path.c
@@ -138,6 +138,7 @@ static int nft_dev_path_info(struct net_device_path_stack *stack,
info->tun.dst_v6 = path->tun.dst_v6;
info->tun.inner_proto = path->tun.inner_proto;
info->tun_dst = path->tun.dst;
+ info->tun.encap_proto = path->tun.encap_proto;
info->num_tuns++;
} else {
if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX)
@@ -249,6 +250,7 @@ static int nft_dev_forward_path(const struct nft_pktinfo *pkt,
route->tuple[!dir].in.tun.src_v6 = info.tun.dst_v6;
route->tuple[!dir].in.tun.dst_v6 = info.tun.src_v6;
route->tuple[!dir].in.tun.inner_proto = info.tun.inner_proto;
+ route->tuple[!dir].in.tun.encap_proto = info.tun.encap_proto;
route->tuple[!dir].in.num_tuns = info.num_tuns;
dst_release(route->tuple[dir].dst);
route->tuple[dir].dst = info.tun_dst;
diff --git a/net/netfilter/nf_internals.h b/net/netfilter/nf_internals.h
index 25403023060b6..ce5aff2cae718 100644
--- a/net/netfilter/nf_internals.h
+++ b/net/netfilter/nf_internals.h
@@ -24,7 +24,7 @@
#define CTA_FILTER_FLAG(ctattr) CTA_FILTER_F_ ## ctattr
/* nf_queue.c */
-void nf_queue_nf_hook_drop(struct net *net);
+void nf_queue_nf_hook_drop(struct net *net, const struct nf_hook_ops *ops);
/* nf_log.c */
int __init netfilter_log_init(void);
diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c
index 73363ceedebe3..166d05e994ac6 100644
--- a/net/netfilter/nf_queue.c
+++ b/net/netfilter/nf_queue.c
@@ -129,14 +129,14 @@ bool nf_queue_entry_get_refs(struct nf_queue_entry *entry)
}
EXPORT_SYMBOL_GPL(nf_queue_entry_get_refs);
-void nf_queue_nf_hook_drop(struct net *net)
+void nf_queue_nf_hook_drop(struct net *net, const struct nf_hook_ops *ops)
{
const struct nf_queue_handler *qh;
rcu_read_lock();
qh = rcu_dereference(nf_queue_handler);
if (qh)
- qh->nf_hook_drop(net);
+ qh->nf_hook_drop(net, ops);
rcu_read_unlock();
}
EXPORT_SYMBOL_GPL(nf_queue_nf_hook_drop);
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index b59628e6240c1..59ac623e6805f 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -1036,32 +1036,67 @@ static inline u64 nf_tables_alloc_handle(struct nft_table *table)
return ++table->hgenerator;
}
-static const struct nft_chain_type *chain_type[NFPROTO_NUMPROTO][NFT_CHAIN_T_MAX];
+static const struct nft_chain_type __rcu *chain_type[NFPROTO_NUMPROTO][NFT_CHAIN_T_MAX];
static const struct nft_chain_type *
-__nft_chain_type_get(u8 family, enum nft_chain_types type)
+__nft_chain_type_peek(u8 family, enum nft_chain_types type)
{
if (family >= NFPROTO_NUMPROTO ||
type >= NFT_CHAIN_T_MAX)
return NULL;
- return chain_type[family][type];
+ return rcu_dereference_check(chain_type[family][type],
+ lockdep_nfnl_is_held(NFNL_SUBSYS_NFTABLES));
}
+/**
+ * nft_chain_type_get - look up a chain type by family and id
+ * @family: netlink family
+ * @type: chain type (enum nft_chain_types)
+ *
+ * Return: chain type with a held module ref, or NULL.
+ */
+static const struct nft_chain_type *
+nft_chain_type_get(u8 family, enum nft_chain_types type)
+{
+ const struct nft_chain_type *t;
+
+ rcu_read_lock();
+ t = __nft_chain_type_peek(family, type);
+
+ if (t && !try_module_get(t->owner))
+ t = NULL;
+
+ rcu_read_unlock();
+ return t;
+}
+
+/**
+ * __nf_tables_chain_type_lookup - look up a chain type by name
+ * @nla: nlattr containing the chain-type name
+ * @family: netlink family
+ *
+ * Return: matching chain type with a held module ref, or NULL.
+ */
static const struct nft_chain_type *
__nf_tables_chain_type_lookup(const struct nlattr *nla, u8 family)
{
- const struct nft_chain_type *type;
+ const struct nft_chain_type *type = NULL, *t;
int i;
+ rcu_read_lock();
for (i = 0; i < NFT_CHAIN_T_MAX; i++) {
- type = __nft_chain_type_get(family, i);
- if (!type)
+ t = __nft_chain_type_peek(family, i);
+ if (!t)
continue;
- if (!nla_strcmp(nla, type->name))
- return type;
+
+ if (!nla_strcmp(nla, t->name) && try_module_get(t->owner)) {
+ type = t;
+ break;
+ }
}
- return NULL;
+ rcu_read_unlock();
+ return type;
}
struct nft_module_request {
@@ -1837,11 +1872,11 @@ static void nf_tables_table_destroy(struct nft_table *table)
void nft_register_chain_type(const struct nft_chain_type *ctype)
{
nfnl_lock(NFNL_SUBSYS_NFTABLES);
- if (WARN_ON(__nft_chain_type_get(ctype->family, ctype->type))) {
+ if (WARN_ON(__nft_chain_type_peek(ctype->family, ctype->type))) {
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
return;
}
- chain_type[ctype->family][ctype->type] = ctype;
+ rcu_assign_pointer(chain_type[ctype->family][ctype->type], ctype);
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
}
EXPORT_SYMBOL_GPL(nft_register_chain_type);
@@ -1849,7 +1884,7 @@ EXPORT_SYMBOL_GPL(nft_register_chain_type);
void nft_unregister_chain_type(const struct nft_chain_type *ctype)
{
nfnl_lock(NFNL_SUBSYS_NFTABLES);
- chain_type[ctype->family][ctype->type] = NULL;
+ RCU_INIT_POINTER(chain_type[ctype->family][ctype->type], NULL);
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
}
EXPORT_SYMBOL_GPL(nft_unregister_chain_type);
@@ -2581,10 +2616,6 @@ static int nft_chain_parse_hook(struct net *net,
hook->num = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM]));
hook->priority = ntohl(nla_get_be32(ha[NFTA_HOOK_PRIORITY]));
- type = __nft_chain_type_get(family, NFT_CHAIN_T_DEFAULT);
- if (!type)
- return -EOPNOTSUPP;
-
if (nla[NFTA_CHAIN_TYPE]) {
type = nf_tables_chain_type_lookup(net, nla[NFTA_CHAIN_TYPE],
family, true);
@@ -2592,13 +2623,19 @@ static int nft_chain_parse_hook(struct net *net,
NL_SET_BAD_ATTR(extack, nla[NFTA_CHAIN_TYPE]);
return PTR_ERR(type);
}
+ } else {
+ type = nft_chain_type_get(family, NFT_CHAIN_T_DEFAULT);
+ if (!type)
+ return -EOPNOTSUPP;
}
+
+ err = -EOPNOTSUPP;
if (hook->num >= NFT_MAX_HOOKS || !(type->hook_mask & (1 << hook->num)))
- return -EOPNOTSUPP;
+ goto out_put_type;
if (type->type == NFT_CHAIN_T_NAT &&
hook->priority <= NF_IP_PRI_CONNTRACK)
- return -EOPNOTSUPP;
+ goto out_put_type;
} else {
if (ha[NFTA_HOOK_HOOKNUM]) {
hook->num = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM]));
@@ -2620,30 +2657,27 @@ static int nft_chain_parse_hook(struct net *net,
}
} else {
type = basechain->type;
+ __module_get(type->owner);
}
}
- if (!try_module_get(type->owner)) {
- if (nla[NFTA_CHAIN_TYPE])
- NL_SET_BAD_ATTR(extack, nla[NFTA_CHAIN_TYPE]);
- return -ENOENT;
- }
-
hook->type = type;
INIT_LIST_HEAD(&hook->list);
if (nft_base_chain_netdev(family, hook->num)) {
err = nft_chain_parse_netdev(net, ha, &hook->list, extack, flags);
- if (err < 0) {
- module_put(type->owner);
- return err;
- }
+ if (err < 0)
+ goto out_put_type;
} else if (ha[NFTA_HOOK_DEV] || ha[NFTA_HOOK_DEVS]) {
- module_put(type->owner);
- return -EOPNOTSUPP;
+ err = -EOPNOTSUPP;
+ goto out_put_type;
}
return 0;
+
+out_put_type:
+ module_put(type->owner);
+ return err;
}
static void nft_chain_release_hook(struct nft_chain_hook *hook)
diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c
index a3bc002800510..00b76c25d55c9 100644
--- a/net/netfilter/nfnetlink_queue.c
+++ b/net/netfilter/nfnetlink_queue.c
@@ -1559,9 +1559,39 @@ static struct notifier_block nfqnl_dev_notifier = {
.notifier_call = nfqnl_rcv_dev_event,
};
-static void nfqnl_nf_hook_drop(struct net *net)
+/* Match the entries whose hook_index points into the hook array that
+ * __nf_unregister_net_hook() replaces when it removes @ops.
+ *
+ * ops->pf/hooknum name the hook as registered; entry->state.pf/hook
+ * name the array actually traversed - they differ only for NFPROTO_INET.
+ * Every other family uses the array of its own name.
+ */
+static int nf_hook_cmp(struct nf_queue_entry *entry, unsigned long ops_ptr)
+{
+ const struct nf_hook_ops *ops = (const struct nf_hook_ops *)ops_ptr;
+ unsigned int hooknum = ops->hooknum;
+ u8 pf = ops->pf;
+
+ /* same translation as nf_static_key_inc() */
+ if (pf == NFPROTO_INET && hooknum == NF_INET_INGRESS) {
+ pf = NFPROTO_NETDEV;
+ hooknum = NF_NETDEV_INGRESS;
+ }
+
+ if (entry->state.hook != hooknum)
+ return 0;
+
+ if (pf == NFPROTO_INET)
+ return entry->state.pf == NFPROTO_IPV4 ||
+ entry->state.pf == NFPROTO_IPV6;
+
+ return entry->state.pf == pf;
+}
+
+static void nfqnl_nf_hook_drop(struct net *net, const struct nf_hook_ops *ops)
{
struct nfnl_queue_net *q = nfnl_queue_pernet(net);
+ nfqnl_cmpfn cmpfn = NULL;
int i;
/* This function is also called on net namespace error unwind,
@@ -1574,12 +1604,19 @@ static void nfqnl_nf_hook_drop(struct net *net)
if (!q)
return;
+ /* ops is NULL when the caller is not unregistering a hook but
+ * removing an object queued packets may reference, every entry
+ * has to go
+ */
+ if (ops)
+ cmpfn = nf_hook_cmp;
+
for (i = 0; i < INSTANCE_BUCKETS; i++) {
struct nfqnl_instance *inst;
struct hlist_head *head = &q->instance_table[i];
hlist_for_each_entry_rcu(inst, head, hlist)
- nfqnl_flush(inst, NULL, 0);
+ nfqnl_flush(inst, cmpfn, (unsigned long)ops);
}
}
diff --git a/net/netfilter/nft_chain_nat.c b/net/netfilter/nft_chain_nat.c
index 40e230d8b712e..1133f79d8d253 100644
--- a/net/netfilter/nft_chain_nat.c
+++ b/net/netfilter/nft_chain_nat.c
@@ -131,6 +131,7 @@ static void __exit nft_chain_nat_exit(void)
#ifdef CONFIG_NF_TABLES_INET
nft_unregister_chain_type(&nft_chain_nat_inet);
#endif
+ synchronize_rcu();
}
module_init(nft_chain_nat_init);
diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c
index 63864b9282590..88ddaf47f5879 100644
--- a/net/netfilter/nft_compat.c
+++ b/net/netfilter/nft_compat.c
@@ -14,6 +14,7 @@
#include <linux/netfilter/nf_tables.h>
#include <linux/netfilter/nf_tables_compat.h>
#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter_ipv6/ip6_tables.h>
#include <linux/netfilter_bridge/ebtables.h>
@@ -45,6 +46,20 @@ static int nft_compat_chain_validate_dependency(const struct nft_ctx *ctx,
type = NFT_CHAIN_T_NAT;
if (basechain->type->type != type)
return -EINVAL;
+ } else if (strcmp(tablename, "raw") == 0) {
+ /* iptables-nft emulates the legacy raw table, which only
+ * ever has prerouting/output chains at this exact priority.
+ */
+ if (ctx->family != NFPROTO_IPV4 &&
+ ctx->family != NFPROTO_IPV6)
+ return -EINVAL;
+
+ if (basechain->ops.hooknum != NF_INET_PRE_ROUTING &&
+ basechain->ops.hooknum != NF_INET_LOCAL_OUT)
+ return -EINVAL;
+
+ if (basechain->ops.priority != NF_IP_PRI_RAW)
+ return -EINVAL;
}
return 0;
diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c
index 3c4c2faa73984..5a8db55920364 100644
--- a/net/netfilter/nft_ct.c
+++ b/net/netfilter/nft_ct.c
@@ -542,7 +542,7 @@ static void __nft_ct_set_destroy(const struct nft_ctx *ctx, struct nft_ct *priv)
#endif
#ifdef CONFIG_NF_CONNTRACK_ZONES
case NFT_CT_ZONE:
- nf_queue_nf_hook_drop(ctx->net);
+ nf_queue_nf_hook_drop(ctx->net, NULL);
mutex_lock(&nft_ct_pcpu_mutex);
if (--nft_ct_pcpu_template_refcnt == 0)
nft_ct_tmpl_put_pcpu();
diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c
index 8abf8e8145ce5..861bab93f4352 100644
--- a/net/netfilter/nft_exthdr.c
+++ b/net/netfilter/nft_exthdr.c
@@ -233,16 +233,23 @@ static void nft_exthdr_tcp_set_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
- u8 buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE];
struct nft_exthdr *priv = nft_expr_priv(expr);
unsigned int i, optl, tcphdr_len, offset;
- struct tcphdr *tcph;
+ struct tcphdr *tcph, _tcph;
u8 *opt;
- tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff, &tcphdr_len);
+ if (pkt->tprot != IPPROTO_TCP || pkt->fragoff)
+ goto err;
+
+ tcph = skb_header_pointer(pkt->skb, nft_thoff(pkt), sizeof(_tcph), &_tcph);
if (!tcph)
goto err;
+ tcphdr_len = __tcp_hdrlen(tcph);
+ if (tcphdr_len < sizeof(*tcph) ||
+ tcphdr_len > sizeof(*tcph) + MAX_TCP_OPTION_SPACE)
+ goto err;
+
if (skb_ensure_writable(pkt->skb, nft_thoff(pkt) + tcphdr_len))
goto err;
@@ -313,16 +320,23 @@ static void nft_exthdr_tcp_strip_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
- u8 buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE];
struct nft_exthdr *priv = nft_expr_priv(expr);
unsigned int i, tcphdr_len, optl;
- struct tcphdr *tcph;
+ struct tcphdr *tcph, _tcph;
u8 *opt;
- tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff, &tcphdr_len);
+ if (pkt->tprot != IPPROTO_TCP || pkt->fragoff)
+ goto err;
+
+ tcph = skb_header_pointer(pkt->skb, nft_thoff(pkt), sizeof(_tcph), &_tcph);
if (!tcph)
goto err;
+ tcphdr_len = __tcp_hdrlen(tcph);
+ if (tcphdr_len < sizeof(*tcph) ||
+ tcphdr_len > sizeof(*tcph) + MAX_TCP_OPTION_SPACE)
+ goto err;
+
if (skb_ensure_writable(pkt->skb, nft_thoff(pkt) + tcphdr_len))
goto drop;
@@ -551,7 +565,7 @@ static int nft_exthdr_tcp_set_init(const struct nft_ctx *ctx,
const struct nlattr * const tb[])
{
struct nft_exthdr *priv = nft_expr_priv(expr);
- u32 offset, len, flags = 0, op = NFT_EXTHDR_OP_IPV6;
+ u32 offset, len, flags = 0;
int err;
if (!tb[NFTA_EXTHDR_SREG] ||
@@ -581,15 +595,11 @@ static int nft_exthdr_tcp_set_init(const struct nft_ctx *ctx,
return -EOPNOTSUPP;
}
- err = nft_parse_u32_check(tb[NFTA_EXTHDR_OP], U8_MAX, &op);
- if (err < 0)
- return err;
-
priv->type = nla_get_u8(tb[NFTA_EXTHDR_TYPE]);
priv->offset = offset;
priv->len = len;
priv->flags = flags;
- priv->op = op;
+ priv->op = NFT_EXTHDR_OP_TCPOPT;
return nft_parse_register_load(ctx, tb[NFTA_EXTHDR_SREG], &priv->sreg,
priv->len);
diff --git a/tools/testing/selftests/net/netfilter/conntrack_vrf.sh b/tools/testing/selftests/net/netfilter/conntrack_vrf.sh
index 207b79932d91a..2dbfabaadf4bd 100755
--- a/tools/testing/selftests/net/netfilter/conntrack_vrf.sh
+++ b/tools/testing/selftests/net/netfilter/conntrack_vrf.sh
@@ -84,7 +84,7 @@ busywait $BUSYWAIT_TIMEOUT listener_ready "$ns1"
# as decided by the first iteration of the ruleset.
test_ct_zone_in()
{
-ip netns exec "$ns0" nft -f - <<EOF
+if ! ip netns exec "$ns0" nft -f - <<EOF
table testct {
chain rawpre {
type filter hook prerouting priority raw;
@@ -105,6 +105,12 @@ table testct {
}
}
EOF
+ then
+ echo "FAIL: cannot load testct nft ruleset"
+ ret=1
+ return
+ fi
+
ip netns exec "$ns1" ping -W 1 -c 1 -I veth0 "$IP0" > /dev/null
# should be in zone 1, not zone 2
@@ -113,6 +119,7 @@ EOF
echo "PASS: entry found in conntrack zone 1"
else
echo "FAIL: entry not found in conntrack zone 1"
+ ret=1
count=$(ip netns exec "$ns0" conntrack -L -s $IP1 -d $IP0 -p icmp --zone 2 2> /dev/null | wc -l)
if [ "$count" -eq 1 ]; then
echo "FAIL: entry found in zone 2 instead"
@@ -137,7 +144,7 @@ test_masquerade_vrf()
ip netns exec "$ns0" conntrack -F 2>/dev/null
-ip netns exec "$ns0" nft -f - <<EOF
+if ! ip netns exec "$ns0" nft -f - <<EOF
flush ruleset
table ip nat {
chain rawout {
@@ -158,6 +165,12 @@ table ip nat {
}
}
EOF
+ then
+ echo "FAIL: cannot load ip nat nft ruleset"
+ ret=1
+ return
+ fi
+
if ! ip netns exec "$ns0" ip vrf exec tvrf socat -u -4 STDIN TCP:"$IP1":55555 < /dev/null > /dev/null;then
echo "FAIL: connect failure with masquerade + sport rewrite on vrf device"
ret=1
@@ -184,7 +197,7 @@ EOF
test_masquerade_veth()
{
ip netns exec "$ns0" conntrack -F 2>/dev/null
-ip netns exec "$ns0" nft -f - <<EOF
+if ! ip netns exec "$ns0" nft -f - <<EOF
flush ruleset
table ip nat {
chain postrouting {
@@ -193,6 +206,12 @@ table ip nat {
}
}
EOF
+ then
+ echo "FAIL: cannot load ip nat postrouting ruleset"
+ ret=1
+ return
+ fi
+
if ! ip netns exec "$ns0" ip vrf exec tvrf socat -u -4 STDIN TCP:"$IP1":55555 < /dev/null > /dev/null;then
echo "FAIL: connect failure with masquerade + sport rewrite on veth device"
ret=1
diff --git a/tools/testing/selftests/net/netfilter/nf_queue.c b/tools/testing/selftests/net/netfilter/nf_queue.c
index 8bbec37f53563..cca6c3cfa9e46 100644
--- a/tools/testing/selftests/net/netfilter/nf_queue.c
+++ b/tools/testing/selftests/net/netfilter/nf_queue.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <errno.h>
+#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
@@ -21,6 +22,7 @@ struct options {
bool failopen;
bool out_of_order;
bool bogus_verdict;
+ bool hold;
int verbose;
unsigned int queue_num;
unsigned int timeout;
@@ -34,6 +36,7 @@ static struct options opts;
static void help(const char *p)
{
printf("Usage: %s [-c|-v [-vv] ] [-o] [-O] [-b] [-t timeout] [-q queue_num] [-Qdst_queue ] [ -d ms_delay ] [-G]\n", p);
+ printf(" [-H] (hold verdicts until SIGUSR1 is received)\n");
}
static int parse_attr_cb(const struct nlattr *attr, void *data)
@@ -280,6 +283,7 @@ static int mainloop(void)
uint32_t ooo_ids[16];
unsigned int portid;
int ooo_count = 0;
+ sigset_t hold_set;
char *buf;
int ret;
@@ -289,6 +293,12 @@ static int mainloop(void)
exit(EXIT_FAILURE);
}
+ /* -H: keep SIGUSR1 pending until the first packet is read */
+ sigemptyset(&hold_set);
+ sigaddset(&hold_set, SIGUSR1);
+ if (opts.hold)
+ sigprocmask(SIG_BLOCK, &hold_set, NULL);
+
nl = open_queue();
portid = mnl_socket_get_portid(nl);
@@ -320,6 +330,13 @@ static int mainloop(void)
}
id = ret - MNL_CB_OK;
+ if (opts.hold) {
+ int sig;
+
+ sigwait(&hold_set, &sig);
+ opts.hold = false;
+ }
+
if (opts.delay_ms)
sleep_ms(opts.delay_ms);
@@ -364,7 +381,7 @@ static void parse_opts(int argc, char **argv)
{
int c;
- while ((c = getopt(argc, argv, "chvoObt:q:Q:d:G")) != -1) {
+ while ((c = getopt(argc, argv, "chvoObt:q:Q:d:GH")) != -1) {
switch (c) {
case 'c':
opts.count_packets = true;
@@ -373,6 +390,9 @@ static void parse_opts(int argc, char **argv)
help(argv[0]);
exit(0);
break;
+ case 'H':
+ opts.hold = true;
+ break;
case 'q':
opts.queue_num = atoi(optarg);
if (opts.queue_num > 0xffff)
diff --git a/tools/testing/selftests/net/netfilter/nft_queue.sh b/tools/testing/selftests/net/netfilter/nft_queue.sh
index 7c857a2e0f349..a07652813a4b0 100755
--- a/tools/testing/selftests/net/netfilter/nft_queue.sh
+++ b/tools/testing/selftests/net/netfilter/nft_queue.sh
@@ -92,7 +92,7 @@ load_ruleset() {
ip netns exec "$nsrouter" nft -f /dev/stdin <<EOF
table $family $name {
chain nfq {
- ip protocol icmp queue bypass
+ icmp type { "echo-request", "echo-reply" } queue bypass
icmpv6 type { "echo-request", "echo-reply" } queue num 1 bypass
}
chain pre {
@@ -861,6 +861,113 @@ test_queue_bridge()
test_queue 20 "bridge"
}
+hook_drop_delivered()
+{
+ ip netns exec "$ns1" nft list counter inet nfqscope delivered |
+ sed -n 's/.*packets \([0-9]*\) .*/\1/p'
+}
+
+hook_drop_queued()
+{
+ ip netns exec "$ns1" awk '$1 == 1 { print $3 }' /proc/self/net/netfilter/nfnetlink_queue
+}
+
+hook_drop_all_queued()
+{
+ [ "$(hook_drop_queued)" = "$1" ]
+}
+
+# hook_drop_case <keep|drop> <description> <nft command...>
+#
+# 1. The inet output chain sends udp packets to port 12345 to queue 1,
+# the inet input chain counts them once they are accepted.
+# 2. nf_queue -H reads the packets but sends no verdict until it gets
+# SIGUSR1, so the 20 packets sent stay in the kernel queue.
+# 3. The nft command removes a base chain while they are queued. The
+# queue length in /proc right after it tells if the kernel dropped
+# them: still 20, or 0.
+# 4. SIGUSR1 releases nf_queue. If the kernel kept the packets they are
+# accepted now and reach the input counter. If it dropped them the
+# first verdict fails with ENOENT and nothing is counted.
+hook_drop_case()
+{
+ local expect="$1"
+ local desc="$2"
+ local packets=20
+ local delivered queued result
+ shift 2
+
+ip netns exec "$ns1" nft -f /dev/stdin <<EOF
+flush ruleset
+table inet nfqscope {
+ chain output {
+ type filter hook output priority 0; policy accept;
+ udp dport 12345 queue num 1
+ }
+ chain input {
+ type filter hook input priority 0; policy accept;
+ udp dport 12345 counter name delivered
+ }
+ chain output2 {
+ type filter hook output priority 100; policy accept;
+ }
+ counter delivered {}
+}
+table ip6 unrelated {
+ chain prerouting {
+ type filter hook prerouting priority 0; policy accept;
+ }
+}
+EOF
+ ip netns exec "$ns1" ./nf_queue -c -q 1 -H -t "$timeout" > "$TMPFILE1" 2>&1 &
+ local nfqpid=$!
+
+ busywait "$BUSYWAIT_TIMEOUT" nf_queue_wait "$ns1" 1
+
+ ip netns exec "$ns1" bash -c \
+ "for i in \$(seq $packets); do echo x > /dev/udp/127.0.0.1/12345; done"
+
+ busywait "$BUSYWAIT_TIMEOUT" hook_drop_all_queued "$packets"
+
+ ip netns exec "$ns1" nft "$@"
+ queued=$(hook_drop_queued)
+
+ kill -USR1 "$nfqpid"
+ wait "$nfqpid"
+ delivered=$(hook_drop_delivered)
+
+ if [ "$queued" = "$packets" ] && [ "$delivered" = "$packets" ] &&
+ grep -q "^$packets packets total" "$TMPFILE1"; then
+ result="keep"
+ elif [ "$queued" = "0" ] && [ "$delivered" = "0" ]; then
+ result="drop"
+ else
+ result="inconsistent"
+ fi
+
+ if [ "$result" = "$expect" ]; then
+ echo "PASS: $desc: queued packets $result"
+ else
+ echo "FAIL: $desc: queued packets $result, expected $expect" 1>&2
+ echo " $queued of $packets queued after the change, $delivered delivered" 1>&2
+ ret=1
+ fi
+}
+
+test_hook_drop_scope()
+{
+ # the hook array of another hook point changes, queued packets stay
+ hook_drop_case keep "base chain removed at another hook point" \
+ delete chain ip6 unrelated prerouting
+
+ # the hook array of the queueing hook point changes, the position of
+ # the queued packets in it is stale, they are dropped
+ hook_drop_case drop "base chain removed at the queueing hook point" \
+ delete chain inet nfqscope output2
+
+ ip netns exec "$ns1" nft flush ruleset
+}
+
ip netns exec "$nsrouter" sysctl net.ipv6.conf.all.forwarding=1 > /dev/null
ip netns exec "$nsrouter" sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null
ip netns exec "$nsrouter" sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null
@@ -909,6 +1016,7 @@ test_queue_stress
# should be last, adds vrf device in ns1 and changes routes
test_icmp_vrf
test_queue_removal
+test_hook_drop_scope
# turns router into a bridge
test_queue_bridge
|
| 41/3 | 2026/10/18 01:20 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 20788 output: 40 thoughts: 1309 Thoughts: **KMSAN Fuzzing Session Evaluation** Okay, so I need to decide if this patch series warrants a dedicated KMSAN fuzzing session. Let's break this down systematically. First, I need to thoroughly understand what's being changed across all the files. * **Documentation:** Just documentation updates, so no immediate concerns from a memory safety perspective. * **Header Files (`netdevice.h`, `nf_flow_table.h`):** Adding `u8 encap_proto` to a couple of structs. This is likely related to tunnel encapsulation, potentially introducing new fields to be considered, but doesn't immediately scream fuzzing target on its own. * **Netfilter Core Changes (`nf_queue.c`, `core.c`, etc.):** The signature change for `nf_hook_drop` to take `const struct nf_hook_ops *ops` is interesting. It introduces filtering based on the hook and PF, which adds an additional dimension. This could be relevant later. * **Bridge Filtering (`br_netfilter_hooks.c`):** The user namespace deprecation for bridge filtering is about isolating the filtering and sysctl. * **Tunneling (`ipip.c`, `ip6_tunnel.c`):** Setting `encap_proto` to `AF_INET` or `AF_INET6` in the forward path functions. This aligns with the changes in the header files, but I need to understand the implications of this new field throughout the code. * **IPVS Synchronization (`ip_vs_sync.c`):** The thread safety fix using `READ_ONCE` is positive, avoiding data races is always a good thing. * **Conntrack and Flow Table Interactions (`nf_conntrack_core.c`, `nf_flow_table_ip.c`, `nf_flow_table_path.c`):** Several changes here. Calls to `nf_queue_nf_hook_drop` with `NULL` or after garbage collection, and tunnel encapsulation improvements are what stand out. The tunnel encapsulation refactor (`nf_flow_table_ip.c`) and the use of `union nf_inet_addr` in `nf_flow_queue_xmit4` and `nf_flow_queue_xmit6` are worth a second look, specifically focusing on the new `encap_proto` field's role. The `nf_flow_table_path.c` file explicitly mentions copying the `encap_proto` field, further highlighting its importance. * **Netfilter Internals and Tables (`nf_internals.h`, `nf_tables_api.c`, `nfnetlink_queue.c`, `nft_chain_nat.c`, `nft_compat.c`):** RCU-ification in `nf_tables_api.c` is good for concurrency, and the rest look like general cleanups and API fixes. * **Extended Header Processing (`nft_exthdr.c`):** This is a red flag. The change to how TCP headers are handled in `nft_exthdr_tcp_set_eval` and `nft_exthdr_tcp_strip_eval` raises concerns. Instead of allocating on the stack and copying, it uses `skb_header_pointer` and then `skb_ensure_writable`. It's very important to ensure the code does not read from uninitialized memory after `skb_ensure_writable`. I need to understand what `tcph` and `opt` contain after the `skb_ensure_writable` call to ensure there's no potential for out-of-bounds access or data corruption. * **Selftests:** Selftests are good for this, but don't always cover every edge case. **Decision Process** Given the changes, I'm leaning toward needing to do a KMSAN fuzzing session: 1. **High-Priority:** `nft_exthdr.c`: The TCP header handling modifications in `nft_exthdr.c` demand immediate scrutiny. This is the area that looks the most vulnerable, especially around the interaction with `skb_header_pointer` and `skb_ensure_writable`. This needs to be carefully reviewed, preferably with the help of a disassembler or debugger to see what's actually in memory after the `skb_ensure_writable` call. I'll focus a targeted fuzzing campaign here. 2. **Medium Priority:** `nf_flow_table_ip.c`: Because of the tunnel encapsulation changes and the introduction of a new field (`encap_proto`), I need to check how it's handled across the different codepaths, particularly when used with |
| 42/3 | 2026/10/18 01:20 | tool | read-file |
0mArgs:{
"File": "net/netfilter/nft_exthdr.c",
"FirstLine": 230,
"LineCount": 80
}Results: {
"Contents": " 230:\t}\n 231:\t\n 232:\tstatic void nft_exthdr_tcp_set_eval(const struct nft_expr *expr,\n 233:\t\t\t\t\t struct nft_regs *regs,\n 234:\t\t\t\t\t const struct nft_pktinfo *pkt)\n 235:\t{\n 236:\t\tstruct nft_exthdr *priv = nft_expr_priv(expr);\n 237:\t\tunsigned int i, optl, tcphdr_len, offset;\n 238:\t\tstruct tcphdr *tcph, _tcph;\n 239:\t\tu8 *opt;\n 240:\t\n 241:\t\tif (pkt-\u003etprot != IPPROTO_TCP || pkt-\u003efragoff)\n 242:\t\t\tgoto err;\n 243:\t\n 244:\t\ttcph = skb_header_pointer(pkt-\u003eskb, nft_thoff(pkt), sizeof(_tcph), \u0026_tcph);\n 245:\t\tif (!tcph)\n 246:\t\t\tgoto err;\n 247:\t\n 248:\t\ttcphdr_len = __tcp_hdrlen(tcph);\n 249:\t\tif (tcphdr_len \u003c sizeof(*tcph) ||\n 250:\t\t tcphdr_len \u003e sizeof(*tcph) + MAX_TCP_OPTION_SPACE)\n 251:\t\t\tgoto err;\n 252:\t\n 253:\t\tif (skb_ensure_writable(pkt-\u003eskb, nft_thoff(pkt) + tcphdr_len))\n 254:\t\t\tgoto err;\n 255:\t\n 256:\t\ttcph = (struct tcphdr *)(pkt-\u003eskb-\u003edata + nft_thoff(pkt));\n 257:\t\topt = (u8 *)tcph;\n 258:\t\n 259:\t\tfor (i = sizeof(*tcph); i \u003c tcphdr_len - 1; i += optl) {\n 260:\t\t\tunion {\n 261:\t\t\t\t__be16 v16;\n 262:\t\t\t\t__be32 v32;\n 263:\t\t\t} old, new;\n 264:\t\n 265:\t\t\toptl = optlen(opt, i);\n 266:\t\n 267:\t\t\tif (priv-\u003etype != opt[i])\n 268:\t\t\t\tcontinue;\n 269:\t\n 270:\t\t\tif (i + optl \u003e tcphdr_len || priv-\u003elen + priv-\u003eoffset \u003e optl)\n 271:\t\t\t\tgoto err;\n 272:\t\n 273:\t\t\toffset = i + priv-\u003eoffset;\n 274:\t\n 275:\t\t\tswitch (priv-\u003elen) {\n 276:\t\t\tcase 2:\n 277:\t\t\t\told.v16 = (__force __be16)get_unaligned((u16 *)(opt + offset));\n 278:\t\t\t\tnew.v16 = (__force __be16)nft_reg_load16(\n 279:\t\t\t\t\t\u0026regs-\u003edata[priv-\u003esreg]);\n 280:\t\n 281:\t\t\t\tswitch (priv-\u003etype) {\n 282:\t\t\t\tcase TCPOPT_MSS:\n 283:\t\t\t\t\t/* increase can cause connection to stall */\n 284:\t\t\t\t\tif (ntohs(old.v16) \u003c= ntohs(new.v16))\n 285:\t\t\t\t\t\treturn;\n 286:\t\t\t\tbreak;\n 287:\t\t\t\t}\n 288:\t\n 289:\t\t\t\tif (old.v16 == new.v16)\n 290:\t\t\t\t\treturn;\n 291:\t\n 292:\t\t\t\tput_unaligned(new.v16, (__be16*)(opt + offset));\n 293:\t\t\t\tinet_proto_csum_replace2(\u0026tcph-\u003echeck, pkt-\u003eskb,\n 294:\t\t\t\t\t\t\t old.v16, new.v16, false);\n 295:\t\t\t\tbreak;\n 296:\t\t\tcase 4:\n 297:\t\t\t\tnew.v32 = nft_reg_load_be32(\u0026regs-\u003edata[priv-\u003esreg]);\n 298:\t\t\t\told.v32 = (__force __be32)get_unaligned((u32 *)(opt + offset));\n 299:\t\n 300:\t\t\t\tif (old.v32 == new.v32)\n 301:\t\t\t\t\treturn;\n 302:\t\n 303:\t\t\t\tput_unaligned(new.v32, (__be32*)(opt + offset));\n 304:\t\t\t\tinet_proto_csum_replace4(\u0026tcph-\u003echeck, pkt-\u003eskb,\n 305:\t\t\t\t\t\t\t old.v32, new.v32, false);\n 306:\t\t\t\tbreak;\n 307:\t\t\tdefault:\n 308:\t\t\t\tDEBUG_NET_WARN_ON_ONCE(1);\n 309:\t\t\t\tbreak;\n"
} |
| 43/3 | 2026/10/18 01:20 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 24431 output: 40 thoughts: 25 Thoughts: Here's a summary of my thought process, as requested: **Diving into `nft_exthdr.c`** Okay, so we're looking at network filter extensions and their headers. My intuition is telling me that something might be amiss in how extended headers are being handled within the Netfilter framework, particularly related to a specific issue we are troubleshooting. I've already scrutinized other areas, but I think it's worth revisiting a chunk of code within `nft_exthdr.c`. Let's zero in on lines 310 to 370. This area likely deals with [insert your expert area, e.g., processing specific protocols, handling header length checks, or interacting with a specific hardware offload mechanism]. I'm expecting to find [insert your expert expectation, e.g., potential vulnerabilities related to buffer overflows, off-by-one errors in header parsing, unexpected interactions during chain traversal, or something related to a specific hardware register]. Time to get my hands dirty and see what's actually going on in that specific section of the code! Let's see if our suspicions hold up. |
| 44/3 | 2026/10/18 01:20 | tool | read-file |
0mArgs:{
"File": "net/netfilter/nft_exthdr.c",
"FirstLine": 310,
"LineCount": 60
}Results: {
"Contents": " 310:\t\t\t}\n 311:\t\n 312:\t\t\treturn;\n 313:\t\t}\n 314:\t\treturn;\n 315:\terr:\n 316:\t\tregs-\u003everdict.code = NFT_BREAK;\n 317:\t}\n 318:\t\n 319:\tstatic void nft_exthdr_tcp_strip_eval(const struct nft_expr *expr,\n 320:\t\t\t\t\t struct nft_regs *regs,\n 321:\t\t\t\t\t const struct nft_pktinfo *pkt)\n 322:\t{\n 323:\t\tstruct nft_exthdr *priv = nft_expr_priv(expr);\n 324:\t\tunsigned int i, tcphdr_len, optl;\n 325:\t\tstruct tcphdr *tcph, _tcph;\n 326:\t\tu8 *opt;\n 327:\t\n 328:\t\tif (pkt-\u003etprot != IPPROTO_TCP || pkt-\u003efragoff)\n 329:\t\t\tgoto err;\n 330:\t\n 331:\t\ttcph = skb_header_pointer(pkt-\u003eskb, nft_thoff(pkt), sizeof(_tcph), \u0026_tcph);\n 332:\t\tif (!tcph)\n 333:\t\t\tgoto err;\n 334:\t\n 335:\t\ttcphdr_len = __tcp_hdrlen(tcph);\n 336:\t\tif (tcphdr_len \u003c sizeof(*tcph) ||\n 337:\t\t tcphdr_len \u003e sizeof(*tcph) + MAX_TCP_OPTION_SPACE)\n 338:\t\t\tgoto err;\n 339:\t\n 340:\t\tif (skb_ensure_writable(pkt-\u003eskb, nft_thoff(pkt) + tcphdr_len))\n 341:\t\t\tgoto drop;\n 342:\t\n 343:\t\ttcph = (struct tcphdr *)(pkt-\u003eskb-\u003edata + nft_thoff(pkt));\n 344:\t\topt = (u8 *)tcph;\n 345:\t\n 346:\t\tfor (i = sizeof(*tcph); i \u003c tcphdr_len - 1; i += optl) {\n 347:\t\t\tunsigned int j;\n 348:\t\n 349:\t\t\toptl = optlen(opt, i);\n 350:\t\t\tif (priv-\u003etype != opt[i])\n 351:\t\t\t\tcontinue;\n 352:\t\n 353:\t\t\tif (i + optl \u003e tcphdr_len)\n 354:\t\t\t\tgoto drop;\n 355:\t\n 356:\t\t\tfor (j = 0; j \u003c optl; ++j) {\n 357:\t\t\t\tu16 n = TCPOPT_NOP;\n 358:\t\t\t\tu16 o = opt[i+j];\n 359:\t\n 360:\t\t\t\tif ((i + j) % 2 == 0) {\n 361:\t\t\t\t\to \u003c\u003c= 8;\n 362:\t\t\t\t\tn \u003c\u003c= 8;\n 363:\t\t\t\t}\n 364:\t\t\t\tinet_proto_csum_replace2(\u0026tcph-\u003echeck, pkt-\u003eskb, htons(o),\n 365:\t\t\t\t\t\t\t htons(n), false);\n 366:\t\t\t}\n 367:\t\t\tmemset(opt + i, TCPOPT_NOP, optl);\n 368:\t\t\treturn;\n 369:\t\t}\n"
} |
| 45/3 | 2026/10/18 01:20 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 25624 output: 21 thoughts: 390 Thoughts: **Analyzing the Changes in `nft_exthdr.c`** Okay, let's break down these changes in `nft_exthdr.c`. My initial observation is focused on the removal of the local buffer `buff` and the change in how the TCP header is accessed within the `nft_exthdr_tcp_set_eval` and `nft_exthdr_tcp_strip_eval` functions. Previously, we were using a stack-allocated buffer and a function called `nft_tcp_header_pointer`. Now, the code appears to be using `skb_header_pointer` directly, along with a statically allocated `_tcph` and additional checks. My immediate question is: what exactly was `nft_tcp_header_pointer` doing? It seems the intent was to ensure access to TCP header data within the packet, and handling potential issues. Let's dig deeper and understand its functionality. I'll need to examine the implementation of `nft_tcp_header_pointer` itself to grasp the reasoning behind this refactoring. It's likely the previous implementation had some limitations or inefficiencies that the new approach aims to address. The extra checks on `pkt->tprot` and `pkt->fragoff`, plus the TCP header length validation are also indicative of a potentially more robust method to extract the TCP header. I need to understand this refactoring in the context of network packet processing and the goals of the Netfilter framework. |
| 46/3 | 2026/10/18 01:20 | tool | grepper |
0mArgs:{
"Expression": "nft_tcp_header_pointer"
}Results: {
"Output": "net/netfilter/nft_exthdr.c=168=static void *\nnet/netfilter/nft_exthdr.c:169:nft_tcp_header_pointer(const struct nft_pktinfo *pkt,\nnet/netfilter/nft_exthdr.c-170-\t\t unsigned int len, void *buffer, unsigned int *tcphdr_len)\n--\nnet/netfilter/nft_exthdr.c=188=static void nft_exthdr_tcp_eval(const struct nft_expr *expr,\n--\nnet/netfilter/nft_exthdr.c-198-\nnet/netfilter/nft_exthdr.c:199:\ttcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff, \u0026tcphdr_len);\nnet/netfilter/nft_exthdr.c-200-\tif (!tcph)\n"
} |
| 47/3 | 2026/10/18 01:20 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 26574 output: 40 thoughts: 29 Thoughts: Okay, let's dive into this. Here's my take on those lines of code, presented as if they were my own internal thought process: **Diving into `nft_tcp_header_pointer` in `nft_exthdr.c`** Alright, time to get my hands dirty with this `nft_exthdr.c` code. I need to understand how the TCP header is being handled within the Netfilter framework. Let's zero in on `nft_tcp_header_pointer`, specifically around line 168. *Why* is this function important? What role does it play in this specific eBPF context or rule evaluation? Is it just calculating an offset, or something more complex like validating the header's presence or data integrity? My initial assumption is that this will be pointing to the start of the TCP header within the packet data, perhaps after some initial parsing of IP headers and potentially extensions. I'll be looking for clues in the surrounding code regarding the input arguments – is it a packet buffer? An offset? And more importantly, I need to see what this pointer is used *for* later in the process. Is it being dereferenced, used as an input to another function, or simply passed along? Context is key. I'll need to follow the data flow to fully grasp the significance of this pointer. |
| 48/3 | 2026/10/18 01:20 | tool | read-file |
0mArgs:{
"File": "net/netfilter/nft_exthdr.c",
"FirstLine": 165,
"LineCount": 40
}Results: {
"Contents": " 165:\t\tregs-\u003everdict.code = NFT_BREAK;\n 166:\t}\n 167:\t\n 168:\tstatic void *\n 169:\tnft_tcp_header_pointer(const struct nft_pktinfo *pkt,\n 170:\t\t\t unsigned int len, void *buffer, unsigned int *tcphdr_len)\n 171:\t{\n 172:\t\tstruct tcphdr *tcph;\n 173:\t\n 174:\t\tif (pkt-\u003etprot != IPPROTO_TCP || pkt-\u003efragoff)\n 175:\t\t\treturn NULL;\n 176:\t\n 177:\t\ttcph = skb_header_pointer(pkt-\u003eskb, nft_thoff(pkt), sizeof(*tcph), buffer);\n 178:\t\tif (!tcph)\n 179:\t\t\treturn NULL;\n 180:\t\n 181:\t\t*tcphdr_len = __tcp_hdrlen(tcph);\n 182:\t\tif (*tcphdr_len \u003c sizeof(*tcph) || *tcphdr_len \u003e len)\n 183:\t\t\treturn NULL;\n 184:\t\n 185:\t\treturn skb_header_pointer(pkt-\u003eskb, nft_thoff(pkt), *tcphdr_len, buffer);\n 186:\t}\n 187:\t\n 188:\tstatic void nft_exthdr_tcp_eval(const struct nft_expr *expr,\n 189:\t\t\t\t\tstruct nft_regs *regs,\n 190:\t\t\t\t\tconst struct nft_pktinfo *pkt)\n 191:\t{\n 192:\t\tu8 buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE];\n 193:\t\tstruct nft_exthdr *priv = nft_expr_priv(expr);\n 194:\t\tunsigned int i, optl, tcphdr_len, offset;\n 195:\t\tu32 *dest = \u0026regs-\u003edata[priv-\u003edreg];\n 196:\t\tstruct tcphdr *tcph;\n 197:\t\tu8 *opt;\n 198:\t\n 199:\t\ttcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff, \u0026tcphdr_len);\n 200:\t\tif (!tcph)\n 201:\t\t\tgoto err;\n 202:\t\n 203:\t\topt = (u8 *)tcph;\n 204:\t\tfor (i = sizeof(*tcph); i \u003c tcphdr_len - 1; i += optl) {\n"
} |
| 49/3 | 2026/10/18 01:20 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 27586 output: 20 thoughts: 1337 Thoughts: **Analyzing Recent Netfilter Changes** Okay, let's break down these Netfilter changes. First, looking at `nft_exthdr_tcp_set_eval` and `nft_exthdr_tcp_strip_eval`, I see a clear optimization. Previously, there was this uninitialized stack buffer, `buff`, used as a temporary holding space. It looks like it was only a scratchpad, though, so it's good that it's gone. The code now directly uses `struct tcphdr *tcph, _tcph;` with `skb_header_pointer` and `skb_ensure_writable`. It's much cleaner, eliminating the unnecessary stack copy and avoiding any potential for uninitialized memory reads. This is a solid, clean improvement. Now, let's dive into the `ip_vs_sync.c` changes. I see the addition of `READ_ONCE` and local variables to cache flag values. Ah, now I remember the issues with `cp->flags` being read multiple times. Without proper synchronization or local caching, there was a race condition where another thread could modify those flags, leading to inconsistent lengths during the sync process. This could cause out-of-bounds writes or inconsistencies in the packet length. By reading `flags` once and storing it in a local variable, `flags`, and deriving `seq_mask` from this local copy, the code guarantees consistency between length calculations and serialization, eliminating that potential race and ensuring the synchronization messages are correctly constructed and handled. No uninitialized memory reads were introduced. This is a very smart and correct fix. Finally, I'll take a look at the changes in `nf_flow_table_ip.c`, `nf_flow_table_path.c`, `ipip.c`, and `ip6_tunnel.c`. The changes to `net_device_path` and `flow_offload_tunnel` look interesting, with the addition of `encap_proto`. I'll need to dig deeper into the allocation and initialization of `struct flow_offload_tunnel` and `struct flow_offload_tuple`. I'll look for where `flow_offload_alloc` or similar functions are defined and how `encap_proto` is being used, especially around tunnel encapsulation, to understand the implications of this change. |
| 50/3 | 2026/10/18 01:21 | tool | grepper |
0mArgs:{
"Expression": "flow_offload_alloc"
}Results: {
"Output": "include/net/netfilter/nf_flow_table.h=219=struct nf_flow_route {\n--\ninclude/net/netfilter/nf_flow_table.h-243-\ninclude/net/netfilter/nf_flow_table.h:244:struct flow_offload *flow_offload_alloc(struct nf_conn *ct);\ninclude/net/netfilter/nf_flow_table.h-245-void flow_offload_free(struct flow_offload *flow);\n--\nnet/netfilter/nf_flow_table_core.c=22=flow_offload_fill_dir(struct flow_offload *flow,\n--\nnet/netfilter/nf_flow_table_core.c-52-\nnet/netfilter/nf_flow_table_core.c:53:struct flow_offload *flow_offload_alloc(struct nf_conn *ct)\nnet/netfilter/nf_flow_table_core.c-54-{\n--\nnet/netfilter/nf_flow_table_core.c-76-}\nnet/netfilter/nf_flow_table_core.c:77:EXPORT_SYMBOL_GPL(flow_offload_alloc);\nnet/netfilter/nf_flow_table_core.c-78-\n--\nnet/netfilter/nf_flow_table_offload.c=860=static void nf_flow_offload_destroy(struct nf_flow_rule *flow_rule[])\n--\nnet/netfilter/nf_flow_table_offload.c-867-\nnet/netfilter/nf_flow_table_offload.c:868:static int nf_flow_offload_alloc(const struct flow_offload_work *offload,\nnet/netfilter/nf_flow_table_offload.c-869-\t\t\t\t struct nf_flow_rule *flow_rule[])\n--\nnet/netfilter/nf_flow_table_offload.c=973=static void flow_offload_work_add(struct flow_offload_work *offload)\n--\nnet/netfilter/nf_flow_table_offload.c-977-\nnet/netfilter/nf_flow_table_offload.c:978:\terr = nf_flow_offload_alloc(offload, flow_rule);\nnet/netfilter/nf_flow_table_offload.c-979-\tif (err \u003c 0)\n--\nnet/netfilter/nft_flow_offload.c=50=static void nft_flow_offload_eval(const struct nft_expr *expr,\n--\nnet/netfilter/nft_flow_offload.c-111-\nnet/netfilter/nft_flow_offload.c:112:\tflow = flow_offload_alloc(ct);\nnet/netfilter/nft_flow_offload.c-113-\tif (!flow)\n--\nnet/sched/act_ct.c=424=static void tcf_ct_flow_table_add(struct tcf_ct_flow_table *ct_ft,\n--\nnet/sched/act_ct.c-435-\t/* NULL if ct is dying (raced flush) or the atomic alloc failed. */\nnet/sched/act_ct.c:436:\tentry = flow_offload_alloc(ct);\nnet/sched/act_ct.c-437-\tif (!entry)\n"
} |
| 51/3 | 2026/10/18 01:21 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 30291 output: 41 thoughts: 29 Thoughts: Okay, let's dive into this. I need to understand how the flow offload memory allocation works, so I'll start by examining the `flow_offload_alloc` function. I know it's located in `net/netfilter/nf_flow_table_core.c`. Let's see how it allocates the necessary resources for offloading flow entries. I'll pay close attention to the data structures involved, the memory management techniques used, and any potential locking mechanisms. I need to understand this deeply to optimize it, so I'll look for any potential bottlenecks or areas for improvement in this allocation process. Let's get to it. |
| 52/3 | 2026/10/18 01:21 | tool | read-file |
0mArgs:{
"File": "net/netfilter/nf_flow_table_core.c",
"FirstLine": 50,
"LineCount": 40
}Results: {
"Contents": " 50:\t\t}\n 51:\t}\n 52:\t\n 53:\tstruct flow_offload *flow_offload_alloc(struct nf_conn *ct)\n 54:\t{\n 55:\t\tstruct flow_offload *flow;\n 56:\t\n 57:\t\tif (unlikely(nf_ct_is_dying(ct)))\n 58:\t\t\treturn NULL;\n 59:\t\n 60:\t\tflow = kmem_cache_zalloc(flow_offload_cachep, GFP_ATOMIC);\n 61:\t\tif (!flow)\n 62:\t\t\treturn NULL;\n 63:\t\n 64:\t\trefcount_inc(\u0026ct-\u003ect_general.use);\n 65:\t\tflow-\u003ect = ct;\n 66:\t\n 67:\t\tflow_offload_fill_dir(flow, FLOW_OFFLOAD_DIR_ORIGINAL);\n 68:\t\tflow_offload_fill_dir(flow, FLOW_OFFLOAD_DIR_REPLY);\n 69:\t\n 70:\t\tif (ct-\u003estatus \u0026 IPS_SRC_NAT)\n 71:\t\t\t__set_bit(NF_FLOW_SNAT, \u0026flow-\u003eflags);\n 72:\t\tif (ct-\u003estatus \u0026 IPS_DST_NAT)\n 73:\t\t\t__set_bit(NF_FLOW_DNAT, \u0026flow-\u003eflags);\n 74:\t\n 75:\t\treturn flow;\n 76:\t}\n 77:\tEXPORT_SYMBOL_GPL(flow_offload_alloc);\n 78:\t\n 79:\tstatic u32 flow_offload_dst_cookie(struct flow_offload_tuple *flow_tuple)\n 80:\t{\n 81:\t\tif (flow_tuple-\u003el3proto == NFPROTO_IPV6)\n 82:\t\t\treturn rt6_get_cookie(dst_rt6_info(flow_tuple-\u003edst_cache));\n 83:\t\n 84:\t\treturn 0;\n 85:\t}\n 86:\t\n 87:\tstatic struct dst_entry *nft_route_dst_fetch(struct nf_flow_route *route,\n 88:\t\t\t\t\t\t enum flow_offload_tuple_dir dir)\n 89:\t{\n"
} |
| 53/3 | 2026/10/18 01:21 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 31076 output: 22 thoughts: 85 Thoughts: Here's my thought process, as if I were considering this code: **Analyzing Flow Offload Allocation and Network Device Path Initialization** Okay, so `flow_offload_alloc` is using `kmem_cache_zalloc`. That's good; a zeroed allocation means I don't need to worry about garbage data in the newly allocated memory. It's a key detail for security and predictability, and it'll save me from having to explicitly zero out the memory myself later, assuming the intention is for clean state. Now I want to understand how the `net_device_path` structure is being initialized in this context. I'll need to figure out which pieces of this path are being setup and when. My immediate focus will be on the relevant structures, so I'll be looking for either `struct net_device_path_ctx` or `struct net_device_path_stack`, or anything calling `ipip_fill_forward_path`. These will be key entry points to understanding the control flow and data flow related to the network device path and its allocation/initialization. This search will lead me to the relevant initialization code, and I'll see where this path is utilized and how it is updated. |
| 54/3 | 2026/10/18 01:21 | tool | grepper |
0mArgs:{
"Expression": "ipip_fill_forward_path"
}Results: {
"Output": "net/ipv4/ipip.c=341=ipip_tunnel_ctl(struct net_device *dev, struct ip_tunnel_parm_kern *p, int cmd)\n--\nnet/ipv4/ipip.c-355-\nnet/ipv4/ipip.c:356:static int ipip_fill_forward_path(struct net_device_path_ctx *ctx,\nnet/ipv4/ipip.c-357-\t\t\t\t struct net_device_path *path)\n--\nnet/ipv4/ipip.c=391=static const struct net_device_ops ipip_netdev_ops = {\n--\nnet/ipv4/ipip.c-399-\t.ndo_tunnel_ctl\t= ipip_tunnel_ctl,\nnet/ipv4/ipip.c:400:\t.ndo_fill_forward_path = ipip_fill_forward_path,\nnet/ipv4/ipip.c-401-};\n"
} |
| 55/3 | 2026/10/18 01:21 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 31696 output: 21 thoughts: 30 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Analyzing `dev_fill_forward_path` and `net_device_path_stack`** Right, let's dig into this. I need to understand the control flow, specifically how the `dev_fill_forward_path` function is utilized and, crucially, how `net_device_path_stack` gets its initial state. This is fundamental to tracing packet paths and understanding any potential issues there. I'm thinking I'll start by tracing all the calls to `dev_fill_forward_path` and pay close attention to the context of each call to see how the stack is set up beforehand. Then I can examine the initialization process of `net_device_path_stack` to ensure there are no surprises with the device path array's starting points and sizes. It's a key area to assess from a performance and correctness perspective, because poorly constructed paths can create excessive processing demands, or worse, introduce pathing loops. I'll be looking for any potential race conditions, memory corruption issues, or initialization errors that could manifest later on. I'll need to look for any scenarios where it could be corrupted, by other modules or during the lifetime of the call. |
| 56/3 | 2026/10/18 01:21 | tool | grepper |
0mArgs:{
"Expression": "dev_fill_forward_path"
}Results: {
"Output": "drivers/net/ethernet/airoha/airoha_ppe.c=282=static int airoha_ppe_get_wdma_info(struct net_device *dev, const u8 *addr,\n--\ndrivers/net/ethernet/airoha/airoha_ppe.c-297-\trcu_read_lock();\ndrivers/net/ethernet/airoha/airoha_ppe.c:298:\terr = dev_fill_forward_path(\u0026ctx, \u0026stack);\ndrivers/net/ethernet/airoha/airoha_ppe.c-299-\trcu_read_unlock();\n--\ndrivers/net/ethernet/airoha/airoha_ppe.c-312-err_out:\ndrivers/net/ethernet/airoha/airoha_ppe.c:313:\tdev_fill_forward_path_release(\u0026stack);\ndrivers/net/ethernet/airoha/airoha_ppe.c-314-\n--\ndrivers/net/ethernet/mediatek/mtk_ppe_offload.c=92=mtk_flow_get_wdma_info(struct net_device *dev, const u8 *addr, struct mtk_wdma_info *info)\n--\ndrivers/net/ethernet/mediatek/mtk_ppe_offload.c-109-\trcu_read_lock();\ndrivers/net/ethernet/mediatek/mtk_ppe_offload.c:110:\terr = dev_fill_forward_path(\u0026ctx, \u0026stack);\ndrivers/net/ethernet/mediatek/mtk_ppe_offload.c-111-\trcu_read_unlock();\n--\ndrivers/net/ethernet/mediatek/mtk_ppe_offload.c-126-err_out:\ndrivers/net/ethernet/mediatek/mtk_ppe_offload.c:127:\tdev_fill_forward_path_release(\u0026stack);\ndrivers/net/ethernet/mediatek/mtk_ppe_offload.c-128-\n--\ninclude/linux/netdevice.h=3438=int dev_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb);\ninclude/linux/netdevice.h:3439:int dev_fill_forward_path(struct net_device_path_ctx *ctx,\ninclude/linux/netdevice.h-3440-\t\t\t struct net_device_path_stack *stack);\ninclude/linux/netdevice.h:3441:void dev_fill_forward_path_release(struct net_device_path_stack *stack);\ninclude/linux/netdevice.h-3442-struct net_device *dev_get_by_name(struct net *net, const char *name);\n--\nnet/8021q/vlan_dev.c=744=static int vlan_dev_get_iflink(const struct net_device *dev)\n--\nnet/8021q/vlan_dev.c-750-\nnet/8021q/vlan_dev.c:751:static int vlan_dev_fill_forward_path(struct net_device_path_ctx *ctx,\nnet/8021q/vlan_dev.c-752-\t\t\t\t struct net_device_path *path)\n--\nnet/8021q/vlan_dev.c=1065=static const struct net_device_ops vlan_netdev_ops = {\n--\nnet/8021q/vlan_dev.c-1096-\t.ndo_get_iflink\t\t= vlan_dev_get_iflink,\nnet/8021q/vlan_dev.c:1097:\t.ndo_fill_forward_path\t= vlan_dev_fill_forward_path,\nnet/8021q/vlan_dev.c-1098-\t.ndo_hwtstamp_get\t= vlan_hwtstamp_get,\n--\nnet/core/dev.c=779=static struct net_device_path *dev_fwd_path(struct net_device_path_stack *stack)\n--\nnet/core/dev.c-786-\nnet/core/dev.c:787:void dev_fill_forward_path_release(struct net_device_path_stack *stack)\nnet/core/dev.c-788-{\n--\nnet/core/dev.c-805-}\nnet/core/dev.c:806:EXPORT_SYMBOL_GPL(dev_fill_forward_path_release);\nnet/core/dev.c-807-\nnet/core/dev.c:808:int dev_fill_forward_path(struct net_device_path_ctx *ctx,\nnet/core/dev.c-809-\t\t\t struct net_device_path_stack *stack)\n--\nnet/core/dev.c-844-err_out:\nnet/core/dev.c:845:\tdev_fill_forward_path_release(stack);\nnet/core/dev.c-846-\n--\nnet/core/dev.c-848-}\nnet/core/dev.c:849:EXPORT_SYMBOL_GPL(dev_fill_forward_path);\nnet/core/dev.c-850-\n--\nnet/mac80211/iface.c=1009=static const struct net_device_ops ieee80211_monitorif_ops = {\n--\nnet/mac80211/iface.c-1018-\nnet/mac80211/iface.c:1019:static int ieee80211_netdev_fill_forward_path(struct net_device_path_ctx *ctx,\nnet/mac80211/iface.c-1020-\t\t\t\t\t struct net_device_path *path)\n--\nnet/mac80211/iface.c=1084=static const struct net_device_ops ieee80211_dataif_8023_ops = {\n--\nnet/mac80211/iface.c-1090-\t.ndo_set_mac_address\t= ieee80211_change_mac,\nnet/mac80211/iface.c:1091:\t.ndo_fill_forward_path\t= ieee80211_netdev_fill_forward_path,\nnet/mac80211/iface.c-1092-\t.ndo_setup_tc\t\t= ieee80211_netdev_setup_tc,\n--\nnet/netfilter/nf_flow_table_path.c=36=static bool nft_is_valid_ether_device(const struct net_device *dev)\n--\nnet/netfilter/nf_flow_table_path.c-44-\nnet/netfilter/nf_flow_table_path.c:45:static int nft_dev_fill_forward_path(const struct dst_entry *dst_cache,\nnet/netfilter/nf_flow_table_path.c-46-\t\t\t\t const struct nf_conn *ct,\n--\nnet/netfilter/nf_flow_table_path.c-81-\nnet/netfilter/nf_flow_table_path.c:82:\treturn dev_fill_forward_path(\u0026ctx, stack);\nnet/netfilter/nf_flow_table_path.c-83-}\n--\nnet/netfilter/nf_flow_table_path.c=105=static int nft_dev_path_info(struct net_device_path_stack *stack,\n--\nnet/netfilter/nf_flow_table_path.c-202-err_out:\nnet/netfilter/nf_flow_table_path.c:203:\tdev_fill_forward_path_release(stack);\nnet/netfilter/nf_flow_table_path.c-204-\n--\nnet/netfilter/nf_flow_table_path.c=225=static int nft_dev_forward_path(const struct nft_pktinfo *pkt,\n--\nnet/netfilter/nf_flow_table_path.c-236-\nnet/netfilter/nf_flow_table_path.c:237:\tif (nft_dev_fill_forward_path(dst, ct, dir, ha, pkt-\u003eethertype, \u0026stack) \u003c 0 ||\nnet/netfilter/nf_flow_table_path.c-238-\t nft_dev_path_info(\u0026stack, \u0026info, ha, ft) \u003c 0)\n"
} |
| 57/3 | 2026/10/18 01:21 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 34166 output: 35 thoughts: 22 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Diving into the Network Forwarding Path** Okay, so we need to trace the path packets take, and I'm thinking the `net/core/dev.c` file is a good starting point, particularly around the `dev_fwd_path` function or macro. My understanding is that this is where a lot of the core forwarding logic is managed, and it likely handles the initial decision-making about where a packet is destined to go within the network stack, or off it. I want to see if this is the correct place. I need to refresh my memory, confirm that this is where the forwarding decisions are made, and understand the surrounding logic. I'm focusing on the forwarding path initially, and I want to see if my assumption is correct. |
| 58/3 | 2026/10/18 01:21 | tool | read-file |
0mArgs:{
"File": "net/core/dev.c",
"FirstLine": 775,
"LineCount": 40
}Results: {
"Contents": " 775:\t\treturn dev-\u003enetdev_ops-\u003endo_fill_metadata_dst(dev, skb);\n 776:\t}\n 777:\tEXPORT_SYMBOL_GPL(dev_fill_metadata_dst);\n 778:\t\n 779:\tstatic struct net_device_path *dev_fwd_path(struct net_device_path_stack *stack)\n 780:\t{\n 781:\t\tif (stack-\u003enum_paths + 1 \u003e NET_DEVICE_PATH_STACK_MAX)\n 782:\t\t\treturn NULL;\n 783:\t\n 784:\t\treturn \u0026stack-\u003epath[stack-\u003enum_paths];\n 785:\t}\n 786:\t\n 787:\tvoid dev_fill_forward_path_release(struct net_device_path_stack *stack)\n 788:\t{\n 789:\t\tstruct net_device_path *path;\n 790:\t\tint k;\n 791:\t\n 792:\t\tif (stack-\u003enum_paths == 0)\n 793:\t\t\treturn;\n 794:\t\n 795:\t\tfor (k = stack-\u003enum_paths - 1; k \u003e= 0; k--) {\n 796:\t\t\tpath = \u0026stack-\u003epath[k];\n 797:\t\t\tswitch (path-\u003etype) {\n 798:\t\t\tcase DEV_PATH_TUN:\n 799:\t\t\t\tdst_release(path-\u003etun.dst);\n 800:\t\t\t\tbreak;\n 801:\t\t\tdefault:\n 802:\t\t\t\tbreak;\n 803:\t\t\t}\n 804:\t\t}\n 805:\t}\n 806:\tEXPORT_SYMBOL_GPL(dev_fill_forward_path_release);\n 807:\t\n 808:\tint dev_fill_forward_path(struct net_device_path_ctx *ctx,\n 809:\t\t\t\t struct net_device_path_stack *stack)\n 810:\t{\n 811:\t\tconst struct net_device *last_dev;\n 812:\t\tstruct net_device_path *path;\n 813:\t\tint ret = 0;\n 814:\t\n"
} |
| 59/3 | 2026/10/18 01:21 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 34981 output: 35 thoughts: 15 Thoughts: Okay, here's my attempt at summarizing the thought process, tailored for an expert audience, and written in the first person: **Diving into `dev_fill_forward_path`** Alright, let's get into this `dev_fill_forward_path` function and see what's what. I need to understand its behavior within this broader context. What's the core functionality it's handling? I'm anticipating this is a critical component for [**mention the expected area, e.g., propagation, optimization, or a specific model aspect**]. I'll be looking for how it handles different data structures, potential edge cases, and of course, any hints on the algorithm's complexity. My mind's already ticking over possible performance bottlenecks that might become an issue down the line, so I'm paying close attention to any nested loops or memory allocation strategies. I expect to be assessing how it’s designed to handle parallelism or asynchronous operations to maximize throughput and minimize latency. Now, let’s dig in and get to the details. |
| 60/3 | 2026/10/18 01:21 | tool | read-file |
0mArgs:{
"File": "net/core/dev.c",
"FirstLine": 815,
"LineCount": 40
}Results: {
"Contents": " 815:\t\tstack-\u003enum_paths = 0;\n 816:\t\twhile (ctx-\u003edev \u0026\u0026 ctx-\u003edev-\u003enetdev_ops-\u003endo_fill_forward_path) {\n 817:\t\t\tlast_dev = ctx-\u003edev;\n 818:\t\t\tpath = dev_fwd_path(stack);\n 819:\t\t\tif (!path)\n 820:\t\t\t\tgoto err_out;\n 821:\t\n 822:\t\t\tmemset(path, 0, sizeof(struct net_device_path));\n 823:\t\t\tret = ctx-\u003edev-\u003enetdev_ops-\u003endo_fill_forward_path(ctx, path);\n 824:\t\t\tif (ret \u003c 0)\n 825:\t\t\t\tgoto err_out;\n 826:\t\n 827:\t\t\tstack-\u003enum_paths++;\n 828:\t\t\tif (last_dev == ctx-\u003edev)\n 829:\t\t\t\tgoto err_out;\n 830:\t\t}\n 831:\t\n 832:\t\tif (!ctx-\u003edev)\n 833:\t\t\treturn ret;\n 834:\t\n 835:\t\tpath = dev_fwd_path(stack);\n 836:\t\tif (!path)\n 837:\t\t\tgoto err_out;\n 838:\t\n 839:\t\tpath-\u003etype = DEV_PATH_ETHERNET;\n 840:\t\tpath-\u003edev = ctx-\u003edev;\n 841:\t\tstack-\u003enum_paths++;\n 842:\t\n 843:\t\treturn 0;\n 844:\terr_out:\n 845:\t\tdev_fill_forward_path_release(stack);\n 846:\t\n 847:\t\treturn -1;\n 848:\t}\n 849:\tEXPORT_SYMBOL_GPL(dev_fill_forward_path);\n 850:\t\n 851:\t/* must be called under rcu_read_lock(), as we dont take a reference */\n 852:\tstatic struct napi_struct *napi_by_id(unsigned int napi_id)\n 853:\t{\n 854:\t\tunsigned int hash = napi_id % HASH_SIZE(napi_hash);\n"
} |
| 61/3 | 2026/10/18 01:21 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 35832 output: 29 thoughts: 1341 Thoughts: **Analyzing Tunnel Handling in Netfilter Flow Offload** Okay, let's break this down. First, I see that the `path` structure is consistently zeroed out before being used in `ndo_fill_forward_path`, which is good. That's a solid defensive programming practice. Now, I'm checking the tunnel encapsulation protocol: `AF_INET` for IPv4 tunnels and `AF_INET6` for IPv6 tunnels. It's properly set in `ipip_fill_forward_path` and `ip6_tnl_fill_forward_path`, respectively. Now I'm diving into `nf_flow_table_ip.c` to see how that `encap_proto` is actually *used*. Ah, in `nf_flow_tuple_encap`, if it's an IP tunnel (IPIP or IPv6), it sets `tuple->tun.encap_proto` to either `AF_INET` or `AF_INET6` based on the inner packet's Ethernet type. Crucially, it populates `tuple->tun.dst_v4/dst_v6` and `tuple->tun.src_v4/src_v6` accordingly. Next, `nf_flow_tunnel_push` dispatches based on `encap_proto`. If it's IPv4, it calls `nf_flow_tunnel_ip_push`; if it's IPv6, it calls `nf_flow_tunnel_ip6_push`. If not, it returns 0. I need to keep that in mind, I need to check how that zero is handled. Okay, let's examine the behavior of `nf_flow_tunnel_push`, and how it uses that `ip_addr` pointer. In `nf_flow_tunnel_ip_push`, `*ip_addr` gets set to `&tuple->tun.src_v4`, and in `nf_flow_tunnel_ip6_push` it's set to `&tuple->tun.src_v6`. So, the return value of zero *without* modifying `ip_addr` is potentially interesting, I need to ensure that the initializations are appropriate, here I'm investigating `nf_flow_queue_xmit4` and `nf_flow_queue_xmit6`... Yes, this is correct; in both cases, `ip_addr` is initialized to point to the *other* tuple's source IP (v4 or v6). If `nf_flow_tunnel_push` *doesn't* touch it, it keeps pointing to that initial location, but if there *is* a tunnel, `ip_addr` gets reassigned. The subsequent calls to `ip_neigh_gw4` or `ip_neigh_gw6` use `ip_addr` to get the neighbor. Alright, so the tunnel encapsulation and IP address retrieval look solid, and the `ip_addr` initialization scheme should always give us valid input to `neigh = ip_neigh_gw4/6`. Now, let's switch gears and investigate potential hook drops related to `nf_queue`. Checking `nf_queue.h`, I see that the drop function is `nf_hook_drop`, that's expected. I'm focusing on the hooks and checking where values are read from the `nf_queue_entry`. It uses `entry->state.hook` and `entry->state.pf`. These members are *initialized* when the queue entry is created, so all is good here. Everything looks consistent and correct. |
| 62/3 | 2026/10/18 01:22 | tool | grepper |
0mArgs:{
"Expression": "entry-\u003estate",
"PathPrefixes": [
"net/netfilter/"
]
}Results: {
"Output": "net/netfilter/nf_queue.c=59=static void nf_queue_entry_release_refs(struct nf_queue_entry *entry)\nnet/netfilter/nf_queue.c-60-{\nnet/netfilter/nf_queue.c:61:\tstruct nf_hook_state *state = \u0026entry-\u003estate;\nnet/netfilter/nf_queue.c-62-\n--\nnet/netfilter/nf_queue.c=84=static void __nf_queue_entry_init_physdevs(struct nf_queue_entry *entry)\n--\nnet/netfilter/nf_queue.c-91-\tif (nf_bridge_info_exists(skb)) {\nnet/netfilter/nf_queue.c:92:\t\tentry-\u003ephysin = nf_bridge_get_physindev(skb, entry-\u003estate.net);\nnet/netfilter/nf_queue.c-93-\t\tentry-\u003ephysout = nf_bridge_get_physoutdev(skb);\n--\nnet/netfilter/nf_queue.c-98-\nnet/netfilter/nf_queue.c:99:\tif (entry-\u003estate.pf == NFPROTO_BRIDGE \u0026\u0026\nnet/netfilter/nf_queue.c-100-\t dst \u0026\u0026 (dst-\u003eflags \u0026 DST_FAKE_RTABLE))\n--\nnet/netfilter/nf_queue.c=112=bool nf_queue_entry_get_refs(struct nf_queue_entry *entry)\nnet/netfilter/nf_queue.c-113-{\nnet/netfilter/nf_queue.c:114:\tstruct nf_hook_state *state = \u0026entry-\u003estate;\nnet/netfilter/nf_queue.c-115-\n--\nnet/netfilter/nf_queue.c=144=static void nf_ip_saveroute(const struct sk_buff *skb,\n--\nnet/netfilter/nf_queue.c-148-\nnet/netfilter/nf_queue.c:149:\tif (entry-\u003estate.hook == NF_INET_LOCAL_OUT) {\nnet/netfilter/nf_queue.c-150-\t\tconst struct iphdr *iph = ip_hdr(skb);\n--\nnet/netfilter/nf_queue.c=159=static void nf_ip6_saveroute(const struct sk_buff *skb,\n--\nnet/netfilter/nf_queue.c-163-\nnet/netfilter/nf_queue.c:164:\tif (entry-\u003estate.hook == NF_INET_LOCAL_OUT) {\nnet/netfilter/nf_queue.c-165-\t\tconst struct ipv6hdr *iph = ipv6_hdr(skb);\n--\nnet/netfilter/nf_queue.c=173=static int __nf_queue(struct sk_buff *skb, const struct nf_hook_state *state,\n--\nnet/netfilter/nf_queue.c-232-\nnet/netfilter/nf_queue.c:233:\tswitch (entry-\u003estate.pf) {\nnet/netfilter/nf_queue.c-234-\tcase AF_INET:\n--\nnet/netfilter/nfnetlink_queue.c=310=static int nf_ip_reroute(struct sk_buff *skb, const struct nf_queue_entry *entry)\n--\nnet/netfilter/nfnetlink_queue.c-314-\nnet/netfilter/nfnetlink_queue.c:315:\tif (entry-\u003estate.hook == NF_INET_LOCAL_OUT) {\nnet/netfilter/nfnetlink_queue.c-316-\t\tconst struct iphdr *iph = ip_hdr(skb);\n--\nnet/netfilter/nfnetlink_queue.c-321-\t\t iph-\u003esaddr == rt_info-\u003esaddr))\nnet/netfilter/nfnetlink_queue.c:322:\t\t\treturn ip_route_me_harder(entry-\u003estate.net, entry-\u003estate.sk,\nnet/netfilter/nfnetlink_queue.c-323-\t\t\t\t\t\t skb, RTN_UNSPEC);\n--\nnet/netfilter/nfnetlink_queue.c=329=static int nf_ip6_reroute(struct sk_buff *skb,\n--\nnet/netfilter/nfnetlink_queue.c-333-\nnet/netfilter/nfnetlink_queue.c:334:\tif (entry-\u003estate.hook == NF_INET_LOCAL_OUT) {\nnet/netfilter/nfnetlink_queue.c-335-\t\tconst struct ipv6hdr *iph = ipv6_hdr(skb);\n--\nnet/netfilter/nfnetlink_queue.c-339-\t\t skb-\u003emark != rt_info-\u003emark)\nnet/netfilter/nfnetlink_queue.c:340:\t\t\treturn nf_ip6_route_me_harder(entry-\u003estate.net,\nnet/netfilter/nfnetlink_queue.c:341:\t\t\t\t\t\t entry-\u003estate.sk, skb);\nnet/netfilter/nfnetlink_queue.c-342-\t}\n--\nnet/netfilter/nfnetlink_queue.c=346=static int nf_reroute(struct sk_buff *skb, struct nf_queue_entry *entry)\n--\nnet/netfilter/nfnetlink_queue.c-349-\nnet/netfilter/nfnetlink_queue.c:350:\tswitch (entry-\u003estate.pf) {\nnet/netfilter/nfnetlink_queue.c-351-\tcase AF_INET:\n--\nnet/netfilter/nfnetlink_queue.c=362=static void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict)\n--\nnet/netfilter/nfnetlink_queue.c-371-\nnet/netfilter/nfnetlink_queue.c:372:\tnet = entry-\u003estate.net;\nnet/netfilter/nfnetlink_queue.c:373:\tpf = entry-\u003estate.pf;\nnet/netfilter/nfnetlink_queue.c-374-\nnet/netfilter/nfnetlink_queue.c:375:\thooks = nf_hook_entries_head(net, pf, entry-\u003estate.hook);\nnet/netfilter/nfnetlink_queue.c-376-\n--\nnet/netfilter/nfnetlink_queue.c-387-\tif (verdict == NF_REPEAT)\nnet/netfilter/nfnetlink_queue.c:388:\t\tverdict = nf_hook_entry_hookfn(hook_entry, skb, \u0026entry-\u003estate);\nnet/netfilter/nfnetlink_queue.c-389-\n--\nnet/netfilter/nfnetlink_queue.c-397-\t\t++i;\nnet/netfilter/nfnetlink_queue.c:398:\t\tverdict = nf_iterate(skb, \u0026entry-\u003estate, hooks, \u0026i);\nnet/netfilter/nfnetlink_queue.c-399-\t}\n--\nnet/netfilter/nfnetlink_queue.c-404-\t\tlocal_bh_disable();\nnet/netfilter/nfnetlink_queue.c:405:\t\tentry-\u003estate.okfn(entry-\u003estate.net, entry-\u003estate.sk, skb);\nnet/netfilter/nfnetlink_queue.c-406-\t\tlocal_bh_enable();\n--\nnet/netfilter/nfnetlink_queue.c-408-\tcase NF_QUEUE:\nnet/netfilter/nfnetlink_queue.c:409:\t\terr = nf_queue(skb, \u0026entry-\u003estate, i, verdict);\nnet/netfilter/nfnetlink_queue.c-410-\t\tif (err == 1)\n--\nnet/netfilter/nfnetlink_queue.c=467=static bool nf_bridge_ports_valid(const struct nf_queue_entry *entry)\n--\nnet/netfilter/nfnetlink_queue.c-473-#endif\nnet/netfilter/nfnetlink_queue.c:474:\tif (entry-\u003estate.pf != PF_BRIDGE)\nnet/netfilter/nfnetlink_queue.c-475-\t\treturn true;\nnet/netfilter/nfnetlink_queue.c-476-\nnet/netfilter/nfnetlink_queue.c:477:\tif (!nf_bridge_port_valid(entry-\u003estate.in) ||\nnet/netfilter/nfnetlink_queue.c:478:\t !nf_bridge_port_valid(entry-\u003estate.out))\nnet/netfilter/nfnetlink_queue.c-479-\t\treturn false;\n--\nnet/netfilter/nfnetlink_queue.c=484=static void nfqnl_reinject(struct nf_queue_entry *entry, unsigned int verdict)\n--\nnet/netfilter/nfnetlink_queue.c-498-\t\tif (ct_hook)\nnet/netfilter/nfnetlink_queue.c:499:\t\t\tct_verdict = ct_hook-\u003eupdate(entry-\u003estate.net, entry-\u003eskb);\nnet/netfilter/nfnetlink_queue.c-500-\t\trcu_read_unlock();\n--\nnet/netfilter/nfnetlink_queue.c=622=static u32 nfqnl_get_bridge_size(struct nf_queue_entry *entry)\n--\nnet/netfilter/nfnetlink_queue.c-627-\nnet/netfilter/nfnetlink_queue.c:628:\tif (entry-\u003estate.pf != PF_BRIDGE || !skb_mac_header_was_set(entskb))\nnet/netfilter/nfnetlink_queue.c-629-\t\treturn 0;\n--\nnet/netfilter/nfnetlink_queue.c=642=static int nfqnl_put_bridge(struct nf_queue_entry *entry, struct sk_buff *skb)\n--\nnet/netfilter/nfnetlink_queue.c-646-\nnet/netfilter/nfnetlink_queue.c:647:\tif (entry-\u003estate.pf != PF_BRIDGE || !skb_mac_header_was_set(entskb))\nnet/netfilter/nfnetlink_queue.c-648-\t\treturn 0;\n--\nnet/netfilter/nfnetlink_queue.c=712=nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,\n--\nnet/netfilter/nfnetlink_queue.c-756-\nnet/netfilter/nfnetlink_queue.c:757:\tif (entry-\u003estate.hook \u003c= NF_INET_FORWARD ||\nnet/netfilter/nfnetlink_queue.c:758:\t (entry-\u003estate.hook == NF_INET_POST_ROUTING \u0026\u0026 entskb-\u003esk == NULL))\nnet/netfilter/nfnetlink_queue.c-759-\t\tcsum_verify = !skb_csum_unnecessary(entskb);\n--\nnet/netfilter/nfnetlink_queue.c-762-\nnet/netfilter/nfnetlink_queue.c:763:\toutdev = entry-\u003estate.out;\nnet/netfilter/nfnetlink_queue.c-764-\n--\nnet/netfilter/nfnetlink_queue.c-816-\t\t\t nfnl_msg_type(NFNL_SUBSYS_QUEUE, NFQNL_MSG_PACKET),\nnet/netfilter/nfnetlink_queue.c:817:\t\t\t 0, entry-\u003estate.pf, NFNETLINK_V0,\nnet/netfilter/nfnetlink_queue.c-818-\t\t\t htons(queue-\u003equeue_num));\n--\nnet/netfilter/nfnetlink_queue.c-827-\tpmsg-\u003ehw_protocol\t= entskb-\u003eprotocol;\nnet/netfilter/nfnetlink_queue.c:828:\tpmsg-\u003ehook\t\t= entry-\u003estate.hook;\nnet/netfilter/nfnetlink_queue.c-829-\t*packet_id_ptr\t\t= \u0026pmsg-\u003epacket_id;\nnet/netfilter/nfnetlink_queue.c-830-\nnet/netfilter/nfnetlink_queue.c:831:\tindev = entry-\u003estate.in;\nnet/netfilter/nfnetlink_queue.c-832-\tif (indev) {\n--\nnet/netfilter/nfnetlink_queue.c-836-#else\nnet/netfilter/nfnetlink_queue.c:837:\t\tif (entry-\u003estate.pf == PF_BRIDGE) {\nnet/netfilter/nfnetlink_queue.c-838-\t\t\t/* Case 1: indev is physical input device, we need to\n--\nnet/netfilter/nfnetlink_queue.c-867-#else\nnet/netfilter/nfnetlink_queue.c:868:\t\tif (entry-\u003estate.pf == PF_BRIDGE) {\nnet/netfilter/nfnetlink_queue.c-869-\t\t\t/* Case 1: outdev is physical output device, we need to\n--\nnet/netfilter/nfnetlink_queue.c-919-\nnet/netfilter/nfnetlink_queue.c:920:\tif (entry-\u003estate.hook \u003c= NF_INET_FORWARD \u0026\u0026 tstamp) {\nnet/netfilter/nfnetlink_queue.c-921-\t\tstruct nfqnl_msg_packet_timestamp ts;\n--\nnet/netfilter/nfnetlink_queue.c=1114=nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum)\n--\nnet/netfilter/nfnetlink_queue.c-1120-\tint err = -ENOBUFS;\nnet/netfilter/nfnetlink_queue.c:1121:\tstruct net *net = entry-\u003estate.net;\nnet/netfilter/nfnetlink_queue.c-1122-\tstruct nfnl_queue_net *q = nfnl_queue_pernet(net);\n--\nnet/netfilter/nfnetlink_queue.c-1133-\nnet/netfilter/nfnetlink_queue.c:1134:\tswitch (entry-\u003estate.pf) {\nnet/netfilter/nfnetlink_queue.c-1135-\tcase NFPROTO_IPV4:\n--\nnet/netfilter/nfnetlink_queue.c=1499=dev_cmp(struct nf_queue_entry *entry, unsigned long ifindex)\n--\nnet/netfilter/nfnetlink_queue.c-1514-\t\treturn 1;\nnet/netfilter/nfnetlink_queue.c:1515:\tif (entry-\u003estate.in)\nnet/netfilter/nfnetlink_queue.c:1516:\t\tif (entry-\u003estate.in-\u003eifindex == ifindex)\nnet/netfilter/nfnetlink_queue.c-1517-\t\t\treturn 1;\nnet/netfilter/nfnetlink_queue.c:1518:\tif (entry-\u003estate.out)\nnet/netfilter/nfnetlink_queue.c:1519:\t\tif (entry-\u003estate.out-\u003eifindex == ifindex)\nnet/netfilter/nfnetlink_queue.c-1520-\t\t\treturn 1;\n--\nnet/netfilter/nfnetlink_queue.c=1558=static struct notifier_block nfqnl_dev_notifier = {\n--\nnet/netfilter/nfnetlink_queue.c-1564- *\nnet/netfilter/nfnetlink_queue.c:1565: * ops-\u003epf/hooknum name the hook as registered; entry-\u003estate.pf/hook\nnet/netfilter/nfnetlink_queue.c-1566- * name the array actually traversed - they differ only for NFPROTO_INET.\n--\nnet/netfilter/nfnetlink_queue.c=1569=static int nf_hook_cmp(struct nf_queue_entry *entry, unsigned long ops_ptr)\n--\nnet/netfilter/nfnetlink_queue.c-1580-\nnet/netfilter/nfnetlink_queue.c:1581:\tif (entry-\u003estate.hook != hooknum)\nnet/netfilter/nfnetlink_queue.c-1582-\t\treturn 0;\n--\nnet/netfilter/nfnetlink_queue.c-1584-\tif (pf == NFPROTO_INET)\nnet/netfilter/nfnetlink_queue.c:1585:\t\treturn entry-\u003estate.pf == NFPROTO_IPV4 ||\nnet/netfilter/nfnetlink_queue.c:1586:\t\t entry-\u003estate.pf == NFPROTO_IPV6;\nnet/netfilter/nfnetlink_queue.c-1587-\nnet/netfilter/nfnetlink_queue.c:1588:\treturn entry-\u003estate.pf == pf;\nnet/netfilter/nfnetlink_queue.c-1589-}\n--\nnet/netfilter/nfnetlink_queue.c=1824=static int nfqnl_recv_verdict(struct sk_buff *skb, const struct nfnl_info *info,\n--\nnet/netfilter/nfnetlink_queue.c-1861-\nnet/netfilter/nfnetlink_queue.c:1862:\tif (entry-\u003estate.pf == PF_BRIDGE) {\nnet/netfilter/nfnetlink_queue.c-1863-\t\terr = nfqa_parse_bridge(entry, nfqa);\n"
} |
| 63/3 | 2026/10/18 01:22 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 42336 output: 509 thoughts: 3295 Thoughts: **Analysis of Kernel Patches for KMSAN Applicability** Okay, so I've gone through each of the code changes meticulously, and here's my assessment. First, regarding `nf_queue_entry`, that’s fully initialized on creation. Then I checked several areas within the `netfilter` subsystem. The `nf_tables_api.c` changes are all about module refcounting, RCU concurrency, and error unwind – nothing that could expose uninitialized memory to the user or interact with user-facing buffers in a risky way. The `br_netfilter_hooks.c` modifications deal with user namespace support and pernet memory, which is allocated via `net_generic` using `kzalloc`. This ensures all `brnf_net` fields, including `in_userns`, are zero-initialized, which is safe. Next, I looked at `nf_conntrack_core.c`, where a race/UAF fix via waiting for the workqueue to finish before teardown. Similarly, `nft_chain_nat.c`'s `synchronize_rcu()` on exit is a standard RCU grace period wait and safe. The `nft_compat.c` changes only involve input validation for table names, hook numbers, and priority. I've also re-examined four key criteria for KMSAN to apply: 1. Kernel structures sent to userspace: In the patch, the fixes for `ip_vs_sync.c` read `flags = READ_ONCE(cp->flags)` to ensure atomicity, which prevents inconsistent state being written or read, which prevents uninitialized reads and writes. In `nft_exthdr.c`, I saw that the 60-byte local stack buffer `buff` was removed and the `struct tcphdr` was used instead, and this modification still prevents information leaks because it's only copied if the header isn't linear. 2. Conditional logic with uninitialized variables: In `nf_flow_table_ip.c`, the `encap_proto` field was added and correctly zero-initialized. Its values are then checked in switches, in which all branches are initialized. All branches have initialized variables, and safe returns. 3. Allocation or initialization of data structures: No complex data structures are introduced with the risk of uninitialized fields. All added fields are zero-initialized. 4. Bounds checks: In `ip_vs_sync.c`, there's a race condition fix with `READ_ONCE`. In `nft_exthdr.c`, it validates lengths before using, which makes it safe to use. My overall assessment is that standard KASAN, KCSAN, and LOCKDEP builds should be sufficient for validating these changes, and KMSAN is not warranted. The patches focus on concurrency fixes (RCU, race conditions), lifetime management (UAF via flushing), and a few optimizations for kernel structures. They do not introduce uninitialized memory risks or information leaks to userspace. Therefore, NeedsKMSAN should be false. |
| 64/3 | 2026/10/18 01:22 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch series touches several netfilter and networking subsystems:\n1. `br_netfilter`: Disables bridge netfilter in non-init user namespaces, adding an `in_userns` flag to `struct brnf_net`. The pernet data structure is zero-allocated via standard netns allocation (`kzalloc`).\n2. `nft_exthdr`: Refactors `nft_exthdr_tcp_set_eval` and `nft_exthdr_tcp_strip_eval` to remove temporary local stack buffers (`buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE]`), inspecting the TCP header directly with `skb_header_pointer` and linearizing with `skb_ensure_writable`. No uninitialized stack memory is left or accessed.\n3. `ip_vs_sync`: Fixes a data race where concurrent modification of `cp-\u003eflags` could cause mismatch between message length calculation and copied options by snapshotting `flags` with `READ_ONCE()`.\n4. `nf_flow_table`: Adds an `encap_proto` field to `struct flow_offload_tunnel` and `struct net_device_path` to unify IPv4/IPv6 tunnel offloading. Flow offload structures are zero-initialized via `kmem_cache_zalloc`, and device paths are zero-initialized with `memset()`. The variables in `nf_flow_tunnel_ip_push` and `nf_flow_tunnel_ip6_push` are fully initialized before use.\n5. `nf_tables`, `nf_queue`, `nf_conntrack`: Implements RCU lookups and correct module refcounting for chain types, selective queue entry flushing in `nfqnl_nf_hook_drop`, and flushes conntrack GC work during netns teardown to prevent use-after-free.\n\nNone of the changes introduce uninitialized memory reads, info-leaks across the kernel-user boundary, or conditional branches on uninitialized variables. Any risks in these changes relate to object lifetimes, concurrency, or out-of-bounds access, which are already covered by KASAN, KCSAN, and LOCKDEP in standard test configurations. Spawning a dedicated KMSAN fuzzing session is not warranted.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch series touches several netfilter and networking subsystems:\n1. `br_netfilter`: Disables bridge netfilter in non-init user namespaces, adding an `in_userns` flag to `struct brnf_net`. The pernet data structure is zero-allocated via standard netns allocation (`kzalloc`).\n2. `nft_exthdr`: Refactors `nft_exthdr_tcp_set_eval` and `nft_exthdr_tcp_strip_eval` to remove temporary local stack buffers (`buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE]`), inspecting the TCP header directly with `skb_header_pointer` and linearizing with `skb_ensure_writable`. No uninitialized stack memory is left or accessed.\n3. `ip_vs_sync`: Fixes a data race where concurrent modification of `cp-\u003eflags` could cause mismatch between message length calculation and copied options by snapshotting `flags` with `READ_ONCE()`.\n4. `nf_flow_table`: Adds an `encap_proto` field to `struct flow_offload_tunnel` and `struct net_device_path` to unify IPv4/IPv6 tunnel offloading. Flow offload structures are zero-initialized via `kmem_cache_zalloc`, and device paths are zero-initialized with `memset()`. The variables in `nf_flow_tunnel_ip_push` and `nf_flow_tunnel_ip6_push` are fully initialized before use.\n5. `nf_tables`, `nf_queue`, `nf_conntrack`: Implements RCU lookups and correct module refcounting for chain types, selective queue entry flushing in `nfqnl_nf_hook_drop`, and flushes conntrack GC work during netns teardown to prevent use-after-free.\n\nNone of the changes introduce uninitialized memory reads, info-leaks across the kernel-user boundary, or conditional branches on uninitialized variables. Any risks in these changes relate to object lifetimes, concurrency, or out-of-bounds access, which are already covered by KASAN, KCSAN, and LOCKDEP in standard test configurations. Spawning a dedicated KMSAN fuzzing session 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) |
|---|