From e31f4bfdd1083535236ff6c54f4695ce91910c24 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 16 Jul 2023 21:22:37 +0200 Subject: [PATCH] 5.4-stable patches added patches: netfilter-add-helper-function-to-set-up-the-nfnetlink-header-and-use-it.patch netfilter-nf_tables-add-nft_trans_prepare_error-to-deal-with-bound-set-chain.patch netfilter-nf_tables-add-rescheduling-points-during-loop-detection-walks.patch netfilter-nf_tables-fix-nat-hook-table-deletion.patch netfilter-nf_tables-fix-scheduling-while-atomic-splat.patch netfilter-nf_tables-incorrect-error-path-handling-with-nft_msg_newrule.patch netfilter-nf_tables-reject-unbound-anonymous-set-before-commit-phase.patch netfilter-nf_tables-unbind-non-anonymous-set-if-rule-construction-fails.patch netfilter-nf_tables-use-net_generic-infra-for-transaction-data.patch netfilter-nftables-add-helper-function-to-set-the-base-sequence-number.patch --- ...t-up-the-nfnetlink-header-and-use-it.patch | 707 ++++++++++ ...e_error-to-deal-with-bound-set-chain.patch | 109 ++ ...g-points-during-loop-detection-walks.patch | 50 + ...f_tables-fix-nat-hook-table-deletion.patch | 104 ++ ...es-fix-scheduling-while-atomic-splat.patch | 39 + ...r-path-handling-with-nft_msg_newrule.patch | 73 + ...nd-anonymous-set-before-commit-phase.patch | 142 ++ ...ymous-set-if-rule-construction-fails.patch | 33 + ...t_generic-infra-for-transaction-data.patch | 1214 +++++++++++++++++ ...tion-to-set-the-base-sequence-number.patch | 117 ++ queue-5.4/series | 10 + 11 files changed, 2598 insertions(+) create mode 100644 queue-5.4/netfilter-add-helper-function-to-set-up-the-nfnetlink-header-and-use-it.patch create mode 100644 queue-5.4/netfilter-nf_tables-add-nft_trans_prepare_error-to-deal-with-bound-set-chain.patch create mode 100644 queue-5.4/netfilter-nf_tables-add-rescheduling-points-during-loop-detection-walks.patch create mode 100644 queue-5.4/netfilter-nf_tables-fix-nat-hook-table-deletion.patch create mode 100644 queue-5.4/netfilter-nf_tables-fix-scheduling-while-atomic-splat.patch create mode 100644 queue-5.4/netfilter-nf_tables-incorrect-error-path-handling-with-nft_msg_newrule.patch create mode 100644 queue-5.4/netfilter-nf_tables-reject-unbound-anonymous-set-before-commit-phase.patch create mode 100644 queue-5.4/netfilter-nf_tables-unbind-non-anonymous-set-if-rule-construction-fails.patch create mode 100644 queue-5.4/netfilter-nf_tables-use-net_generic-infra-for-transaction-data.patch create mode 100644 queue-5.4/netfilter-nftables-add-helper-function-to-set-the-base-sequence-number.patch diff --git a/queue-5.4/netfilter-add-helper-function-to-set-up-the-nfnetlink-header-and-use-it.patch b/queue-5.4/netfilter-add-helper-function-to-set-up-the-nfnetlink-header-and-use-it.patch new file mode 100644 index 00000000000..9161a221c76 --- /dev/null +++ b/queue-5.4/netfilter-add-helper-function-to-set-up-the-nfnetlink-header-and-use-it.patch @@ -0,0 +1,707 @@ +From stable-owner@vger.kernel.org Wed Jul 5 18:54:56 2023 +From: Pablo Neira Ayuso +Date: Wed, 5 Jul 2023 18:54:16 +0200 +Subject: netfilter: add helper function to set up the nfnetlink header and use it +To: netfilter-devel@vger.kernel.org +Cc: sashal@kernel.org, gregkh@linuxfoundation.org, stable@vger.kernel.org +Message-ID: <20230705165423.50054-4-pablo@netfilter.org> + +From: Pablo Neira Ayuso + +[ 19c28b1374fb1073a9ec873a6c10bf5f16b10b9d ] + +This patch adds a helper function to set up the netlink and nfnetlink headers. +Update existing codebase to use it. + +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/netfilter/nfnetlink.h | 27 +++++++++ + net/netfilter/ipset/ip_set_core.c | 17 +---- + net/netfilter/nf_conntrack_netlink.c | 77 +++++++------------------- + net/netfilter/nf_tables_api.c | 102 +++++++++-------------------------- + net/netfilter/nf_tables_trace.c | 9 --- + net/netfilter/nfnetlink_acct.c | 11 +-- + net/netfilter/nfnetlink_cthelper.c | 11 +-- + net/netfilter/nfnetlink_cttimeout.c | 22 ++----- + net/netfilter/nfnetlink_log.c | 11 +-- + net/netfilter/nfnetlink_queue.c | 12 +--- + net/netfilter/nft_compat.c | 11 +-- + 11 files changed, 102 insertions(+), 208 deletions(-) + +--- a/include/linux/netfilter/nfnetlink.h ++++ b/include/linux/netfilter/nfnetlink.h +@@ -56,6 +56,33 @@ static inline u16 nfnl_msg_type(u8 subsy + return subsys << 8 | msg_type; + } + ++static inline void nfnl_fill_hdr(struct nlmsghdr *nlh, u8 family, u8 version, ++ __be16 res_id) ++{ ++ struct nfgenmsg *nfmsg; ++ ++ nfmsg = nlmsg_data(nlh); ++ nfmsg->nfgen_family = family; ++ nfmsg->version = version; ++ nfmsg->res_id = res_id; ++} ++ ++static inline struct nlmsghdr *nfnl_msg_put(struct sk_buff *skb, u32 portid, ++ u32 seq, int type, int flags, ++ u8 family, u8 version, ++ __be16 res_id) ++{ ++ struct nlmsghdr *nlh; ++ ++ nlh = nlmsg_put(skb, portid, seq, type, sizeof(struct nfgenmsg), flags); ++ if (!nlh) ++ return NULL; ++ ++ nfnl_fill_hdr(nlh, family, version, res_id); ++ ++ return nlh; ++} ++ + void nfnl_lock(__u8 subsys_id); + void nfnl_unlock(__u8 subsys_id); + #ifdef CONFIG_PROVE_LOCKING +--- a/net/netfilter/ipset/ip_set_core.c ++++ b/net/netfilter/ipset/ip_set_core.c +@@ -811,20 +811,9 @@ static struct nlmsghdr * + start_msg(struct sk_buff *skb, u32 portid, u32 seq, unsigned int flags, + enum ipset_cmd cmd) + { +- struct nlmsghdr *nlh; +- struct nfgenmsg *nfmsg; +- +- nlh = nlmsg_put(skb, portid, seq, nfnl_msg_type(NFNL_SUBSYS_IPSET, cmd), +- sizeof(*nfmsg), flags); +- if (!nlh) +- return NULL; +- +- nfmsg = nlmsg_data(nlh); +- nfmsg->nfgen_family = NFPROTO_IPV4; +- nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = 0; +- +- return nlh; ++ return nfnl_msg_put(skb, portid, seq, ++ nfnl_msg_type(NFNL_SUBSYS_IPSET, cmd), flags, ++ NFPROTO_IPV4, NFNETLINK_V0, 0); + } + + /* Create a set */ +--- a/net/netfilter/nf_conntrack_netlink.c ++++ b/net/netfilter/nf_conntrack_netlink.c +@@ -515,20 +515,15 @@ ctnetlink_fill_info(struct sk_buff *skb, + { + const struct nf_conntrack_zone *zone; + struct nlmsghdr *nlh; +- struct nfgenmsg *nfmsg; + struct nlattr *nest_parms; + unsigned int flags = portid ? NLM_F_MULTI : 0, event; + + event = nfnl_msg_type(NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_NEW); +- nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); +- if (nlh == NULL) ++ nlh = nfnl_msg_put(skb, portid, seq, event, flags, nf_ct_l3num(ct), ++ NFNETLINK_V0, 0); ++ if (!nlh) + goto nlmsg_failure; + +- nfmsg = nlmsg_data(nlh); +- nfmsg->nfgen_family = nf_ct_l3num(ct); +- nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = 0; +- + zone = nf_ct_zone(ct); + + nest_parms = nla_nest_start(skb, CTA_TUPLE_ORIG); +@@ -685,7 +680,6 @@ ctnetlink_conntrack_event(unsigned int e + const struct nf_conntrack_zone *zone; + struct net *net; + struct nlmsghdr *nlh; +- struct nfgenmsg *nfmsg; + struct nlattr *nest_parms; + struct nf_conn *ct = item->ct; + struct sk_buff *skb; +@@ -715,15 +709,11 @@ ctnetlink_conntrack_event(unsigned int e + goto errout; + + type = nfnl_msg_type(NFNL_SUBSYS_CTNETLINK, type); +- nlh = nlmsg_put(skb, item->portid, 0, type, sizeof(*nfmsg), flags); +- if (nlh == NULL) ++ nlh = nfnl_msg_put(skb, item->portid, 0, type, flags, nf_ct_l3num(ct), ++ NFNETLINK_V0, 0); ++ if (!nlh) + goto nlmsg_failure; + +- nfmsg = nlmsg_data(nlh); +- nfmsg->nfgen_family = nf_ct_l3num(ct); +- nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = 0; +- + zone = nf_ct_zone(ct); + + nest_parms = nla_nest_start(skb, CTA_TUPLE_ORIG); +@@ -2200,20 +2190,15 @@ ctnetlink_ct_stat_cpu_fill_info(struct s + __u16 cpu, const struct ip_conntrack_stat *st) + { + struct nlmsghdr *nlh; +- struct nfgenmsg *nfmsg; + unsigned int flags = portid ? NLM_F_MULTI : 0, event; + + event = nfnl_msg_type(NFNL_SUBSYS_CTNETLINK, + IPCTNL_MSG_CT_GET_STATS_CPU); +- nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); +- if (nlh == NULL) ++ nlh = nfnl_msg_put(skb, portid, seq, event, flags, AF_UNSPEC, ++ NFNETLINK_V0, htons(cpu)); ++ if (!nlh) + goto nlmsg_failure; + +- nfmsg = nlmsg_data(nlh); +- nfmsg->nfgen_family = AF_UNSPEC; +- nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = htons(cpu); +- + if (nla_put_be32(skb, CTA_STATS_FOUND, htonl(st->found)) || + nla_put_be32(skb, CTA_STATS_INVALID, htonl(st->invalid)) || + nla_put_be32(skb, CTA_STATS_IGNORE, htonl(st->ignore)) || +@@ -2284,20 +2269,15 @@ ctnetlink_stat_ct_fill_info(struct sk_bu + struct net *net) + { + struct nlmsghdr *nlh; +- struct nfgenmsg *nfmsg; + unsigned int flags = portid ? NLM_F_MULTI : 0, event; + unsigned int nr_conntracks = atomic_read(&net->ct.count); + + event = nfnl_msg_type(NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_GET_STATS); +- nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); +- if (nlh == NULL) ++ nlh = nfnl_msg_put(skb, portid, seq, event, flags, AF_UNSPEC, ++ NFNETLINK_V0, 0); ++ if (!nlh) + goto nlmsg_failure; + +- nfmsg = nlmsg_data(nlh); +- nfmsg->nfgen_family = AF_UNSPEC; +- nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = 0; +- + if (nla_put_be32(skb, CTA_STATS_GLOBAL_ENTRIES, htonl(nr_conntracks))) + goto nla_put_failure; + +@@ -2803,19 +2783,14 @@ ctnetlink_exp_fill_info(struct sk_buff * + int event, const struct nf_conntrack_expect *exp) + { + struct nlmsghdr *nlh; +- struct nfgenmsg *nfmsg; + unsigned int flags = portid ? NLM_F_MULTI : 0; + + event = nfnl_msg_type(NFNL_SUBSYS_CTNETLINK_EXP, event); +- nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); +- if (nlh == NULL) ++ nlh = nfnl_msg_put(skb, portid, seq, event, flags, ++ exp->tuple.src.l3num, NFNETLINK_V0, 0); ++ if (!nlh) + goto nlmsg_failure; + +- nfmsg = nlmsg_data(nlh); +- nfmsg->nfgen_family = exp->tuple.src.l3num; +- nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = 0; +- + if (ctnetlink_exp_dump_expect(skb, exp) < 0) + goto nla_put_failure; + +@@ -2835,7 +2810,6 @@ ctnetlink_expect_event(unsigned int even + struct nf_conntrack_expect *exp = item->exp; + struct net *net = nf_ct_exp_net(exp); + struct nlmsghdr *nlh; +- struct nfgenmsg *nfmsg; + struct sk_buff *skb; + unsigned int type, group; + int flags = 0; +@@ -2858,15 +2832,11 @@ ctnetlink_expect_event(unsigned int even + goto errout; + + type = nfnl_msg_type(NFNL_SUBSYS_CTNETLINK_EXP, type); +- nlh = nlmsg_put(skb, item->portid, 0, type, sizeof(*nfmsg), flags); +- if (nlh == NULL) ++ nlh = nfnl_msg_put(skb, item->portid, 0, type, flags, ++ exp->tuple.src.l3num, NFNETLINK_V0, 0); ++ if (!nlh) + goto nlmsg_failure; + +- nfmsg = nlmsg_data(nlh); +- nfmsg->nfgen_family = exp->tuple.src.l3num; +- nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = 0; +- + if (ctnetlink_exp_dump_expect(skb, exp) < 0) + goto nla_put_failure; + +@@ -3436,20 +3406,15 @@ ctnetlink_exp_stat_fill_info(struct sk_b + const struct ip_conntrack_stat *st) + { + struct nlmsghdr *nlh; +- struct nfgenmsg *nfmsg; + unsigned int flags = portid ? NLM_F_MULTI : 0, event; + + event = nfnl_msg_type(NFNL_SUBSYS_CTNETLINK, + IPCTNL_MSG_EXP_GET_STATS_CPU); +- nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); +- if (nlh == NULL) ++ nlh = nfnl_msg_put(skb, portid, seq, event, flags, AF_UNSPEC, ++ NFNETLINK_V0, htons(cpu)); ++ if (!nlh) + goto nlmsg_failure; + +- nfmsg = nlmsg_data(nlh); +- nfmsg->nfgen_family = AF_UNSPEC; +- nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = htons(cpu); +- + if (nla_put_be32(skb, CTA_STATS_EXP_NEW, htonl(st->expect_new)) || + nla_put_be32(skb, CTA_STATS_EXP_CREATE, htonl(st->expect_create)) || + nla_put_be32(skb, CTA_STATS_EXP_DELETE, htonl(st->expect_delete))) +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -605,18 +605,13 @@ static int nf_tables_fill_table_info(str + int family, const struct nft_table *table) + { + struct nlmsghdr *nlh; +- struct nfgenmsg *nfmsg; + + event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event); +- nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags); +- if (nlh == NULL) ++ nlh = nfnl_msg_put(skb, portid, seq, event, flags, family, ++ NFNETLINK_V0, nft_base_seq(net)); ++ if (!nlh) + goto nla_put_failure; + +- nfmsg = nlmsg_data(nlh); +- nfmsg->nfgen_family = family; +- nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = nft_base_seq(net); +- + if (nla_put_string(skb, NFTA_TABLE_NAME, table->name) || + nla_put_be32(skb, NFTA_TABLE_FLAGS, htonl(table->flags)) || + nla_put_be32(skb, NFTA_TABLE_USE, htonl(table->use)) || +@@ -1269,18 +1264,13 @@ static int nf_tables_fill_chain_info(str + const struct nft_chain *chain) + { + struct nlmsghdr *nlh; +- struct nfgenmsg *nfmsg; + + event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event); +- nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags); +- if (nlh == NULL) ++ nlh = nfnl_msg_put(skb, portid, seq, event, flags, family, ++ NFNETLINK_V0, nft_base_seq(net)); ++ if (!nlh) + goto nla_put_failure; + +- nfmsg = nlmsg_data(nlh); +- nfmsg->nfgen_family = family; +- nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = nft_base_seq(net); +- + if (nla_put_string(skb, NFTA_CHAIN_TABLE, table->name)) + goto nla_put_failure; + if (nla_put_be64(skb, NFTA_CHAIN_HANDLE, cpu_to_be64(chain->handle), +@@ -2359,20 +2349,15 @@ static int nf_tables_fill_rule_info(stru + const struct nft_rule *prule) + { + struct nlmsghdr *nlh; +- struct nfgenmsg *nfmsg; + const struct nft_expr *expr, *next; + struct nlattr *list; + u16 type = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event); + +- nlh = nlmsg_put(skb, portid, seq, type, sizeof(struct nfgenmsg), flags); +- if (nlh == NULL) ++ nlh = nfnl_msg_put(skb, portid, seq, type, flags, family, NFNETLINK_V0, ++ nft_base_seq(net)); ++ if (!nlh) + goto nla_put_failure; + +- nfmsg = nlmsg_data(nlh); +- nfmsg->nfgen_family = family; +- nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = nft_base_seq(net); +- + if (nla_put_string(skb, NFTA_RULE_TABLE, table->name)) + goto nla_put_failure; + if (nla_put_string(skb, NFTA_RULE_CHAIN, chain->name)) +@@ -3315,23 +3300,17 @@ __be64 nf_jiffies64_to_msecs(u64 input) + static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx, + const struct nft_set *set, u16 event, u16 flags) + { +- struct nfgenmsg *nfmsg; + struct nlmsghdr *nlh; + struct nlattr *desc; + u32 portid = ctx->portid; + u32 seq = ctx->seq; + + event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event); +- nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), +- flags); +- if (nlh == NULL) ++ nlh = nfnl_msg_put(skb, portid, seq, event, flags, ctx->family, ++ NFNETLINK_V0, nft_base_seq(ctx->net)); ++ if (!nlh) + goto nla_put_failure; + +- nfmsg = nlmsg_data(nlh); +- nfmsg->nfgen_family = ctx->family; +- nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = nft_base_seq(ctx->net); +- + if (nla_put_string(skb, NFTA_SET_TABLE, ctx->table->name)) + goto nla_put_failure; + if (nla_put_string(skb, NFTA_SET_NAME, set->name)) +@@ -4144,7 +4123,6 @@ static int nf_tables_dump_set(struct sk_ + struct nft_set *set; + struct nft_set_dump_args args; + bool set_found = false; +- struct nfgenmsg *nfmsg; + struct nlmsghdr *nlh; + struct nlattr *nest; + u32 portid, seq; +@@ -4177,16 +4155,11 @@ static int nf_tables_dump_set(struct sk_ + portid = NETLINK_CB(cb->skb).portid; + seq = cb->nlh->nlmsg_seq; + +- nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), +- NLM_F_MULTI); +- if (nlh == NULL) ++ nlh = nfnl_msg_put(skb, portid, seq, event, NLM_F_MULTI, ++ table->family, NFNETLINK_V0, nft_base_seq(net)); ++ if (!nlh) + goto nla_put_failure; + +- nfmsg = nlmsg_data(nlh); +- nfmsg->nfgen_family = table->family; +- nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = nft_base_seq(net); +- + if (nla_put_string(skb, NFTA_SET_ELEM_LIST_TABLE, table->name)) + goto nla_put_failure; + if (nla_put_string(skb, NFTA_SET_ELEM_LIST_SET, set->name)) +@@ -4243,22 +4216,16 @@ static int nf_tables_fill_setelem_info(s + const struct nft_set *set, + const struct nft_set_elem *elem) + { +- struct nfgenmsg *nfmsg; + struct nlmsghdr *nlh; + struct nlattr *nest; + int err; + + event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event); +- nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), +- flags); +- if (nlh == NULL) ++ nlh = nfnl_msg_put(skb, portid, seq, event, flags, ctx->family, ++ NFNETLINK_V0, nft_base_seq(ctx->net)); ++ if (!nlh) + goto nla_put_failure; + +- nfmsg = nlmsg_data(nlh); +- nfmsg->nfgen_family = ctx->family; +- nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = nft_base_seq(ctx->net); +- + if (nla_put_string(skb, NFTA_SET_TABLE, ctx->table->name)) + goto nla_put_failure; + if (nla_put_string(skb, NFTA_SET_NAME, set->name)) +@@ -5377,19 +5344,14 @@ static int nf_tables_fill_obj_info(struc + int family, const struct nft_table *table, + struct nft_object *obj, bool reset) + { +- struct nfgenmsg *nfmsg; + struct nlmsghdr *nlh; + + event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event); +- nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags); +- if (nlh == NULL) ++ nlh = nfnl_msg_put(skb, portid, seq, event, flags, family, ++ NFNETLINK_V0, nft_base_seq(net)); ++ if (!nlh) + goto nla_put_failure; + +- nfmsg = nlmsg_data(nlh); +- nfmsg->nfgen_family = family; +- nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = nft_base_seq(net); +- + if (nla_put_string(skb, NFTA_OBJ_TABLE, table->name) || + nla_put_string(skb, NFTA_OBJ_NAME, obj->key.name) || + nla_put_be32(skb, NFTA_OBJ_TYPE, htonl(obj->ops->type->type)) || +@@ -6052,20 +6014,15 @@ static int nf_tables_fill_flowtable_info + struct nft_flowtable *flowtable) + { + struct nlattr *nest, *nest_devs; +- struct nfgenmsg *nfmsg; + struct nlmsghdr *nlh; + int i; + + event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event); +- nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags); +- if (nlh == NULL) ++ nlh = nfnl_msg_put(skb, portid, seq, event, flags, family, ++ NFNETLINK_V0, nft_base_seq(net)); ++ if (!nlh) + goto nla_put_failure; + +- nfmsg = nlmsg_data(nlh); +- nfmsg->nfgen_family = family; +- nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = nft_base_seq(net); +- + if (nla_put_string(skb, NFTA_FLOWTABLE_TABLE, flowtable->table->name) || + nla_put_string(skb, NFTA_FLOWTABLE_NAME, flowtable->name) || + nla_put_be32(skb, NFTA_FLOWTABLE_USE, htonl(flowtable->use)) || +@@ -6291,19 +6248,14 @@ static int nf_tables_fill_gen_info(struc + u32 portid, u32 seq) + { + struct nlmsghdr *nlh; +- struct nfgenmsg *nfmsg; + char buf[TASK_COMM_LEN]; + int event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWGEN); + +- nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), 0); +- if (nlh == NULL) ++ nlh = nfnl_msg_put(skb, portid, seq, event, 0, AF_UNSPEC, ++ NFNETLINK_V0, nft_base_seq(net)); ++ if (!nlh) + goto nla_put_failure; + +- nfmsg = nlmsg_data(nlh); +- nfmsg->nfgen_family = AF_UNSPEC; +- nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = nft_base_seq(net); +- + if (nla_put_be32(skb, NFTA_GEN_ID, htonl(net->nft.base_seq)) || + nla_put_be32(skb, NFTA_GEN_PROC_PID, htonl(task_pid_nr(current))) || + nla_put_string(skb, NFTA_GEN_PROC_NAME, get_task_comm(buf, current))) +--- a/net/netfilter/nf_tables_trace.c ++++ b/net/netfilter/nf_tables_trace.c +@@ -183,7 +183,6 @@ static bool nft_trace_have_verdict_chain + void nft_trace_notify(struct nft_traceinfo *info) + { + const struct nft_pktinfo *pkt = info->pkt; +- struct nfgenmsg *nfmsg; + struct nlmsghdr *nlh; + struct sk_buff *skb; + unsigned int size; +@@ -219,15 +218,11 @@ void nft_trace_notify(struct nft_tracein + return; + + event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, NFT_MSG_TRACE); +- nlh = nlmsg_put(skb, 0, 0, event, sizeof(struct nfgenmsg), 0); ++ nlh = nfnl_msg_put(skb, 0, 0, event, 0, info->basechain->type->family, ++ NFNETLINK_V0, 0); + if (!nlh) + goto nla_put_failure; + +- nfmsg = nlmsg_data(nlh); +- nfmsg->nfgen_family = info->basechain->type->family; +- nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = 0; +- + if (nla_put_be32(skb, NFTA_TRACE_NFPROTO, htonl(nft_pf(pkt)))) + goto nla_put_failure; + +--- a/net/netfilter/nfnetlink_acct.c ++++ b/net/netfilter/nfnetlink_acct.c +@@ -132,21 +132,16 @@ nfnl_acct_fill_info(struct sk_buff *skb, + int event, struct nf_acct *acct) + { + struct nlmsghdr *nlh; +- struct nfgenmsg *nfmsg; + unsigned int flags = portid ? NLM_F_MULTI : 0; + u64 pkts, bytes; + u32 old_flags; + + event = nfnl_msg_type(NFNL_SUBSYS_ACCT, event); +- nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); +- if (nlh == NULL) ++ nlh = nfnl_msg_put(skb, portid, seq, event, flags, AF_UNSPEC, ++ NFNETLINK_V0, 0); ++ if (!nlh) + goto nlmsg_failure; + +- nfmsg = nlmsg_data(nlh); +- nfmsg->nfgen_family = AF_UNSPEC; +- nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = 0; +- + if (nla_put_string(skb, NFACCT_NAME, acct->name)) + goto nla_put_failure; + +--- a/net/netfilter/nfnetlink_cthelper.c ++++ b/net/netfilter/nfnetlink_cthelper.c +@@ -530,20 +530,15 @@ nfnl_cthelper_fill_info(struct sk_buff * + int event, struct nf_conntrack_helper *helper) + { + struct nlmsghdr *nlh; +- struct nfgenmsg *nfmsg; + unsigned int flags = portid ? NLM_F_MULTI : 0; + int status; + + event = nfnl_msg_type(NFNL_SUBSYS_CTHELPER, event); +- nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); +- if (nlh == NULL) ++ nlh = nfnl_msg_put(skb, portid, seq, event, flags, AF_UNSPEC, ++ NFNETLINK_V0, 0); ++ if (!nlh) + goto nlmsg_failure; + +- nfmsg = nlmsg_data(nlh); +- nfmsg->nfgen_family = AF_UNSPEC; +- nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = 0; +- + if (nla_put_string(skb, NFCTH_NAME, helper->name)) + goto nla_put_failure; + +--- a/net/netfilter/nfnetlink_cttimeout.c ++++ b/net/netfilter/nfnetlink_cttimeout.c +@@ -160,22 +160,17 @@ ctnl_timeout_fill_info(struct sk_buff *s + int event, struct ctnl_timeout *timeout) + { + struct nlmsghdr *nlh; +- struct nfgenmsg *nfmsg; + unsigned int flags = portid ? NLM_F_MULTI : 0; + const struct nf_conntrack_l4proto *l4proto = timeout->timeout.l4proto; + struct nlattr *nest_parms; + int ret; + + event = nfnl_msg_type(NFNL_SUBSYS_CTNETLINK_TIMEOUT, event); +- nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); +- if (nlh == NULL) ++ nlh = nfnl_msg_put(skb, portid, seq, event, flags, AF_UNSPEC, ++ NFNETLINK_V0, 0); ++ if (!nlh) + goto nlmsg_failure; + +- nfmsg = nlmsg_data(nlh); +- nfmsg->nfgen_family = AF_UNSPEC; +- nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = 0; +- + if (nla_put_string(skb, CTA_TIMEOUT_NAME, timeout->name) || + nla_put_be16(skb, CTA_TIMEOUT_L3PROTO, + htons(timeout->timeout.l3num)) || +@@ -382,21 +377,16 @@ cttimeout_default_fill_info(struct net * + const unsigned int *timeouts) + { + struct nlmsghdr *nlh; +- struct nfgenmsg *nfmsg; + unsigned int flags = portid ? NLM_F_MULTI : 0; + struct nlattr *nest_parms; + int ret; + + event = nfnl_msg_type(NFNL_SUBSYS_CTNETLINK_TIMEOUT, event); +- nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); +- if (nlh == NULL) ++ nlh = nfnl_msg_put(skb, portid, seq, event, flags, AF_UNSPEC, ++ NFNETLINK_V0, 0); ++ if (!nlh) + goto nlmsg_failure; + +- nfmsg = nlmsg_data(nlh); +- nfmsg->nfgen_family = AF_UNSPEC; +- nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = 0; +- + if (nla_put_be16(skb, CTA_TIMEOUT_L3PROTO, htons(l3num)) || + nla_put_u8(skb, CTA_TIMEOUT_L4PROTO, l4proto->l4proto)) + goto nla_put_failure; +--- a/net/netfilter/nfnetlink_log.c ++++ b/net/netfilter/nfnetlink_log.c +@@ -452,20 +452,15 @@ __build_packet_message(struct nfnl_log_n + { + struct nfulnl_msg_packet_hdr pmsg; + struct nlmsghdr *nlh; +- struct nfgenmsg *nfmsg; + sk_buff_data_t old_tail = inst->skb->tail; + struct sock *sk; + const unsigned char *hwhdrp; + +- nlh = nlmsg_put(inst->skb, 0, 0, +- nfnl_msg_type(NFNL_SUBSYS_ULOG, NFULNL_MSG_PACKET), +- sizeof(struct nfgenmsg), 0); ++ nlh = nfnl_msg_put(inst->skb, 0, 0, ++ nfnl_msg_type(NFNL_SUBSYS_ULOG, NFULNL_MSG_PACKET), ++ 0, pf, NFNETLINK_V0, htons(inst->group_num)); + if (!nlh) + return -1; +- nfmsg = nlmsg_data(nlh); +- nfmsg->nfgen_family = pf; +- nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = htons(inst->group_num); + + memset(&pmsg, 0, sizeof(pmsg)); + pmsg.hw_protocol = skb->protocol; +--- a/net/netfilter/nfnetlink_queue.c ++++ b/net/netfilter/nfnetlink_queue.c +@@ -383,7 +383,6 @@ nfqnl_build_packet_message(struct net *n + struct nlattr *nla; + struct nfqnl_msg_packet_hdr *pmsg; + struct nlmsghdr *nlh; +- struct nfgenmsg *nfmsg; + struct sk_buff *entskb = entry->skb; + struct net_device *indev; + struct net_device *outdev; +@@ -469,18 +468,15 @@ nfqnl_build_packet_message(struct net *n + goto nlmsg_failure; + } + +- nlh = nlmsg_put(skb, 0, 0, +- nfnl_msg_type(NFNL_SUBSYS_QUEUE, NFQNL_MSG_PACKET), +- sizeof(struct nfgenmsg), 0); ++ nlh = nfnl_msg_put(skb, 0, 0, ++ nfnl_msg_type(NFNL_SUBSYS_QUEUE, NFQNL_MSG_PACKET), ++ 0, entry->state.pf, NFNETLINK_V0, ++ htons(queue->queue_num)); + if (!nlh) { + skb_tx_error(entskb); + kfree_skb(skb); + goto nlmsg_failure; + } +- nfmsg = nlmsg_data(nlh); +- nfmsg->nfgen_family = entry->state.pf; +- nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = htons(queue->queue_num); + + nla = __nla_reserve(skb, NFQA_PACKET_HDR, sizeof(*pmsg)); + pmsg = nla_data(nla); +--- a/net/netfilter/nft_compat.c ++++ b/net/netfilter/nft_compat.c +@@ -591,19 +591,14 @@ nfnl_compat_fill_info(struct sk_buff *sk + int rev, int target) + { + struct nlmsghdr *nlh; +- struct nfgenmsg *nfmsg; + unsigned int flags = portid ? NLM_F_MULTI : 0; + + event = nfnl_msg_type(NFNL_SUBSYS_NFT_COMPAT, event); +- nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); +- if (nlh == NULL) ++ nlh = nfnl_msg_put(skb, portid, seq, event, flags, family, ++ NFNETLINK_V0, 0); ++ if (!nlh) + goto nlmsg_failure; + +- nfmsg = nlmsg_data(nlh); +- nfmsg->nfgen_family = family; +- nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = 0; +- + if (nla_put_string(skb, NFTA_COMPAT_NAME, name) || + nla_put_be32(skb, NFTA_COMPAT_REV, htonl(rev)) || + nla_put_be32(skb, NFTA_COMPAT_TYPE, htonl(target))) diff --git a/queue-5.4/netfilter-nf_tables-add-nft_trans_prepare_error-to-deal-with-bound-set-chain.patch b/queue-5.4/netfilter-nf_tables-add-nft_trans_prepare_error-to-deal-with-bound-set-chain.patch new file mode 100644 index 00000000000..94908a86bd8 --- /dev/null +++ b/queue-5.4/netfilter-nf_tables-add-nft_trans_prepare_error-to-deal-with-bound-set-chain.patch @@ -0,0 +1,109 @@ +From stable-owner@vger.kernel.org Wed Jul 5 18:55:01 2023 +From: Pablo Neira Ayuso +Date: Wed, 5 Jul 2023 18:54:20 +0200 +Subject: netfilter: nf_tables: add NFT_TRANS_PREPARE_ERROR to deal with bound set/chain +To: netfilter-devel@vger.kernel.org +Cc: sashal@kernel.org, gregkh@linuxfoundation.org, stable@vger.kernel.org +Message-ID: <20230705165423.50054-8-pablo@netfilter.org> + +From: Pablo Neira Ayuso + +[ 26b5a5712eb85e253724e56a54c17f8519bd8e4e ] + +Add a new state to deal with rule expressions deactivation from the +newrule error path, otherwise the anonymous set remains in the list in +inactive state for the next generation. Mark the set/chain transaction +as unbound so the abort path releases this object, set it as inactive in +the next generation so it is not reachable anymore from this transaction +and reference counter is dropped. + +Fixes: 1240eb93f061 ("netfilter: nf_tables: incorrect error path handling with NFT_MSG_NEWRULE") +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman +--- + include/net/netfilter/nf_tables.h | 1 + + net/netfilter/nf_tables_api.c | 27 +++++++++++++++++++++++---- + 2 files changed, 24 insertions(+), 4 deletions(-) + +--- a/include/net/netfilter/nf_tables.h ++++ b/include/net/netfilter/nf_tables.h +@@ -756,6 +756,7 @@ struct nft_expr_type { + + enum nft_trans_phase { + NFT_TRANS_PREPARE, ++ NFT_TRANS_PREPARE_ERROR, + NFT_TRANS_ABORT, + NFT_TRANS_COMMIT, + NFT_TRANS_RELEASE +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -137,7 +137,8 @@ static void nft_trans_destroy(struct nft + kfree(trans); + } + +-static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set) ++static void __nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set, ++ bool bind) + { + struct nftables_pernet *nft_net; + struct net *net = ctx->net; +@@ -151,16 +152,26 @@ static void nft_set_trans_bind(const str + switch (trans->msg_type) { + case NFT_MSG_NEWSET: + if (nft_trans_set(trans) == set) +- nft_trans_set_bound(trans) = true; ++ nft_trans_set_bound(trans) = bind; + break; + case NFT_MSG_NEWSETELEM: + if (nft_trans_elem_set(trans) == set) +- nft_trans_elem_set_bound(trans) = true; ++ nft_trans_elem_set_bound(trans) = bind; + break; + } + } + } + ++static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set) ++{ ++ return __nft_set_trans_bind(ctx, set, true); ++} ++ ++static void nft_set_trans_unbind(const struct nft_ctx *ctx, struct nft_set *set) ++{ ++ return __nft_set_trans_bind(ctx, set, false); ++} ++ + static void nft_trans_commit_list_add_tail(struct net *net, struct nft_trans *trans) + { + struct nftables_pernet *nft_net; +@@ -2939,7 +2950,7 @@ static int nf_tables_newrule(struct net + + return 0; + err2: +- nft_rule_expr_deactivate(&ctx, rule, NFT_TRANS_PREPARE); ++ nft_rule_expr_deactivate(&ctx, rule, NFT_TRANS_PREPARE_ERROR); + nf_tables_rule_destroy(&ctx, rule); + err1: + for (i = 0; i < n; i++) { +@@ -3959,6 +3970,13 @@ void nf_tables_deactivate_set(const stru + enum nft_trans_phase phase) + { + switch (phase) { ++ case NFT_TRANS_PREPARE_ERROR: ++ nft_set_trans_unbind(ctx, set); ++ if (nft_set_is_anonymous(set)) ++ nft_deactivate_next(ctx->net, set); ++ ++ set->use--; ++ break; + case NFT_TRANS_PREPARE: + if (nft_set_is_anonymous(set)) + nft_deactivate_next(ctx->net, set); +@@ -5724,6 +5742,7 @@ void nf_tables_deactivate_flowtable(cons + enum nft_trans_phase phase) + { + switch (phase) { ++ case NFT_TRANS_PREPARE_ERROR: + case NFT_TRANS_PREPARE: + case NFT_TRANS_ABORT: + case NFT_TRANS_RELEASE: diff --git a/queue-5.4/netfilter-nf_tables-add-rescheduling-points-during-loop-detection-walks.patch b/queue-5.4/netfilter-nf_tables-add-rescheduling-points-during-loop-detection-walks.patch new file mode 100644 index 00000000000..8905a30f650 --- /dev/null +++ b/queue-5.4/netfilter-nf_tables-add-rescheduling-points-during-loop-detection-walks.patch @@ -0,0 +1,50 @@ +From stable-owner@vger.kernel.org Wed Jul 5 18:54:56 2023 +From: Pablo Neira Ayuso +Date: Wed, 5 Jul 2023 18:54:18 +0200 +Subject: netfilter: nf_tables: add rescheduling points during loop detection walks +To: netfilter-devel@vger.kernel.org +Cc: sashal@kernel.org, gregkh@linuxfoundation.org, stable@vger.kernel.org +Message-ID: <20230705165423.50054-6-pablo@netfilter.org> + +From: Florian Westphal + +[ 81ea010667417ef3f218dfd99b69769fe66c2b67 ] + +Add explicit rescheduling points during ruleset walk. + +Switching to a faster algorithm is possible but this is a much +smaller change, suitable for nf tree. + +Link: https://bugzilla.netfilter.org/show_bug.cgi?id=1460 +Signed-off-by: Florian Westphal +Acked-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman +--- + net/netfilter/nf_tables_api.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -2712,6 +2712,8 @@ int nft_chain_validate(const struct nft_ + if (err < 0) + return err; + } ++ ++ cond_resched(); + } + + return 0; +@@ -7379,9 +7381,13 @@ static int nf_tables_check_loops(const s + break; + } + } ++ ++ cond_resched(); + } + + list_for_each_entry(set, &ctx->table->sets, list) { ++ cond_resched(); ++ + if (!nft_is_active_next(ctx->net, set)) + continue; + if (!(set->flags & NFT_SET_MAP) || diff --git a/queue-5.4/netfilter-nf_tables-fix-nat-hook-table-deletion.patch b/queue-5.4/netfilter-nf_tables-fix-nat-hook-table-deletion.patch new file mode 100644 index 00000000000..e7eeb4a40f3 --- /dev/null +++ b/queue-5.4/netfilter-nf_tables-fix-nat-hook-table-deletion.patch @@ -0,0 +1,104 @@ +From stable-owner@vger.kernel.org Wed Jul 5 18:54:50 2023 +From: Pablo Neira Ayuso +Date: Wed, 5 Jul 2023 18:54:14 +0200 +Subject: netfilter: nf_tables: fix nat hook table deletion +To: netfilter-devel@vger.kernel.org +Cc: sashal@kernel.org, gregkh@linuxfoundation.org, stable@vger.kernel.org +Message-ID: <20230705165423.50054-2-pablo@netfilter.org> + +From: Florian Westphal + +[ 1e9451cbda456a170518b2bfd643e2cb980880bf ] + +sybot came up with following transaction: + add table ip syz0 + add chain ip syz0 syz2 { type nat hook prerouting priority 0; policy accept; } + add table ip syz0 { flags dormant; } + delete chain ip syz0 syz2 + delete table ip syz0 + +which yields: +hook not found, pf 2 num 0 +WARNING: CPU: 0 PID: 6775 at net/netfilter/core.c:413 __nf_unregister_net_hook+0x3e6/0x4a0 net/netfilter/core.c:413 +[..] + nft_unregister_basechain_hooks net/netfilter/nf_tables_api.c:206 [inline] + nft_table_disable net/netfilter/nf_tables_api.c:835 [inline] + nf_tables_table_disable net/netfilter/nf_tables_api.c:868 [inline] + nf_tables_commit+0x32d3/0x4d70 net/netfilter/nf_tables_api.c:7550 + nfnetlink_rcv_batch net/netfilter/nfnetlink.c:486 [inline] + nfnetlink_rcv_skb_batch net/netfilter/nfnetlink.c:544 [inline] + nfnetlink_rcv+0x14a5/0x1e50 net/netfilter/nfnetlink.c:562 + netlink_unicast_kernel net/netlink/af_netlink.c:1303 [inline] + +Problem is that when I added ability to override base hook registration +to make nat basechains register with the nat core instead of netfilter +core, I forgot to update nft_table_disable() to use that instead of +the 'raw' hook register interface. + +In syzbot transaction, the basechain is of 'nat' type. Its registered +with the nat core. The switch to 'dormant mode' attempts to delete from +netfilter core instead. + +After updating nft_table_disable/enable to use the correct helper, +nft_(un)register_basechain_hooks can be folded into the only remaining +caller. + +Because nft_trans_table_enable() won't do anything when the DORMANT flag +is set, remove the flag first, then re-add it in case re-enablement +fails, else this patch breaks sequence: + +add table ip x { flags dormant; } +/* add base chains */ +add table ip x + +The last 'add' will remove the dormant flags, but won't have any other +effect -- base chains are not registered. +Then, next 'set dormant flag' will create another 'hook not found' +splat. + +Reported-by: syzbot+2570f2c036e3da5db176@syzkaller.appspotmail.com +Fixes: 4e25ceb80b58 ("netfilter: nf_tables: allow chain type to override hook register") +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit 1e9451cbda456a170518b2bfd643e2cb980880bf) +Signed-off-by: Greg Kroah-Hartman +--- + net/netfilter/nf_tables_api.c | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -770,7 +770,7 @@ static void nft_table_disable(struct net + if (cnt && i++ == cnt) + break; + +- nf_unregister_net_hook(net, &nft_base_chain(chain)->ops); ++ nf_tables_unregister_hook(net, table, chain); + } + } + +@@ -785,7 +785,7 @@ static int nf_tables_table_enable(struct + if (!nft_is_base_chain(chain)) + continue; + +- err = nf_register_net_hook(net, &nft_base_chain(chain)->ops); ++ err = nf_tables_register_hook(net, table, chain); + if (err < 0) + goto err; + +@@ -829,11 +829,12 @@ static int nf_tables_updtable(struct nft + nft_trans_table_enable(trans) = false; + } else if (!(flags & NFT_TABLE_F_DORMANT) && + ctx->table->flags & NFT_TABLE_F_DORMANT) { ++ ctx->table->flags &= ~NFT_TABLE_F_DORMANT; + ret = nf_tables_table_enable(ctx->net, ctx->table); +- if (ret >= 0) { +- ctx->table->flags &= ~NFT_TABLE_F_DORMANT; ++ if (ret >= 0) + nft_trans_table_enable(trans) = true; +- } ++ else ++ ctx->table->flags |= NFT_TABLE_F_DORMANT; + } + if (ret < 0) + goto err; diff --git a/queue-5.4/netfilter-nf_tables-fix-scheduling-while-atomic-splat.patch b/queue-5.4/netfilter-nf_tables-fix-scheduling-while-atomic-splat.patch new file mode 100644 index 00000000000..56382fb2ce1 --- /dev/null +++ b/queue-5.4/netfilter-nf_tables-fix-scheduling-while-atomic-splat.patch @@ -0,0 +1,39 @@ +From stable-owner@vger.kernel.org Wed Jul 5 18:54:56 2023 +From: Pablo Neira Ayuso +Date: Wed, 5 Jul 2023 18:54:23 +0200 +Subject: netfilter: nf_tables: fix scheduling-while-atomic splat +To: netfilter-devel@vger.kernel.org +Cc: sashal@kernel.org, gregkh@linuxfoundation.org, stable@vger.kernel.org +Message-ID: <20230705165423.50054-11-pablo@netfilter.org> + +From: Florian Westphal + +[ 2024439bd5ceb145eeeb428b2a59e9b905153ac3 ] + +nf_tables_check_loops() can be called from rhashtable list +walk so cond_resched() cannot be used here. + +Fixes: 81ea01066741 ("netfilter: nf_tables: add rescheduling points during loop detection walks") +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman +--- + net/netfilter/nf_tables_api.c | 4 ---- + 1 file changed, 4 deletions(-) + +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -7428,13 +7428,9 @@ static int nf_tables_check_loops(const s + break; + } + } +- +- cond_resched(); + } + + list_for_each_entry(set, &ctx->table->sets, list) { +- cond_resched(); +- + if (!nft_is_active_next(ctx->net, set)) + continue; + if (!(set->flags & NFT_SET_MAP) || diff --git a/queue-5.4/netfilter-nf_tables-incorrect-error-path-handling-with-nft_msg_newrule.patch b/queue-5.4/netfilter-nf_tables-incorrect-error-path-handling-with-nft_msg_newrule.patch new file mode 100644 index 00000000000..9f021445f0f --- /dev/null +++ b/queue-5.4/netfilter-nf_tables-incorrect-error-path-handling-with-nft_msg_newrule.patch @@ -0,0 +1,73 @@ +From stable-owner@vger.kernel.org Wed Jul 5 18:54:55 2023 +From: Pablo Neira Ayuso +Date: Wed, 5 Jul 2023 18:54:19 +0200 +Subject: netfilter: nf_tables: incorrect error path handling with NFT_MSG_NEWRULE +To: netfilter-devel@vger.kernel.org +Cc: sashal@kernel.org, gregkh@linuxfoundation.org, stable@vger.kernel.org +Message-ID: <20230705165423.50054-7-pablo@netfilter.org> + +From: Pablo Neira Ayuso + +[ 1240eb93f0616b21c675416516ff3d74798fdc97 ] + +In case of error when adding a new rule that refers to an anonymous set, +deactivate expressions via NFT_TRANS_PREPARE state, not NFT_TRANS_RELEASE. +Thus, the lookup expression marks anonymous sets as inactive in the next +generation to ensure it is not reachable in this transaction anymore and +decrement the set refcount as introduced by c1592a89942e ("netfilter: +nf_tables: deactivate anonymous set from preparation phase"). The abort +step takes care of undoing the anonymous set. + +This is also consistent with rule deletion, where NFT_TRANS_PREPARE is +used. Note that this error path is exercised in the preparation step of +the commit protocol. This patch replaces nf_tables_rule_release() by the +deactivate and destroy calls, this time with NFT_TRANS_PREPARE. + +Due to this incorrect error handling, it is possible to access a +dangling pointer to the anonymous set that remains in the transaction +list. + +[1009.379054] BUG: KASAN: use-after-free in nft_set_lookup_global+0x147/0x1a0 [nf_tables] +[1009.379106] Read of size 8 at addr ffff88816c4c8020 by task nft-rule-add/137110 +[1009.379116] CPU: 7 PID: 137110 Comm: nft-rule-add Not tainted 6.4.0-rc4+ #256 +[1009.379128] Call Trace: +[1009.379132] +[1009.379135] dump_stack_lvl+0x33/0x50 +[1009.379146] ? nft_set_lookup_global+0x147/0x1a0 [nf_tables] +[1009.379191] print_address_description.constprop.0+0x27/0x300 +[1009.379201] kasan_report+0x107/0x120 +[1009.379210] ? nft_set_lookup_global+0x147/0x1a0 [nf_tables] +[1009.379255] nft_set_lookup_global+0x147/0x1a0 [nf_tables] +[1009.379302] nft_lookup_init+0xa5/0x270 [nf_tables] +[1009.379350] nf_tables_newrule+0x698/0xe50 [nf_tables] +[1009.379397] ? nf_tables_rule_release+0xe0/0xe0 [nf_tables] +[1009.379441] ? kasan_unpoison+0x23/0x50 +[1009.379450] nfnetlink_rcv_batch+0x97c/0xd90 [nfnetlink] +[1009.379470] ? nfnetlink_rcv_msg+0x480/0x480 [nfnetlink] +[1009.379485] ? __alloc_skb+0xb8/0x1e0 +[1009.379493] ? __alloc_skb+0xb8/0x1e0 +[1009.379502] ? entry_SYSCALL_64_after_hwframe+0x46/0xb0 +[1009.379509] ? unwind_get_return_address+0x2a/0x40 +[1009.379517] ? write_profile+0xc0/0xc0 +[1009.379524] ? avc_lookup+0x8f/0xc0 +[1009.379532] ? __rcu_read_unlock+0x43/0x60 + +Fixes: 958bee14d071 ("netfilter: nf_tables: use new transaction infrastructure to handle sets") +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman +--- + net/netfilter/nf_tables_api.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -2939,7 +2939,8 @@ static int nf_tables_newrule(struct net + + return 0; + err2: +- nf_tables_rule_release(&ctx, rule); ++ nft_rule_expr_deactivate(&ctx, rule, NFT_TRANS_PREPARE); ++ nf_tables_rule_destroy(&ctx, rule); + err1: + for (i = 0; i < n; i++) { + if (info[i].ops) { diff --git a/queue-5.4/netfilter-nf_tables-reject-unbound-anonymous-set-before-commit-phase.patch b/queue-5.4/netfilter-nf_tables-reject-unbound-anonymous-set-before-commit-phase.patch new file mode 100644 index 00000000000..5c341b110b8 --- /dev/null +++ b/queue-5.4/netfilter-nf_tables-reject-unbound-anonymous-set-before-commit-phase.patch @@ -0,0 +1,142 @@ +From stable-owner@vger.kernel.org Wed Jul 5 18:54:56 2023 +From: Pablo Neira Ayuso +Date: Wed, 5 Jul 2023 18:54:21 +0200 +Subject: netfilter: nf_tables: reject unbound anonymous set before commit phase +To: netfilter-devel@vger.kernel.org +Cc: sashal@kernel.org, gregkh@linuxfoundation.org, stable@vger.kernel.org +Message-ID: <20230705165423.50054-9-pablo@netfilter.org> + +From: Pablo Neira Ayuso + +[ 938154b93be8cd611ddfd7bafc1849f3c4355201 ] + +Add a new list to track set transaction and to check for unbound +anonymous sets before entering the commit phase. + +Bail out at the end of the transaction handling if an anonymous set +remains unbound. + +Fixes: 96518518cc41 ("netfilter: add nftables") +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman +--- + include/net/netfilter/nf_tables.h | 3 +++ + net/netfilter/nf_tables_api.c | 36 +++++++++++++++++++++++++++++++----- + 2 files changed, 34 insertions(+), 5 deletions(-) + +--- a/include/net/netfilter/nf_tables.h ++++ b/include/net/netfilter/nf_tables.h +@@ -1364,6 +1364,7 @@ static inline void nft_set_elem_clear_bu + * struct nft_trans - nf_tables object update in transaction + * + * @list: used internally ++ * @binding_list: list of objects with possible bindings + * @msg_type: message type + * @put_net: ctx->net needs to be put + * @ctx: transaction context +@@ -1371,6 +1372,7 @@ static inline void nft_set_elem_clear_bu + */ + struct nft_trans { + struct list_head list; ++ struct list_head binding_list; + int msg_type; + bool put_net; + struct nft_ctx ctx; +@@ -1476,6 +1478,7 @@ __be64 nf_jiffies64_to_msecs(u64 input); + struct nftables_pernet { + struct list_head tables; + struct list_head commit_list; ++ struct list_head binding_list; + struct list_head module_list; + struct list_head notify_list; + struct mutex commit_mutex; +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -119,6 +119,7 @@ static struct nft_trans *nft_trans_alloc + return NULL; + + INIT_LIST_HEAD(&trans->list); ++ INIT_LIST_HEAD(&trans->binding_list); + trans->msg_type = msg_type; + trans->ctx = *ctx; + +@@ -131,9 +132,15 @@ static struct nft_trans *nft_trans_alloc + return nft_trans_alloc_gfp(ctx, msg_type, size, GFP_KERNEL); + } + +-static void nft_trans_destroy(struct nft_trans *trans) ++static void nft_trans_list_del(struct nft_trans *trans) + { + list_del(&trans->list); ++ list_del(&trans->binding_list); ++} ++ ++static void nft_trans_destroy(struct nft_trans *trans) ++{ ++ nft_trans_list_del(trans); + kfree(trans); + } + +@@ -174,9 +181,15 @@ static void nft_set_trans_unbind(const s + + static void nft_trans_commit_list_add_tail(struct net *net, struct nft_trans *trans) + { +- struct nftables_pernet *nft_net; ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); ++ ++ switch (trans->msg_type) { ++ case NFT_MSG_NEWSET: ++ if (nft_set_is_anonymous(nft_trans_set(trans))) ++ list_add_tail(&trans->binding_list, &nft_net->binding_list); ++ break; ++ } + +- nft_net = net_generic(net, nf_tables_net_id); + list_add_tail(&trans->list, &nft_net->commit_list); + } + +@@ -6697,7 +6710,7 @@ static void nf_tables_trans_destroy_work + synchronize_rcu(); + + list_for_each_entry_safe(trans, next, &head, list) { +- list_del(&trans->list); ++ nft_trans_list_del(trans); + nft_commit_release(trans); + } + } +@@ -6901,6 +6914,18 @@ static int nf_tables_commit(struct net * + return 0; + } + ++ list_for_each_entry(trans, &nft_net->binding_list, binding_list) { ++ switch (trans->msg_type) { ++ case NFT_MSG_NEWSET: ++ if (nft_set_is_anonymous(nft_trans_set(trans)) && ++ !nft_trans_set_bound(trans)) { ++ pr_warn_once("nftables ruleset with unbound set\n"); ++ return -EINVAL; ++ } ++ break; ++ } ++ } ++ + /* 0. Validate ruleset, otherwise roll back for error reporting. */ + if (nf_tables_validate(net) < 0) + return -EAGAIN; +@@ -7249,7 +7274,7 @@ static int __nf_tables_abort(struct net + + list_for_each_entry_safe_reverse(trans, next, + &nft_net->commit_list, list) { +- list_del(&trans->list); ++ nft_trans_list_del(trans); + nf_tables_abort_release(trans); + } + +@@ -7914,6 +7939,7 @@ static int __net_init nf_tables_init_net + + INIT_LIST_HEAD(&nft_net->tables); + INIT_LIST_HEAD(&nft_net->commit_list); ++ INIT_LIST_HEAD(&nft_net->binding_list); + INIT_LIST_HEAD(&nft_net->module_list); + INIT_LIST_HEAD(&nft_net->notify_list); + mutex_init(&nft_net->commit_mutex); diff --git a/queue-5.4/netfilter-nf_tables-unbind-non-anonymous-set-if-rule-construction-fails.patch b/queue-5.4/netfilter-nf_tables-unbind-non-anonymous-set-if-rule-construction-fails.patch new file mode 100644 index 00000000000..b16cedfa942 --- /dev/null +++ b/queue-5.4/netfilter-nf_tables-unbind-non-anonymous-set-if-rule-construction-fails.patch @@ -0,0 +1,33 @@ +From stable-owner@vger.kernel.org Wed Jul 5 18:54:56 2023 +From: Pablo Neira Ayuso +Date: Wed, 5 Jul 2023 18:54:22 +0200 +Subject: netfilter: nf_tables: unbind non-anonymous set if rule construction fails +To: netfilter-devel@vger.kernel.org +Cc: sashal@kernel.org, gregkh@linuxfoundation.org, stable@vger.kernel.org +Message-ID: <20230705165423.50054-10-pablo@netfilter.org> + +From: Pablo Neira Ayuso + +[ 3e70489721b6c870252c9082c496703677240f53 ] + +Otherwise a dangling reference to a rule object that is gone remains +in the set binding list. + +Fixes: 26b5a5712eb8 ("netfilter: nf_tables: add NFT_TRANS_PREPARE_ERROR to deal with bound set/chain") +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman +--- + net/netfilter/nf_tables_api.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -3987,6 +3987,8 @@ void nf_tables_deactivate_set(const stru + nft_set_trans_unbind(ctx, set); + if (nft_set_is_anonymous(set)) + nft_deactivate_next(ctx->net, set); ++ else ++ list_del_rcu(&binding->list); + + set->use--; + break; diff --git a/queue-5.4/netfilter-nf_tables-use-net_generic-infra-for-transaction-data.patch b/queue-5.4/netfilter-nf_tables-use-net_generic-infra-for-transaction-data.patch new file mode 100644 index 00000000000..72ceee23a7f --- /dev/null +++ b/queue-5.4/netfilter-nf_tables-use-net_generic-infra-for-transaction-data.patch @@ -0,0 +1,1214 @@ +From stable-owner@vger.kernel.org Wed Jul 5 18:54:55 2023 +From: Pablo Neira Ayuso +Date: Wed, 5 Jul 2023 18:54:17 +0200 +Subject: netfilter: nf_tables: use net_generic infra for transaction data +To: netfilter-devel@vger.kernel.org +Cc: sashal@kernel.org, gregkh@linuxfoundation.org, stable@vger.kernel.org +Message-ID: <20230705165423.50054-5-pablo@netfilter.org> + +From: Florian Westphal + +[ 0854db2aaef3fcdd3498a9d299c60adea2aa3dc6 ] + +This moves all nf_tables pernet data from struct net to a net_generic +extension, with the exception of the gencursor. + +The latter is used in the data path and also outside of the nf_tables +core. All others are only used from the configuration plane. + +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman +--- + include/net/netfilter/nf_tables.h | 10 + + include/net/netns/nftables.h | 6 + net/netfilter/nf_tables_api.c | 330 +++++++++++++++++++++++--------------- + net/netfilter/nf_tables_offload.c | 29 ++- + net/netfilter/nft_chain_filter.c | 11 - + net/netfilter/nft_dynset.c | 6 + 6 files changed, 245 insertions(+), 147 deletions(-) + +--- a/include/net/netfilter/nf_tables.h ++++ b/include/net/netfilter/nf_tables.h +@@ -1472,4 +1472,14 @@ void nf_tables_trans_destroy_flush_work( + int nf_msecs_to_jiffies64(const struct nlattr *nla, u64 *result); + __be64 nf_jiffies64_to_msecs(u64 input); + ++struct nftables_pernet { ++ struct list_head tables; ++ struct list_head commit_list; ++ struct list_head module_list; ++ struct list_head notify_list; ++ struct mutex commit_mutex; ++ unsigned int base_seq; ++ u8 validate_state; ++}; ++ + #endif /* _NET_NF_TABLES_H */ +--- a/include/net/netns/nftables.h ++++ b/include/net/netns/nftables.h +@@ -5,13 +5,7 @@ + #include + + struct netns_nftables { +- struct list_head tables; +- struct list_head commit_list; +- struct list_head module_list; +- struct mutex commit_mutex; +- unsigned int base_seq; + u8 gencursor; +- u8 validate_state; + }; + + #endif +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -20,10 +20,13 @@ + #include + #include + #include ++#include + #include + + #define NFT_MODULE_AUTOLOAD_LIMIT (MODULE_NAME_LEN - sizeof("nft-expr-255-")) + ++unsigned int nf_tables_net_id __read_mostly; ++ + static LIST_HEAD(nf_tables_expressions); + static LIST_HEAD(nf_tables_objects); + static LIST_HEAD(nf_tables_flowtables); +@@ -67,7 +70,9 @@ static const struct rhashtable_params nf + + static void nft_validate_state_update(struct net *net, u8 new_validate_state) + { +- switch (net->nft.validate_state) { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); ++ ++ switch (nft_net->validate_state) { + case NFT_VALIDATE_SKIP: + WARN_ON_ONCE(new_validate_state == NFT_VALIDATE_DO); + break; +@@ -78,7 +83,7 @@ static void nft_validate_state_update(st + return; + } + +- net->nft.validate_state = new_validate_state; ++ nft_net->validate_state = new_validate_state; + } + static void nf_tables_trans_destroy_work(struct work_struct *w); + static DECLARE_WORK(trans_destroy_work, nf_tables_trans_destroy_work); +@@ -134,13 +139,15 @@ static void nft_trans_destroy(struct nft + + static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set) + { ++ struct nftables_pernet *nft_net; + struct net *net = ctx->net; + struct nft_trans *trans; + + if (!nft_set_is_anonymous(set)) + return; + +- list_for_each_entry_reverse(trans, &net->nft.commit_list, list) { ++ nft_net = net_generic(net, nf_tables_net_id); ++ list_for_each_entry_reverse(trans, &nft_net->commit_list, list) { + switch (trans->msg_type) { + case NFT_MSG_NEWSET: + if (nft_trans_set(trans) == set) +@@ -154,6 +161,14 @@ static void nft_set_trans_bind(const str + } + } + ++static void nft_trans_commit_list_add_tail(struct net *net, struct nft_trans *trans) ++{ ++ struct nftables_pernet *nft_net; ++ ++ nft_net = net_generic(net, nf_tables_net_id); ++ list_add_tail(&trans->list, &nft_net->commit_list); ++} ++ + static int nf_tables_register_hook(struct net *net, + const struct nft_table *table, + struct nft_chain *chain) +@@ -204,7 +219,7 @@ static int nft_trans_table_add(struct nf + if (msg_type == NFT_MSG_NEWTABLE) + nft_activate_next(ctx->net, ctx->table); + +- list_add_tail(&trans->list, &ctx->net->nft.commit_list); ++ nft_trans_commit_list_add_tail(ctx->net, trans); + return 0; + } + +@@ -231,7 +246,7 @@ static struct nft_trans *nft_trans_chain + if (msg_type == NFT_MSG_NEWCHAIN) + nft_activate_next(ctx->net, ctx->chain); + +- list_add_tail(&trans->list, &ctx->net->nft.commit_list); ++ nft_trans_commit_list_add_tail(ctx->net, trans); + return trans; + } + +@@ -304,7 +319,7 @@ static struct nft_trans *nft_trans_rule_ + ntohl(nla_get_be32(ctx->nla[NFTA_RULE_ID])); + } + nft_trans_rule(trans) = rule; +- list_add_tail(&trans->list, &ctx->net->nft.commit_list); ++ nft_trans_commit_list_add_tail(ctx->net, trans); + + return trans; + } +@@ -359,7 +374,7 @@ static int nft_trans_set_add(const struc + nft_activate_next(ctx->net, set); + } + nft_trans_set(trans) = set; +- list_add_tail(&trans->list, &ctx->net->nft.commit_list); ++ nft_trans_commit_list_add_tail(ctx->net, trans); + + return 0; + } +@@ -391,7 +406,7 @@ static int nft_trans_obj_add(struct nft_ + nft_activate_next(ctx->net, obj); + + nft_trans_obj(trans) = obj; +- list_add_tail(&trans->list, &ctx->net->nft.commit_list); ++ nft_trans_commit_list_add_tail(ctx->net, trans); + + return 0; + } +@@ -424,7 +439,7 @@ static int nft_trans_flowtable_add(struc + nft_activate_next(ctx->net, flowtable); + + nft_trans_flowtable(trans) = flowtable; +- list_add_tail(&trans->list, &ctx->net->nft.commit_list); ++ nft_trans_commit_list_add_tail(ctx->net, trans); + + return 0; + } +@@ -452,13 +467,15 @@ static struct nft_table *nft_table_looku + const struct nlattr *nla, + u8 family, u8 genmask) + { ++ struct nftables_pernet *nft_net; + struct nft_table *table; + + if (nla == NULL) + return ERR_PTR(-EINVAL); + +- list_for_each_entry_rcu(table, &net->nft.tables, list, +- lockdep_is_held(&net->nft.commit_mutex)) { ++ nft_net = net_generic(net, nf_tables_net_id); ++ list_for_each_entry_rcu(table, &nft_net->tables, list, ++ lockdep_is_held(&nft_net->commit_mutex)) { + if (!nla_strcmp(nla, table->name) && + table->family == family && + nft_active_genmask(table, genmask)) +@@ -472,9 +489,11 @@ static struct nft_table *nft_table_looku + const struct nlattr *nla, + u8 genmask) + { ++ struct nftables_pernet *nft_net; + struct nft_table *table; + +- list_for_each_entry(table, &net->nft.tables, list) { ++ nft_net = net_generic(net, nf_tables_net_id); ++ list_for_each_entry(table, &nft_net->tables, list) { + if (be64_to_cpu(nla_get_be64(nla)) == table->handle && + nft_active_genmask(table, genmask)) + return table; +@@ -526,6 +545,7 @@ struct nft_module_request { + static int nft_request_module(struct net *net, const char *fmt, ...) + { + char module_name[MODULE_NAME_LEN]; ++ struct nftables_pernet *nft_net; + struct nft_module_request *req; + va_list args; + int ret; +@@ -536,7 +556,8 @@ static int nft_request_module(struct net + if (ret >= MODULE_NAME_LEN) + return 0; + +- list_for_each_entry(req, &net->nft.module_list, list) { ++ nft_net = net_generic(net, nf_tables_net_id); ++ list_for_each_entry(req, &nft_net->module_list, list) { + if (!strcmp(req->module, module_name)) { + if (req->done) + return 0; +@@ -552,7 +573,7 @@ static int nft_request_module(struct net + + req->done = false; + strlcpy(req->module, module_name, MODULE_NAME_LEN); +- list_add_tail(&req->list, &net->nft.module_list); ++ list_add_tail(&req->list, &nft_net->module_list); + + return -EAGAIN; + } +@@ -590,7 +611,9 @@ nf_tables_chain_type_lookup(struct net * + + static __be16 nft_base_seq(const struct net *net) + { +- return htons(net->nft.base_seq & 0xffff); ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); ++ ++ return htons(nft_net->base_seq & 0xffff); + } + + static const struct nla_policy nft_table_policy[NFTA_TABLE_MAX + 1] = { +@@ -658,15 +681,17 @@ static int nf_tables_dump_tables(struct + struct netlink_callback *cb) + { + const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); ++ struct nftables_pernet *nft_net; + const struct nft_table *table; + unsigned int idx = 0, s_idx = cb->args[0]; + struct net *net = sock_net(skb->sk); + int family = nfmsg->nfgen_family; + + rcu_read_lock(); +- cb->seq = net->nft.base_seq; ++ nft_net = net_generic(net, nf_tables_net_id); ++ cb->seq = nft_net->base_seq; + +- list_for_each_entry_rcu(table, &net->nft.tables, list) { ++ list_for_each_entry_rcu(table, &nft_net->tables, list) { + if (family != NFPROTO_UNSPEC && family != table->family) + continue; + +@@ -840,7 +865,7 @@ static int nf_tables_updtable(struct nft + goto err; + + nft_trans_table_update(trans) = true; +- list_add_tail(&trans->list, &ctx->net->nft.commit_list); ++ nft_trans_commit_list_add_tail(ctx->net, trans); + return 0; + err: + nft_trans_destroy(trans); +@@ -903,6 +928,7 @@ static int nf_tables_newtable(struct net + const struct nlattr * const nla[], + struct netlink_ext_ack *extack) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + const struct nfgenmsg *nfmsg = nlmsg_data(nlh); + u8 genmask = nft_genmask_next(net); + int family = nfmsg->nfgen_family; +@@ -912,7 +938,7 @@ static int nf_tables_newtable(struct net + struct nft_ctx ctx; + int err; + +- lockdep_assert_held(&net->nft.commit_mutex); ++ lockdep_assert_held(&nft_net->commit_mutex); + attr = nla[NFTA_TABLE_NAME]; + table = nft_table_lookup(net, attr, family, genmask); + if (IS_ERR(table)) { +@@ -962,7 +988,7 @@ static int nf_tables_newtable(struct net + if (err < 0) + goto err_trans; + +- list_add_tail_rcu(&table->list, &net->nft.tables); ++ list_add_tail_rcu(&table->list, &nft_net->tables); + return 0; + err_trans: + rhltable_destroy(&table->chains_ht); +@@ -1042,11 +1068,12 @@ out: + + static int nft_flush(struct nft_ctx *ctx, int family) + { ++ struct nftables_pernet *nft_net = net_generic(ctx->net, nf_tables_net_id); + struct nft_table *table, *nt; + const struct nlattr * const *nla = ctx->nla; + int err = 0; + +- list_for_each_entry_safe(table, nt, &ctx->net->nft.tables, list) { ++ list_for_each_entry_safe(table, nt, &nft_net->tables, list) { + if (family != AF_UNSPEC && table->family != family) + continue; + +@@ -1160,7 +1187,9 @@ nft_chain_lookup_byhandle(const struct n + static bool lockdep_commit_lock_is_held(const struct net *net) + { + #ifdef CONFIG_PROVE_LOCKING +- return lockdep_is_held(&net->nft.commit_mutex); ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); ++ ++ return lockdep_is_held(&nft_net->commit_mutex); + #else + return true; + #endif +@@ -1363,11 +1392,13 @@ static int nf_tables_dump_chains(struct + unsigned int idx = 0, s_idx = cb->args[0]; + struct net *net = sock_net(skb->sk); + int family = nfmsg->nfgen_family; ++ struct nftables_pernet *nft_net; + + rcu_read_lock(); +- cb->seq = net->nft.base_seq; ++ nft_net = net_generic(net, nf_tables_net_id); ++ cb->seq = nft_net->base_seq; + +- list_for_each_entry_rcu(table, &net->nft.tables, list) { ++ list_for_each_entry_rcu(table, &nft_net->tables, list) { + if (family != NFPROTO_UNSPEC && family != table->family) + continue; + +@@ -1553,12 +1584,13 @@ static int nft_chain_parse_hook(struct n + struct nft_chain_hook *hook, u8 family, + bool autoload) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + struct nlattr *ha[NFTA_HOOK_MAX + 1]; + const struct nft_chain_type *type; + struct net_device *dev; + int err; + +- lockdep_assert_held(&net->nft.commit_mutex); ++ lockdep_assert_held(&nft_net->commit_mutex); + lockdep_nfnl_nft_mutex_not_held(); + + err = nla_parse_nested_deprecated(ha, NFTA_HOOK_MAX, +@@ -1843,6 +1875,7 @@ static int nf_tables_updchain(struct nft + + if (nla[NFTA_CHAIN_HANDLE] && + nla[NFTA_CHAIN_NAME]) { ++ struct nftables_pernet *nft_net = net_generic(ctx->net, nf_tables_net_id); + struct nft_trans *tmp; + char *name; + +@@ -1852,7 +1885,7 @@ static int nf_tables_updchain(struct nft + goto err; + + err = -EEXIST; +- list_for_each_entry(tmp, &ctx->net->nft.commit_list, list) { ++ list_for_each_entry(tmp, &nft_net->commit_list, list) { + if (tmp->msg_type == NFT_MSG_NEWCHAIN && + tmp->ctx.table == table && + nft_trans_chain_update(tmp) && +@@ -1865,7 +1898,7 @@ static int nf_tables_updchain(struct nft + + nft_trans_chain_name(trans) = name; + } +- list_add_tail(&trans->list, &ctx->net->nft.commit_list); ++ nft_trans_commit_list_add_tail(ctx->net, trans); + + return 0; + err: +@@ -1879,6 +1912,7 @@ static int nf_tables_newchain(struct net + const struct nlattr * const nla[], + struct netlink_ext_ack *extack) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + const struct nfgenmsg *nfmsg = nlmsg_data(nlh); + u8 genmask = nft_genmask_next(net); + int family = nfmsg->nfgen_family; +@@ -1890,7 +1924,7 @@ static int nf_tables_newchain(struct net + u64 handle = 0; + u32 flags = 0; + +- lockdep_assert_held(&net->nft.commit_mutex); ++ lockdep_assert_held(&nft_net->commit_mutex); + + table = nft_table_lookup(net, nla[NFTA_CHAIN_TABLE], family, genmask); + if (IS_ERR(table)) { +@@ -2478,11 +2512,13 @@ static int nf_tables_dump_rules(struct s + unsigned int idx = 0; + struct net *net = sock_net(skb->sk); + int family = nfmsg->nfgen_family; ++ struct nftables_pernet *nft_net; + + rcu_read_lock(); +- cb->seq = net->nft.base_seq; ++ nft_net = net_generic(net, nf_tables_net_id); ++ cb->seq = nft_net->base_seq; + +- list_for_each_entry_rcu(table, &net->nft.tables, list) { ++ list_for_each_entry_rcu(table, &nft_net->tables, list) { + if (family != NFPROTO_UNSPEC && family != table->family) + continue; + +@@ -2715,6 +2751,7 @@ static int nf_tables_newrule(struct net + const struct nlattr * const nla[], + struct netlink_ext_ack *extack) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + const struct nfgenmsg *nfmsg = nlmsg_data(nlh); + u8 genmask = nft_genmask_next(net); + struct nft_expr_info *info = NULL; +@@ -2732,7 +2769,7 @@ static int nf_tables_newrule(struct net + int err, rem; + u64 handle, pos_handle; + +- lockdep_assert_held(&net->nft.commit_mutex); ++ lockdep_assert_held(&nft_net->commit_mutex); + + table = nft_table_lookup(net, nla[NFTA_RULE_TABLE], family, genmask); + if (IS_ERR(table)) { +@@ -2887,7 +2924,7 @@ static int nf_tables_newrule(struct net + kvfree(info); + chain->use++; + +- if (net->nft.validate_state == NFT_VALIDATE_DO) ++ if (nft_net->validate_state == NFT_VALIDATE_DO) + return nft_table_validate(net, table); + + if (chain->flags & NFT_CHAIN_HW_OFFLOAD) { +@@ -2917,10 +2954,11 @@ static struct nft_rule *nft_rule_lookup_ + const struct nft_chain *chain, + const struct nlattr *nla) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + u32 id = ntohl(nla_get_be32(nla)); + struct nft_trans *trans; + +- list_for_each_entry(trans, &net->nft.commit_list, list) { ++ list_for_each_entry(trans, &nft_net->commit_list, list) { + struct nft_rule *rule = nft_trans_rule(trans); + + if (trans->msg_type == NFT_MSG_NEWRULE && +@@ -3039,12 +3077,13 @@ nft_select_set_ops(const struct nft_ctx + const struct nft_set_desc *desc, + enum nft_set_policies policy) + { ++ struct nftables_pernet *nft_net = net_generic(ctx->net, nf_tables_net_id); + const struct nft_set_ops *ops, *bops; + struct nft_set_estimate est, best; + const struct nft_set_type *type; + u32 flags = 0; + +- lockdep_assert_held(&ctx->net->nft.commit_mutex); ++ lockdep_assert_held(&nft_net->commit_mutex); + lockdep_nfnl_nft_mutex_not_held(); + #ifdef CONFIG_MODULES + if (list_empty(&nf_tables_set_types)) { +@@ -3189,10 +3228,11 @@ static struct nft_set *nft_set_lookup_by + const struct nft_table *table, + const struct nlattr *nla, u8 genmask) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + struct nft_trans *trans; + u32 id = ntohl(nla_get_be32(nla)); + +- list_for_each_entry(trans, &net->nft.commit_list, list) { ++ list_for_each_entry(trans, &nft_net->commit_list, list) { + if (trans->msg_type == NFT_MSG_NEWSET) { + struct nft_set *set = nft_trans_set(trans); + +@@ -3406,14 +3446,16 @@ static int nf_tables_dump_sets(struct sk + struct nft_table *table, *cur_table = (struct nft_table *)cb->args[2]; + struct net *net = sock_net(skb->sk); + struct nft_ctx *ctx = cb->data, ctx_set; ++ struct nftables_pernet *nft_net; + + if (cb->args[1]) + return skb->len; + + rcu_read_lock(); +- cb->seq = net->nft.base_seq; ++ nft_net = net_generic(net, nf_tables_net_id); ++ cb->seq = nft_net->base_seq; + +- list_for_each_entry_rcu(table, &net->nft.tables, list) { ++ list_for_each_entry_rcu(table, &nft_net->tables, list) { + if (ctx->family != NFPROTO_UNSPEC && + ctx->family != table->family) + continue; +@@ -4119,6 +4161,7 @@ static int nf_tables_dump_set(struct sk_ + { + struct nft_set_dump_ctx *dump_ctx = cb->data; + struct net *net = sock_net(skb->sk); ++ struct nftables_pernet *nft_net; + struct nft_table *table; + struct nft_set *set; + struct nft_set_dump_args args; +@@ -4129,7 +4172,8 @@ static int nf_tables_dump_set(struct sk_ + int event; + + rcu_read_lock(); +- list_for_each_entry_rcu(table, &net->nft.tables, list) { ++ nft_net = net_generic(net, nf_tables_net_id); ++ list_for_each_entry_rcu(table, &nft_net->tables, list) { + if (dump_ctx->ctx.family != NFPROTO_UNSPEC && + dump_ctx->ctx.family != table->family) + continue; +@@ -4733,7 +4777,7 @@ static int nft_add_set_elem(struct nft_c + } + + nft_trans_elem(trans) = elem; +- list_add_tail(&trans->list, &ctx->net->nft.commit_list); ++ nft_trans_commit_list_add_tail(ctx->net, trans); + return 0; + + err6: +@@ -4758,6 +4802,7 @@ static int nf_tables_newsetelem(struct n + const struct nlattr * const nla[], + struct netlink_ext_ack *extack) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + u8 genmask = nft_genmask_next(net); + const struct nlattr *attr; + struct nft_set *set; +@@ -4787,7 +4832,7 @@ static int nf_tables_newsetelem(struct n + return err; + } + +- if (net->nft.validate_state == NFT_VALIDATE_DO) ++ if (nft_net->validate_state == NFT_VALIDATE_DO) + return nft_table_validate(net, ctx.table); + + return 0; +@@ -4900,7 +4945,7 @@ static int nft_del_setelem(struct nft_ct + nft_set_elem_deactivate(ctx->net, set, &elem); + + nft_trans_elem(trans) = elem; +- list_add_tail(&trans->list, &ctx->net->nft.commit_list); ++ nft_trans_commit_list_add_tail(ctx->net, trans); + return 0; + + fail_ops: +@@ -4934,7 +4979,7 @@ static int nft_flush_set(const struct nf + nft_set_elem_deactivate(ctx->net, set, elem); + nft_trans_elem_set(trans) = set; + nft_trans_elem(trans) = *elem; +- list_add_tail(&trans->list, &ctx->net->nft.commit_list); ++ nft_trans_commit_list_add_tail(ctx->net, trans); + + return 0; + err1: +@@ -5233,7 +5278,7 @@ static int nf_tables_updobj(const struct + nft_trans_obj(trans) = obj; + nft_trans_obj_update(trans) = true; + nft_trans_obj_newobj(trans) = newobj; +- list_add_tail(&trans->list, &ctx->net->nft.commit_list); ++ nft_trans_commit_list_add_tail(ctx->net, trans); + + return 0; + +@@ -5382,6 +5427,7 @@ static int nf_tables_dump_obj(struct sk_ + struct nft_obj_filter *filter = cb->data; + struct net *net = sock_net(skb->sk); + int family = nfmsg->nfgen_family; ++ struct nftables_pernet *nft_net; + struct nft_object *obj; + bool reset = false; + +@@ -5389,9 +5435,10 @@ static int nf_tables_dump_obj(struct sk_ + reset = true; + + rcu_read_lock(); +- cb->seq = net->nft.base_seq; ++ nft_net = net_generic(net, nf_tables_net_id); ++ cb->seq = nft_net->base_seq; + +- list_for_each_entry_rcu(table, &net->nft.tables, list) { ++ list_for_each_entry_rcu(table, &nft_net->tables, list) { + if (family != NFPROTO_UNSPEC && family != table->family) + continue; + +@@ -6071,13 +6118,15 @@ static int nf_tables_dump_flowtable(stru + unsigned int idx = 0, s_idx = cb->args[0]; + struct net *net = sock_net(skb->sk); + int family = nfmsg->nfgen_family; ++ struct nftables_pernet *nft_net; + struct nft_flowtable *flowtable; + const struct nft_table *table; + + rcu_read_lock(); +- cb->seq = net->nft.base_seq; ++ nft_net = net_generic(net, nf_tables_net_id); ++ cb->seq = nft_net->base_seq; + +- list_for_each_entry_rcu(table, &net->nft.tables, list) { ++ list_for_each_entry_rcu(table, &nft_net->tables, list) { + if (family != NFPROTO_UNSPEC && family != table->family) + continue; + +@@ -6247,6 +6296,7 @@ static void nf_tables_flowtable_destroy( + static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net, + u32 portid, u32 seq) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + struct nlmsghdr *nlh; + char buf[TASK_COMM_LEN]; + int event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWGEN); +@@ -6256,7 +6306,7 @@ static int nf_tables_fill_gen_info(struc + if (!nlh) + goto nla_put_failure; + +- if (nla_put_be32(skb, NFTA_GEN_ID, htonl(net->nft.base_seq)) || ++ if (nla_put_be32(skb, NFTA_GEN_ID, htonl(nft_net->base_seq)) || + nla_put_be32(skb, NFTA_GEN_PROC_PID, htonl(task_pid_nr(current))) || + nla_put_string(skb, NFTA_GEN_PROC_NAME, get_task_comm(buf, current))) + goto nla_put_failure; +@@ -6289,6 +6339,7 @@ static int nf_tables_flowtable_event(str + { + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + struct nft_flowtable *flowtable; ++ struct nftables_pernet *nft_net; + struct nft_table *table; + struct net *net; + +@@ -6296,13 +6347,14 @@ static int nf_tables_flowtable_event(str + return 0; + + net = dev_net(dev); +- mutex_lock(&net->nft.commit_mutex); +- list_for_each_entry(table, &net->nft.tables, list) { ++ nft_net = net_generic(net, nf_tables_net_id); ++ mutex_lock(&nft_net->commit_mutex); ++ list_for_each_entry(table, &nft_net->tables, list) { + list_for_each_entry(flowtable, &table->flowtables, list) { + nft_flowtable_event(event, dev, flowtable); + } + } +- mutex_unlock(&net->nft.commit_mutex); ++ mutex_unlock(&nft_net->commit_mutex); + + return NOTIFY_DONE; + } +@@ -6483,16 +6535,17 @@ static const struct nfnl_callback nf_tab + + static int nf_tables_validate(struct net *net) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + struct nft_table *table; + +- switch (net->nft.validate_state) { ++ switch (nft_net->validate_state) { + case NFT_VALIDATE_SKIP: + break; + case NFT_VALIDATE_NEED: + nft_validate_state_update(net, NFT_VALIDATE_DO); + /* fall through */ + case NFT_VALIDATE_DO: +- list_for_each_entry(table, &net->nft.tables, list) { ++ list_for_each_entry(table, &nft_net->tables, list) { + if (nft_table_validate(net, table) < 0) + return -EAGAIN; + } +@@ -6666,9 +6719,10 @@ static int nf_tables_commit_chain_prepar + + static void nf_tables_commit_chain_prepare_cancel(struct net *net) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + struct nft_trans *trans, *next; + +- list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { ++ list_for_each_entry_safe(trans, next, &nft_net->commit_list, list) { + struct nft_chain *chain = trans->ctx.chain; + + if (trans->msg_type == NFT_MSG_NEWRULE || +@@ -6766,10 +6820,11 @@ static void nft_chain_del(struct nft_cha + + static void nf_tables_module_autoload_cleanup(struct net *net) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + struct nft_module_request *req, *next; + +- WARN_ON_ONCE(!list_empty(&net->nft.commit_list)); +- list_for_each_entry_safe(req, next, &net->nft.module_list, list) { ++ WARN_ON_ONCE(!list_empty(&nft_net->commit_list)); ++ list_for_each_entry_safe(req, next, &nft_net->module_list, list) { + WARN_ON_ONCE(!req->done); + list_del(&req->list); + kfree(req); +@@ -6778,6 +6833,7 @@ static void nf_tables_module_autoload_cl + + static void nf_tables_commit_release(struct net *net) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + struct nft_trans *trans; + + /* all side effects have to be made visible. +@@ -6787,38 +6843,39 @@ static void nf_tables_commit_release(str + * Memory reclaim happens asynchronously from work queue + * to prevent expensive synchronize_rcu() in commit phase. + */ +- if (list_empty(&net->nft.commit_list)) { ++ if (list_empty(&nft_net->commit_list)) { + nf_tables_module_autoload_cleanup(net); +- mutex_unlock(&net->nft.commit_mutex); ++ mutex_unlock(&nft_net->commit_mutex); + return; + } + +- trans = list_last_entry(&net->nft.commit_list, ++ trans = list_last_entry(&nft_net->commit_list, + struct nft_trans, list); + get_net(trans->ctx.net); + WARN_ON_ONCE(trans->put_net); + + trans->put_net = true; + spin_lock(&nf_tables_destroy_list_lock); +- list_splice_tail_init(&net->nft.commit_list, &nf_tables_destroy_list); ++ list_splice_tail_init(&nft_net->commit_list, &nf_tables_destroy_list); + spin_unlock(&nf_tables_destroy_list_lock); + + nf_tables_module_autoload_cleanup(net); + schedule_work(&trans_destroy_work); + +- mutex_unlock(&net->nft.commit_mutex); ++ mutex_unlock(&nft_net->commit_mutex); + } + + static int nf_tables_commit(struct net *net, struct sk_buff *skb) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + struct nft_trans *trans, *next; + struct nft_trans_elem *te; + struct nft_chain *chain; + struct nft_table *table; + int err; + +- if (list_empty(&net->nft.commit_list)) { +- mutex_unlock(&net->nft.commit_mutex); ++ if (list_empty(&nft_net->commit_list)) { ++ mutex_unlock(&nft_net->commit_mutex); + return 0; + } + +@@ -6831,7 +6888,7 @@ static int nf_tables_commit(struct net * + return err; + + /* 1. Allocate space for next generation rules_gen_X[] */ +- list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { ++ list_for_each_entry_safe(trans, next, &nft_net->commit_list, list) { + int ret; + + if (trans->msg_type == NFT_MSG_NEWRULE || +@@ -6847,7 +6904,7 @@ static int nf_tables_commit(struct net * + } + + /* step 2. Make rules_gen_X visible to packet path */ +- list_for_each_entry(table, &net->nft.tables, list) { ++ list_for_each_entry(table, &nft_net->tables, list) { + list_for_each_entry(chain, &table->chains, list) + nf_tables_commit_chain(net, chain); + } +@@ -6856,12 +6913,13 @@ static int nf_tables_commit(struct net * + * Bump generation counter, invalidate any dump in progress. + * Cannot fail after this point. + */ +- while (++net->nft.base_seq == 0); ++ while (++nft_net->base_seq == 0) ++ ; + + /* step 3. Start new generation, rules_gen_X now in use. */ + net->nft.gencursor = nft_gencursor_next(net); + +- list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { ++ list_for_each_entry_safe(trans, next, &nft_net->commit_list, list) { + switch (trans->msg_type) { + case NFT_MSG_NEWTABLE: + if (nft_trans_table_update(trans)) { +@@ -7003,17 +7061,18 @@ static int nf_tables_commit(struct net * + + static void nf_tables_module_autoload(struct net *net) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + struct nft_module_request *req, *next; + LIST_HEAD(module_list); + +- list_splice_init(&net->nft.module_list, &module_list); +- mutex_unlock(&net->nft.commit_mutex); ++ list_splice_init(&nft_net->module_list, &module_list); ++ mutex_unlock(&nft_net->commit_mutex); + list_for_each_entry_safe(req, next, &module_list, list) { + request_module("%s", req->module); + req->done = true; + } +- mutex_lock(&net->nft.commit_mutex); +- list_splice(&module_list, &net->nft.module_list); ++ mutex_lock(&nft_net->commit_mutex); ++ list_splice(&module_list, &nft_net->module_list); + } + + static void nf_tables_abort_release(struct nft_trans *trans) +@@ -7047,6 +7106,7 @@ static void nf_tables_abort_release(stru + + static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + struct nft_trans *trans, *next; + struct nft_trans_elem *te; + +@@ -7054,7 +7114,7 @@ static int __nf_tables_abort(struct net + nf_tables_validate(net) < 0) + return -EAGAIN; + +- list_for_each_entry_safe_reverse(trans, next, &net->nft.commit_list, ++ list_for_each_entry_safe_reverse(trans, next, &nft_net->commit_list, + list) { + switch (trans->msg_type) { + case NFT_MSG_NEWTABLE: +@@ -7166,7 +7226,7 @@ static int __nf_tables_abort(struct net + synchronize_rcu(); + + list_for_each_entry_safe_reverse(trans, next, +- &net->nft.commit_list, list) { ++ &nft_net->commit_list, list) { + list_del(&trans->list); + nf_tables_abort_release(trans); + } +@@ -7182,22 +7242,24 @@ static int __nf_tables_abort(struct net + static int nf_tables_abort(struct net *net, struct sk_buff *skb, + enum nfnl_abort_action action) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + int ret = __nf_tables_abort(net, action); + +- mutex_unlock(&net->nft.commit_mutex); ++ mutex_unlock(&nft_net->commit_mutex); + + return ret; + } + + static bool nf_tables_valid_genid(struct net *net, u32 genid) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + bool genid_ok; + +- mutex_lock(&net->nft.commit_mutex); ++ mutex_lock(&nft_net->commit_mutex); + +- genid_ok = genid == 0 || net->nft.base_seq == genid; ++ genid_ok = genid == 0 || nft_net->base_seq == genid; + if (!genid_ok) +- mutex_unlock(&net->nft.commit_mutex); ++ mutex_unlock(&nft_net->commit_mutex); + + /* else, commit mutex has to be released by commit or abort function */ + return genid_ok; +@@ -7754,19 +7816,19 @@ EXPORT_SYMBOL_GPL(__nft_release_basechai + + static void __nft_release_hooks(struct net *net) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + struct nft_table *table; + struct nft_chain *chain; + +- list_for_each_entry(table, &net->nft.tables, list) { ++ list_for_each_entry(table, &nft_net->tables, list) { + list_for_each_entry(chain, &table->chains, list) + nf_tables_unregister_hook(net, table, chain); + } + } + +-static void __nft_release_tables(struct net *net) ++static void __nft_release_table(struct net *net, struct nft_table *table) + { + struct nft_flowtable *flowtable, *nf; +- struct nft_table *table, *nt; + struct nft_chain *chain, *nc; + struct nft_object *obj, *ne; + struct nft_rule *rule, *nr; +@@ -7776,77 +7838,93 @@ static void __nft_release_tables(struct + .family = NFPROTO_NETDEV, + }; + +- list_for_each_entry_safe(table, nt, &net->nft.tables, list) { +- ctx.family = table->family; +- ctx.table = table; +- list_for_each_entry(chain, &table->chains, list) { +- ctx.chain = chain; +- list_for_each_entry_safe(rule, nr, &chain->rules, list) { +- list_del(&rule->list); +- chain->use--; +- nf_tables_rule_release(&ctx, rule); +- } +- } +- list_for_each_entry_safe(flowtable, nf, &table->flowtables, list) { +- list_del(&flowtable->list); +- table->use--; +- nf_tables_flowtable_destroy(flowtable); +- } +- list_for_each_entry_safe(set, ns, &table->sets, list) { +- list_del(&set->list); +- table->use--; +- nft_set_destroy(set); +- } +- list_for_each_entry_safe(obj, ne, &table->objects, list) { +- nft_obj_del(obj); +- table->use--; +- nft_obj_destroy(&ctx, obj); +- } +- list_for_each_entry_safe(chain, nc, &table->chains, list) { +- ctx.chain = chain; +- nft_chain_del(chain); +- table->use--; +- nf_tables_chain_destroy(&ctx); ++ ctx.family = table->family; ++ ctx.table = table; ++ list_for_each_entry(chain, &table->chains, list) { ++ ctx.chain = chain; ++ list_for_each_entry_safe(rule, nr, &chain->rules, list) { ++ list_del(&rule->list); ++ chain->use--; ++ nf_tables_rule_release(&ctx, rule); + } +- list_del(&table->list); +- nf_tables_table_destroy(&ctx); + } ++ list_for_each_entry_safe(flowtable, nf, &table->flowtables, list) { ++ list_del(&flowtable->list); ++ table->use--; ++ nf_tables_flowtable_destroy(flowtable); ++ } ++ list_for_each_entry_safe(set, ns, &table->sets, list) { ++ list_del(&set->list); ++ table->use--; ++ nft_set_destroy(set); ++ } ++ list_for_each_entry_safe(obj, ne, &table->objects, list) { ++ nft_obj_del(obj); ++ table->use--; ++ nft_obj_destroy(&ctx, obj); ++ } ++ list_for_each_entry_safe(chain, nc, &table->chains, list) { ++ ctx.chain = chain; ++ nft_chain_del(chain); ++ table->use--; ++ nf_tables_chain_destroy(&ctx); ++ } ++ list_del(&table->list); ++ nf_tables_table_destroy(&ctx); ++} ++ ++static void __nft_release_tables(struct net *net) ++{ ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); ++ struct nft_table *table, *nt; ++ ++ list_for_each_entry_safe(table, nt, &nft_net->tables, list) ++ __nft_release_table(net, table); + } + + static int __net_init nf_tables_init_net(struct net *net) + { +- INIT_LIST_HEAD(&net->nft.tables); +- INIT_LIST_HEAD(&net->nft.commit_list); +- INIT_LIST_HEAD(&net->nft.module_list); +- mutex_init(&net->nft.commit_mutex); +- net->nft.base_seq = 1; +- net->nft.validate_state = NFT_VALIDATE_SKIP; ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); ++ ++ INIT_LIST_HEAD(&nft_net->tables); ++ INIT_LIST_HEAD(&nft_net->commit_list); ++ INIT_LIST_HEAD(&nft_net->module_list); ++ INIT_LIST_HEAD(&nft_net->notify_list); ++ mutex_init(&nft_net->commit_mutex); ++ nft_net->base_seq = 1; ++ nft_net->validate_state = NFT_VALIDATE_SKIP; + + return 0; + } + + static void __net_exit nf_tables_pre_exit_net(struct net *net) + { +- mutex_lock(&net->nft.commit_mutex); ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); ++ ++ mutex_lock(&nft_net->commit_mutex); + __nft_release_hooks(net); +- mutex_unlock(&net->nft.commit_mutex); ++ mutex_unlock(&nft_net->commit_mutex); + } + + static void __net_exit nf_tables_exit_net(struct net *net) + { +- mutex_lock(&net->nft.commit_mutex); +- if (!list_empty(&net->nft.commit_list)) ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); ++ ++ mutex_lock(&nft_net->commit_mutex); ++ if (!list_empty(&nft_net->commit_list)) + __nf_tables_abort(net, NFNL_ABORT_NONE); + __nft_release_tables(net); +- mutex_unlock(&net->nft.commit_mutex); +- WARN_ON_ONCE(!list_empty(&net->nft.tables)); +- WARN_ON_ONCE(!list_empty(&net->nft.module_list)); ++ mutex_unlock(&nft_net->commit_mutex); ++ WARN_ON_ONCE(!list_empty(&nft_net->tables)); ++ WARN_ON_ONCE(!list_empty(&nft_net->module_list)); + } + + static struct pernet_operations nf_tables_net_ops = { + .init = nf_tables_init_net, + .pre_exit = nf_tables_pre_exit_net, + .exit = nf_tables_exit_net, ++ .id = &nf_tables_net_id, ++ .size = sizeof(struct nftables_pernet), + }; + + static int __init nf_tables_module_init(void) +--- a/net/netfilter/nf_tables_offload.c ++++ b/net/netfilter/nf_tables_offload.c +@@ -7,6 +7,8 @@ + #include + #include + ++extern unsigned int nf_tables_net_id; ++ + static struct nft_flow_rule *nft_flow_rule_alloc(int num_actions) + { + struct nft_flow_rule *flow; +@@ -345,11 +347,12 @@ static int nft_flow_offload_chain(struct + + int nft_flow_rule_offload_commit(struct net *net) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + struct nft_trans *trans; + int err = 0; + u8 policy; + +- list_for_each_entry(trans, &net->nft.commit_list, list) { ++ list_for_each_entry(trans, &nft_net->commit_list, list) { + if (trans->ctx.family != NFPROTO_NETDEV) + continue; + +@@ -400,7 +403,7 @@ int nft_flow_rule_offload_commit(struct + break; + } + +- list_for_each_entry(trans, &net->nft.commit_list, list) { ++ list_for_each_entry(trans, &nft_net->commit_list, list) { + if (trans->ctx.family != NFPROTO_NETDEV) + continue; + +@@ -419,14 +422,14 @@ int nft_flow_rule_offload_commit(struct + return err; + } + +-static struct nft_chain *__nft_offload_get_chain(struct net_device *dev) ++static struct nft_chain *__nft_offload_get_chain(const struct nftables_pernet *nft_net, ++ struct net_device *dev) + { + struct nft_base_chain *basechain; +- struct net *net = dev_net(dev); + const struct nft_table *table; + struct nft_chain *chain; + +- list_for_each_entry(table, &net->nft.tables, list) { ++ list_for_each_entry(table, &nft_net->tables, list) { + if (table->family != NFPROTO_NETDEV) + continue; + +@@ -450,18 +453,20 @@ static void nft_indr_block_cb(struct net + flow_indr_block_bind_cb_t *cb, void *cb_priv, + enum flow_block_command cmd) + { ++ struct nftables_pernet *nft_net; + struct net *net = dev_net(dev); + struct nft_chain *chain; + +- mutex_lock(&net->nft.commit_mutex); +- chain = __nft_offload_get_chain(dev); ++ nft_net = net_generic(net, nf_tables_net_id); ++ mutex_lock(&nft_net->commit_mutex); ++ chain = __nft_offload_get_chain(nft_net, dev); + if (chain && chain->flags & NFT_CHAIN_HW_OFFLOAD) { + struct nft_base_chain *basechain; + + basechain = nft_base_chain(chain); + nft_indr_block_ing_cmd(dev, basechain, cb, cb_priv, cmd); + } +- mutex_unlock(&net->nft.commit_mutex); ++ mutex_unlock(&nft_net->commit_mutex); + } + + static void nft_offload_chain_clean(struct nft_chain *chain) +@@ -480,17 +485,19 @@ static int nft_offload_netdev_event(stru + unsigned long event, void *ptr) + { + struct net_device *dev = netdev_notifier_info_to_dev(ptr); ++ struct nftables_pernet *nft_net; + struct net *net = dev_net(dev); + struct nft_chain *chain; + + if (event != NETDEV_UNREGISTER) + return NOTIFY_DONE; + +- mutex_lock(&net->nft.commit_mutex); +- chain = __nft_offload_get_chain(dev); ++ nft_net = net_generic(net, nf_tables_net_id); ++ mutex_lock(&nft_net->commit_mutex); ++ chain = __nft_offload_get_chain(nft_net, dev); + if (chain) + nft_offload_chain_clean(chain); +- mutex_unlock(&net->nft.commit_mutex); ++ mutex_unlock(&nft_net->commit_mutex); + + return NOTIFY_DONE; + } +--- a/net/netfilter/nft_chain_filter.c ++++ b/net/netfilter/nft_chain_filter.c +@@ -2,6 +2,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -10,6 +11,8 @@ + #include + #include + ++extern unsigned int nf_tables_net_id; ++ + #ifdef CONFIG_NF_TABLES_IPV4 + static unsigned int nft_do_chain_ipv4(void *priv, + struct sk_buff *skb, +@@ -315,6 +318,7 @@ static int nf_tables_netdev_event(struct + unsigned long event, void *ptr) + { + struct net_device *dev = netdev_notifier_info_to_dev(ptr); ++ struct nftables_pernet *nft_net; + struct nft_table *table; + struct nft_chain *chain, *nr; + struct nft_ctx ctx = { +@@ -325,8 +329,9 @@ static int nf_tables_netdev_event(struct + event != NETDEV_CHANGENAME) + return NOTIFY_DONE; + +- mutex_lock(&ctx.net->nft.commit_mutex); +- list_for_each_entry(table, &ctx.net->nft.tables, list) { ++ nft_net = net_generic(ctx.net, nf_tables_net_id); ++ mutex_lock(&nft_net->commit_mutex); ++ list_for_each_entry(table, &nft_net->tables, list) { + if (table->family != NFPROTO_NETDEV) + continue; + +@@ -340,7 +345,7 @@ static int nf_tables_netdev_event(struct + nft_netdev_event(event, dev, &ctx); + } + } +- mutex_unlock(&ctx.net->nft.commit_mutex); ++ mutex_unlock(&nft_net->commit_mutex); + + return NOTIFY_DONE; + } +--- a/net/netfilter/nft_dynset.c ++++ b/net/netfilter/nft_dynset.c +@@ -11,6 +11,9 @@ + #include + #include + #include ++#include ++ ++extern unsigned int nf_tables_net_id; + + struct nft_dynset { + struct nft_set *set; +@@ -129,13 +132,14 @@ static int nft_dynset_init(const struct + const struct nft_expr *expr, + const struct nlattr * const tb[]) + { ++ struct nftables_pernet *nft_net = net_generic(ctx->net, nf_tables_net_id); + struct nft_dynset *priv = nft_expr_priv(expr); + u8 genmask = nft_genmask_next(ctx->net); + struct nft_set *set; + u64 timeout; + int err; + +- lockdep_assert_held(&ctx->net->nft.commit_mutex); ++ lockdep_assert_held(&nft_net->commit_mutex); + + if (tb[NFTA_DYNSET_SET_NAME] == NULL || + tb[NFTA_DYNSET_OP] == NULL || diff --git a/queue-5.4/netfilter-nftables-add-helper-function-to-set-the-base-sequence-number.patch b/queue-5.4/netfilter-nftables-add-helper-function-to-set-the-base-sequence-number.patch new file mode 100644 index 00000000000..deec0868261 --- /dev/null +++ b/queue-5.4/netfilter-nftables-add-helper-function-to-set-the-base-sequence-number.patch @@ -0,0 +1,117 @@ +From stable-owner@vger.kernel.org Wed Jul 5 18:54:50 2023 +From: Pablo Neira Ayuso +Date: Wed, 5 Jul 2023 18:54:15 +0200 +Subject: netfilter: nftables: add helper function to set the base sequence number +To: netfilter-devel@vger.kernel.org +Cc: sashal@kernel.org, gregkh@linuxfoundation.org, stable@vger.kernel.org +Message-ID: <20230705165423.50054-3-pablo@netfilter.org> + +From: Pablo Neira Ayuso + +[ 802b805162a1b7d8391c40ac8a878e9e63287aff ] + +This patch adds a helper function to calculate the base sequence number +field that is stored in the nfnetlink header. Use the helper function +whenever possible. + +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman +--- + net/netfilter/nf_tables_api.c | 23 ++++++++++++++--------- + 1 file changed, 14 insertions(+), 9 deletions(-) + +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -588,6 +588,11 @@ nf_tables_chain_type_lookup(struct net * + return ERR_PTR(-ENOENT); + } + ++static __be16 nft_base_seq(const struct net *net) ++{ ++ return htons(net->nft.base_seq & 0xffff); ++} ++ + static const struct nla_policy nft_table_policy[NFTA_TABLE_MAX + 1] = { + [NFTA_TABLE_NAME] = { .type = NLA_STRING, + .len = NFT_TABLE_MAXNAMELEN - 1 }, +@@ -610,7 +615,7 @@ static int nf_tables_fill_table_info(str + nfmsg = nlmsg_data(nlh); + nfmsg->nfgen_family = family; + nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = htons(net->nft.base_seq & 0xffff); ++ nfmsg->res_id = nft_base_seq(net); + + if (nla_put_string(skb, NFTA_TABLE_NAME, table->name) || + nla_put_be32(skb, NFTA_TABLE_FLAGS, htonl(table->flags)) || +@@ -1274,7 +1279,7 @@ static int nf_tables_fill_chain_info(str + nfmsg = nlmsg_data(nlh); + nfmsg->nfgen_family = family; + nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = htons(net->nft.base_seq & 0xffff); ++ nfmsg->res_id = nft_base_seq(net); + + if (nla_put_string(skb, NFTA_CHAIN_TABLE, table->name)) + goto nla_put_failure; +@@ -2366,7 +2371,7 @@ static int nf_tables_fill_rule_info(stru + nfmsg = nlmsg_data(nlh); + nfmsg->nfgen_family = family; + nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = htons(net->nft.base_seq & 0xffff); ++ nfmsg->res_id = nft_base_seq(net); + + if (nla_put_string(skb, NFTA_RULE_TABLE, table->name)) + goto nla_put_failure; +@@ -3325,7 +3330,7 @@ static int nf_tables_fill_set(struct sk_ + nfmsg = nlmsg_data(nlh); + nfmsg->nfgen_family = ctx->family; + nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = htons(ctx->net->nft.base_seq & 0xffff); ++ nfmsg->res_id = nft_base_seq(ctx->net); + + if (nla_put_string(skb, NFTA_SET_TABLE, ctx->table->name)) + goto nla_put_failure; +@@ -4180,7 +4185,7 @@ static int nf_tables_dump_set(struct sk_ + nfmsg = nlmsg_data(nlh); + nfmsg->nfgen_family = table->family; + nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = htons(net->nft.base_seq & 0xffff); ++ nfmsg->res_id = nft_base_seq(net); + + if (nla_put_string(skb, NFTA_SET_ELEM_LIST_TABLE, table->name)) + goto nla_put_failure; +@@ -4252,7 +4257,7 @@ static int nf_tables_fill_setelem_info(s + nfmsg = nlmsg_data(nlh); + nfmsg->nfgen_family = ctx->family; + nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = htons(ctx->net->nft.base_seq & 0xffff); ++ nfmsg->res_id = nft_base_seq(ctx->net); + + if (nla_put_string(skb, NFTA_SET_TABLE, ctx->table->name)) + goto nla_put_failure; +@@ -5383,7 +5388,7 @@ static int nf_tables_fill_obj_info(struc + nfmsg = nlmsg_data(nlh); + nfmsg->nfgen_family = family; + nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = htons(net->nft.base_seq & 0xffff); ++ nfmsg->res_id = nft_base_seq(net); + + if (nla_put_string(skb, NFTA_OBJ_TABLE, table->name) || + nla_put_string(skb, NFTA_OBJ_NAME, obj->key.name) || +@@ -6059,7 +6064,7 @@ static int nf_tables_fill_flowtable_info + nfmsg = nlmsg_data(nlh); + nfmsg->nfgen_family = family; + nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = htons(net->nft.base_seq & 0xffff); ++ nfmsg->res_id = nft_base_seq(net); + + if (nla_put_string(skb, NFTA_FLOWTABLE_TABLE, flowtable->table->name) || + nla_put_string(skb, NFTA_FLOWTABLE_NAME, flowtable->name) || +@@ -6297,7 +6302,7 @@ static int nf_tables_fill_gen_info(struc + nfmsg = nlmsg_data(nlh); + nfmsg->nfgen_family = AF_UNSPEC; + nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = htons(net->nft.base_seq & 0xffff); ++ nfmsg->res_id = nft_base_seq(net); + + if (nla_put_be32(skb, NFTA_GEN_ID, htonl(net->nft.base_seq)) || + nla_put_be32(skb, NFTA_GEN_PROC_PID, htonl(task_pid_nr(current))) || diff --git a/queue-5.4/series b/queue-5.4/series index 1ed16398317..f5618b2ba84 100644 --- a/queue-5.4/series +++ b/queue-5.4/series @@ -178,3 +178,13 @@ arm-orion5x-fix-d2net-gpio-initialization.patch fs-no-need-to-check-source.patch fanotify-disallow-mount-sb-marks-on-kernel-internal-pseudo-fs.patch block-add-overflow-checks-for-amiga-partition-support.patch +netfilter-nf_tables-fix-nat-hook-table-deletion.patch +netfilter-nftables-add-helper-function-to-set-the-base-sequence-number.patch +netfilter-add-helper-function-to-set-up-the-nfnetlink-header-and-use-it.patch +netfilter-nf_tables-use-net_generic-infra-for-transaction-data.patch +netfilter-nf_tables-add-rescheduling-points-during-loop-detection-walks.patch +netfilter-nf_tables-incorrect-error-path-handling-with-nft_msg_newrule.patch +netfilter-nf_tables-add-nft_trans_prepare_error-to-deal-with-bound-set-chain.patch +netfilter-nf_tables-reject-unbound-anonymous-set-before-commit-phase.patch +netfilter-nf_tables-unbind-non-anonymous-set-if-rule-construction-fails.patch +netfilter-nf_tables-fix-scheduling-while-atomic-splat.patch -- 2.47.3