Commit 1b54f4fa authored by David S. Miller's avatar David S. Miller

Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf

Pablo Neira Ayuso says:

====================
Netfilter fixes for net

The following patchset contains Netfilter fixes for net:

1) Fix gcc-10 compilation warning in nf_conntrack, from Arnd Bergmann.

2) Add NF_FLOW_HW_PENDING to avoid races between stats and deletion
   commands, from Paul Blakey.

3) Remove WQ_MEM_RECLAIM from the offload workqueue, from Roi Dayan.

4) Infinite loop when removing nf_conntrack module, from Florian Westphal.

5) Set NF_FLOW_TEARDOWN bit on expiration to avoid races when refreshing
   the timeout from the software path.

6) Missing nft_set_elem_expired() check in the rbtree, from Phil Sutter.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents c9e2053d 340eaff6
...@@ -87,7 +87,7 @@ struct nf_conn { ...@@ -87,7 +87,7 @@ struct nf_conn {
struct hlist_node nat_bysource; struct hlist_node nat_bysource;
#endif #endif
/* all members below initialized via memset */ /* all members below initialized via memset */
u8 __nfct_init_offset[0]; struct { } __nfct_init_offset;
/* If we were expected by an expectation, this will be it */ /* If we were expected by an expectation, this will be it */
struct nf_conn *master; struct nf_conn *master;
......
...@@ -127,6 +127,7 @@ enum nf_flow_flags { ...@@ -127,6 +127,7 @@ enum nf_flow_flags {
NF_FLOW_HW_DYING, NF_FLOW_HW_DYING,
NF_FLOW_HW_DEAD, NF_FLOW_HW_DEAD,
NF_FLOW_HW_REFRESH, NF_FLOW_HW_REFRESH,
NF_FLOW_HW_PENDING,
}; };
enum flow_offload_type { enum flow_offload_type {
......
...@@ -1519,9 +1519,9 @@ __nf_conntrack_alloc(struct net *net, ...@@ -1519,9 +1519,9 @@ __nf_conntrack_alloc(struct net *net,
ct->status = 0; ct->status = 0;
ct->timeout = 0; ct->timeout = 0;
write_pnet(&ct->ct_net, net); write_pnet(&ct->ct_net, net);
memset(&ct->__nfct_init_offset[0], 0, memset(&ct->__nfct_init_offset, 0,
offsetof(struct nf_conn, proto) - offsetof(struct nf_conn, proto) -
offsetof(struct nf_conn, __nfct_init_offset[0])); offsetof(struct nf_conn, __nfct_init_offset));
nf_ct_zone_add(ct, zone); nf_ct_zone_add(ct, zone);
...@@ -2139,8 +2139,19 @@ get_next_corpse(int (*iter)(struct nf_conn *i, void *data), ...@@ -2139,8 +2139,19 @@ get_next_corpse(int (*iter)(struct nf_conn *i, void *data),
nf_conntrack_lock(lockp); nf_conntrack_lock(lockp);
if (*bucket < nf_conntrack_htable_size) { if (*bucket < nf_conntrack_htable_size) {
hlist_nulls_for_each_entry(h, n, &nf_conntrack_hash[*bucket], hnnode) { hlist_nulls_for_each_entry(h, n, &nf_conntrack_hash[*bucket], hnnode) {
if (NF_CT_DIRECTION(h) != IP_CT_DIR_ORIGINAL) if (NF_CT_DIRECTION(h) != IP_CT_DIR_REPLY)
continue; continue;
/* All nf_conn objects are added to hash table twice, one
* for original direction tuple, once for the reply tuple.
*
* Exception: In the IPS_NAT_CLASH case, only the reply
* tuple is added (the original tuple already existed for
* a different object).
*
* We only need to call the iterator once for each
* conntrack, so we just use the 'reply' direction
* tuple while iterating.
*/
ct = nf_ct_tuplehash_to_ctrack(h); ct = nf_ct_tuplehash_to_ctrack(h);
if (iter(ct, data)) if (iter(ct, data))
goto found; goto found;
......
...@@ -284,7 +284,7 @@ static void flow_offload_del(struct nf_flowtable *flow_table, ...@@ -284,7 +284,7 @@ static void flow_offload_del(struct nf_flowtable *flow_table,
if (nf_flow_has_expired(flow)) if (nf_flow_has_expired(flow))
flow_offload_fixup_ct(flow->ct); flow_offload_fixup_ct(flow->ct);
else if (test_bit(NF_FLOW_TEARDOWN, &flow->flags)) else
flow_offload_fixup_ct_timeout(flow->ct); flow_offload_fixup_ct_timeout(flow->ct);
flow_offload_free(flow); flow_offload_free(flow);
...@@ -361,8 +361,10 @@ static void nf_flow_offload_gc_step(struct flow_offload *flow, void *data) ...@@ -361,8 +361,10 @@ static void nf_flow_offload_gc_step(struct flow_offload *flow, void *data)
{ {
struct nf_flowtable *flow_table = data; struct nf_flowtable *flow_table = data;
if (nf_flow_has_expired(flow) || nf_ct_is_dying(flow->ct) || if (nf_flow_has_expired(flow) || nf_ct_is_dying(flow->ct))
test_bit(NF_FLOW_TEARDOWN, &flow->flags)) { set_bit(NF_FLOW_TEARDOWN, &flow->flags);
if (test_bit(NF_FLOW_TEARDOWN, &flow->flags)) {
if (test_bit(NF_FLOW_HW, &flow->flags)) { if (test_bit(NF_FLOW_HW, &flow->flags)) {
if (!test_bit(NF_FLOW_HW_DYING, &flow->flags)) if (!test_bit(NF_FLOW_HW_DYING, &flow->flags))
nf_flow_offload_del(flow_table, flow); nf_flow_offload_del(flow_table, flow);
......
...@@ -817,6 +817,7 @@ static void flow_offload_work_handler(struct work_struct *work) ...@@ -817,6 +817,7 @@ static void flow_offload_work_handler(struct work_struct *work)
WARN_ON_ONCE(1); WARN_ON_ONCE(1);
} }
clear_bit(NF_FLOW_HW_PENDING, &offload->flow->flags);
kfree(offload); kfree(offload);
} }
...@@ -831,9 +832,14 @@ nf_flow_offload_work_alloc(struct nf_flowtable *flowtable, ...@@ -831,9 +832,14 @@ nf_flow_offload_work_alloc(struct nf_flowtable *flowtable,
{ {
struct flow_offload_work *offload; struct flow_offload_work *offload;
if (test_and_set_bit(NF_FLOW_HW_PENDING, &flow->flags))
return NULL;
offload = kmalloc(sizeof(struct flow_offload_work), GFP_ATOMIC); offload = kmalloc(sizeof(struct flow_offload_work), GFP_ATOMIC);
if (!offload) if (!offload) {
clear_bit(NF_FLOW_HW_PENDING, &flow->flags);
return NULL; return NULL;
}
offload->cmd = cmd; offload->cmd = cmd;
offload->flow = flow; offload->flow = flow;
...@@ -1056,7 +1062,7 @@ static struct flow_indr_block_entry block_ing_entry = { ...@@ -1056,7 +1062,7 @@ static struct flow_indr_block_entry block_ing_entry = {
int nf_flow_table_offload_init(void) int nf_flow_table_offload_init(void)
{ {
nf_flow_offload_wq = alloc_workqueue("nf_flow_table_offload", nf_flow_offload_wq = alloc_workqueue("nf_flow_table_offload",
WQ_UNBOUND | WQ_MEM_RECLAIM, 0); WQ_UNBOUND, 0);
if (!nf_flow_offload_wq) if (!nf_flow_offload_wq)
return -ENOMEM; return -ENOMEM;
......
...@@ -79,6 +79,10 @@ static bool __nft_rbtree_lookup(const struct net *net, const struct nft_set *set ...@@ -79,6 +79,10 @@ static bool __nft_rbtree_lookup(const struct net *net, const struct nft_set *set
parent = rcu_dereference_raw(parent->rb_left); parent = rcu_dereference_raw(parent->rb_left);
continue; continue;
} }
if (nft_set_elem_expired(&rbe->ext))
return false;
if (nft_rbtree_interval_end(rbe)) { if (nft_rbtree_interval_end(rbe)) {
if (nft_set_is_anonymous(set)) if (nft_set_is_anonymous(set))
return false; return false;
...@@ -94,6 +98,7 @@ static bool __nft_rbtree_lookup(const struct net *net, const struct nft_set *set ...@@ -94,6 +98,7 @@ static bool __nft_rbtree_lookup(const struct net *net, const struct nft_set *set
if (set->flags & NFT_SET_INTERVAL && interval != NULL && if (set->flags & NFT_SET_INTERVAL && interval != NULL &&
nft_set_elem_active(&interval->ext, genmask) && nft_set_elem_active(&interval->ext, genmask) &&
!nft_set_elem_expired(&interval->ext) &&
nft_rbtree_interval_start(interval)) { nft_rbtree_interval_start(interval)) {
*ext = &interval->ext; *ext = &interval->ext;
return true; return true;
...@@ -154,6 +159,9 @@ static bool __nft_rbtree_get(const struct net *net, const struct nft_set *set, ...@@ -154,6 +159,9 @@ static bool __nft_rbtree_get(const struct net *net, const struct nft_set *set,
continue; continue;
} }
if (nft_set_elem_expired(&rbe->ext))
return false;
if (!nft_set_ext_exists(&rbe->ext, NFT_SET_EXT_FLAGS) || if (!nft_set_ext_exists(&rbe->ext, NFT_SET_EXT_FLAGS) ||
(*nft_set_ext_flags(&rbe->ext) & NFT_SET_ELEM_INTERVAL_END) == (*nft_set_ext_flags(&rbe->ext) & NFT_SET_ELEM_INTERVAL_END) ==
(flags & NFT_SET_ELEM_INTERVAL_END)) { (flags & NFT_SET_ELEM_INTERVAL_END)) {
...@@ -170,6 +178,7 @@ static bool __nft_rbtree_get(const struct net *net, const struct nft_set *set, ...@@ -170,6 +178,7 @@ static bool __nft_rbtree_get(const struct net *net, const struct nft_set *set,
if (set->flags & NFT_SET_INTERVAL && interval != NULL && if (set->flags & NFT_SET_INTERVAL && interval != NULL &&
nft_set_elem_active(&interval->ext, genmask) && nft_set_elem_active(&interval->ext, genmask) &&
!nft_set_elem_expired(&interval->ext) &&
((!nft_rbtree_interval_end(interval) && ((!nft_rbtree_interval_end(interval) &&
!(flags & NFT_SET_ELEM_INTERVAL_END)) || !(flags & NFT_SET_ELEM_INTERVAL_END)) ||
(nft_rbtree_interval_end(interval) && (nft_rbtree_interval_end(interval) &&
...@@ -418,6 +427,8 @@ static void nft_rbtree_walk(const struct nft_ctx *ctx, ...@@ -418,6 +427,8 @@ static void nft_rbtree_walk(const struct nft_ctx *ctx,
if (iter->count < iter->skip) if (iter->count < iter->skip)
goto cont; goto cont;
if (nft_set_elem_expired(&rbe->ext))
goto cont;
if (!nft_set_elem_active(&rbe->ext, iter->genmask)) if (!nft_set_elem_active(&rbe->ext, iter->genmask))
goto cont; goto cont;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment