Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions include/net/netfilter/nf_tables.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,9 @@ static inline u16 nft_reg_load16(const u32 *sreg)
return *(u16 *)sreg;
}

static inline void nft_reg_store64(u32 *dreg, u64 val)
static inline void nft_reg_store64(u64 *dreg, u64 val)
{
put_unaligned(val, (u64 *)dreg);
put_unaligned(val, dreg);
}

static inline u64 nft_reg_load64(const u32 *sreg)
Expand Down Expand Up @@ -665,15 +665,18 @@ struct nft_set_ext_tmpl {
/**
* struct nft_set_ext - set extensions
*
* @genmask: generation mask
* @genmask: generation mask, but also flags (see NFT_SET_ELEM_DEAD_BIT)
* @offset: offsets of individual extension types
* @data: beginning of extension data
*
* This structure must be aligned to word size, otherwise atomic bitops
* on genmask field can cause alignment failure on some archs.
*/
struct nft_set_ext {
u8 genmask;
u8 offset[NFT_SET_EXT_NUM];
char data[];
};
} __aligned(BITS_PER_LONG / 8);

static inline void nft_set_ext_prepare(struct nft_set_ext_tmpl *tmpl)
{
Expand Down Expand Up @@ -1654,7 +1657,7 @@ void nft_chain_filter_fini(void);
void __init nft_chain_route_init(void);
void nft_chain_route_fini(void);

void nf_tables_trans_destroy_flush_work(void);
void nf_tables_trans_destroy_flush_work(struct net *net);

int nf_msecs_to_jiffies64(const struct nlattr *nla, u64 *result);
__be64 nf_jiffies64_to_msecs(u64 input);
Expand All @@ -1668,6 +1671,7 @@ static inline int nft_request_module(struct net *net, const char *fmt, ...) { re
struct nftables_pernet {
struct list_head tables;
struct list_head commit_list;
struct list_head destroy_list;
struct list_head binding_list;
struct list_head module_list;
struct list_head notify_list;
Expand All @@ -1676,6 +1680,7 @@ struct nftables_pernet {
unsigned int base_seq;
unsigned int gc_seq;
u8 validate_state;
struct work_struct destroy_work;
};

extern unsigned int nf_tables_net_id;
Expand Down
29 changes: 17 additions & 12 deletions net/netfilter/nf_tables_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ 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);
static LIST_HEAD(nf_tables_destroy_list);
static LIST_HEAD(nf_tables_gc_list);
static DEFINE_SPINLOCK(nf_tables_destroy_list_lock);
static DEFINE_SPINLOCK(nf_tables_gc_list_lock);
Expand Down Expand Up @@ -122,7 +121,6 @@ static void nft_validate_state_update(struct net *net, u8 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);

static void nft_trans_gc_work(struct work_struct *work);
static DECLARE_WORK(trans_gc_work, nft_trans_gc_work);
Expand Down Expand Up @@ -5379,7 +5377,7 @@ static int nf_tables_dump_setelem(const struct nft_ctx *ctx,
const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
struct nft_set_dump_args *args;

if (nft_set_elem_expired(ext))
if (nft_set_elem_expired(ext) || nft_set_elem_is_dead(ext))
return 0;

args = container_of(iter, struct nft_set_dump_args, iter);
Expand Down Expand Up @@ -8824,11 +8822,12 @@ static void nft_commit_release(struct nft_trans *trans)

static void nf_tables_trans_destroy_work(struct work_struct *w)
{
struct nftables_pernet *nft_net = container_of(w, struct nftables_pernet, destroy_work);
struct nft_trans *trans, *next;
LIST_HEAD(head);

spin_lock(&nf_tables_destroy_list_lock);
list_splice_init(&nf_tables_destroy_list, &head);
list_splice_init(&nft_net->destroy_list, &head);
spin_unlock(&nf_tables_destroy_list_lock);

if (list_empty(&head))
Expand All @@ -8842,9 +8841,11 @@ static void nf_tables_trans_destroy_work(struct work_struct *w)
}
}

void nf_tables_trans_destroy_flush_work(void)
void nf_tables_trans_destroy_flush_work(struct net *net)
{
flush_work(&trans_destroy_work);
struct nftables_pernet *nft_net = nft_pernet(net);

flush_work(&nft_net->destroy_work);
}
EXPORT_SYMBOL_GPL(nf_tables_trans_destroy_flush_work);

Expand Down Expand Up @@ -9263,11 +9264,11 @@ static void nf_tables_commit_release(struct net *net)

trans->put_net = true;
spin_lock(&nf_tables_destroy_list_lock);
list_splice_tail_init(&nft_net->commit_list, &nf_tables_destroy_list);
list_splice_tail_init(&nft_net->commit_list, &nft_net->destroy_list);
spin_unlock(&nf_tables_destroy_list_lock);

nf_tables_module_autoload_cleanup(net);
schedule_work(&trans_destroy_work);
schedule_work(&nft_net->destroy_work);

mutex_unlock(&nft_net->commit_mutex);
}
Expand Down Expand Up @@ -9376,7 +9377,7 @@ static void nft_set_commit_update(struct list_head *set_update_list)
list_for_each_entry_safe(set, next, set_update_list, pending_update) {
list_del_init(&set->pending_update);

if (!set->ops->commit)
if (!set->ops->commit || set->dead)
continue;

set->ops->commit(set);
Expand Down Expand Up @@ -9834,6 +9835,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
nft_trans_destroy(trans);
break;
}
nft_trans_set(trans)->dead = 1;
list_del_rcu(&nft_trans_set(trans)->list);
break;
case NFT_MSG_DELSET:
Expand Down Expand Up @@ -10676,8 +10678,7 @@ static int nft_rcv_nl_event(struct notifier_block *this, unsigned long event,

gc_seq = nft_gc_seq_begin(nft_net);

if (!list_empty(&nf_tables_destroy_list))
nf_tables_trans_destroy_flush_work();
nf_tables_trans_destroy_flush_work(net);
again:
list_for_each_entry(table, &nft_net->tables, list) {
if (nft_table_has_owner(table) &&
Expand Down Expand Up @@ -10715,13 +10716,15 @@ static int __net_init nf_tables_init_net(struct net *net)

INIT_LIST_HEAD(&nft_net->tables);
INIT_LIST_HEAD(&nft_net->commit_list);
INIT_LIST_HEAD(&nft_net->destroy_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);
nft_net->base_seq = 1;
nft_net->gc_seq = 0;
nft_net->validate_state = NFT_VALIDATE_SKIP;
INIT_WORK(&nft_net->destroy_work, nf_tables_trans_destroy_work);

return 0;
}
Expand Down Expand Up @@ -10749,14 +10752,17 @@ static void __net_exit nf_tables_exit_net(struct net *net)
if (!list_empty(&nft_net->module_list))
nf_tables_module_autoload_cleanup(net);

cancel_work_sync(&nft_net->destroy_work);
__nft_release_tables(net);

nft_gc_seq_end(nft_net, gc_seq);

mutex_unlock(&nft_net->commit_mutex);

WARN_ON_ONCE(!list_empty(&nft_net->tables));
WARN_ON_ONCE(!list_empty(&nft_net->module_list));
WARN_ON_ONCE(!list_empty(&nft_net->notify_list));
WARN_ON_ONCE(!list_empty(&nft_net->destroy_list));
}

static void nf_tables_exit_batch(struct list_head *net_exit_list)
Expand Down Expand Up @@ -10841,7 +10847,6 @@ static void __exit nf_tables_module_exit(void)
nft_chain_route_fini();
unregister_pernet_subsys(&nf_tables_net_ops);
cancel_work_sync(&trans_gc_work);
cancel_work_sync(&trans_destroy_work);
rcu_barrier();
rhltable_destroy(&nft_objname_ht);
nf_tables_core_module_exit();
Expand Down
6 changes: 4 additions & 2 deletions net/netfilter/nft_byteorder.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,22 @@ void nft_byteorder_eval(const struct nft_expr *expr,

switch (priv->size) {
case 8: {
u64 *dst64 = (void *)dst;
u64 src64;

switch (priv->op) {
case NFT_BYTEORDER_NTOH:
for (i = 0; i < priv->len / 8; i++) {
src64 = nft_reg_load64(&src[i]);
nft_reg_store64(&dst[i], be64_to_cpu(src64));
nft_reg_store64(&dst64[i],
be64_to_cpu((__force __be64)src64));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems that the 9.4 backport commit e7fce92 accidentally squashed in a change from a different upstream commit, d86473b ("netfilter: nf_tables: use the correct get/put helpers"). The addition of the (technically cosmetic) (__force __be64) cast shouldn't be part of the netfilter: nf_tables: fix pointer math issue in nft_byteorder_eval() backport.

}
break;
case NFT_BYTEORDER_HTON:
for (i = 0; i < priv->len / 8; i++) {
src64 = (__force __u64)
cpu_to_be64(nft_reg_load64(&src[i]));
nft_reg_store64(&dst[i], src64);
nft_reg_store64(&dst64[i], src64);
}
break;
}
Expand Down
8 changes: 4 additions & 4 deletions net/netfilter/nft_compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -223,15 +223,15 @@ static int nft_parse_compat(const struct nlattr *attr, u16 *proto, bool *inv)
return 0;
}

static void nft_compat_wait_for_destructors(void)
static void nft_compat_wait_for_destructors(struct net *net)
{
/* xtables matches or targets can have side effects, e.g.
* creation/destruction of /proc files.
* The xt ->destroy functions are run asynchronously from
* work queue. If we have pending invocations we thus
* need to wait for those to finish.
*/
nf_tables_trans_destroy_flush_work();
nf_tables_trans_destroy_flush_work(net);
}

static int
Expand All @@ -257,7 +257,7 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,

nft_target_set_tgchk_param(&par, ctx, target, info, &e, proto, inv);

nft_compat_wait_for_destructors();
nft_compat_wait_for_destructors(ctx->net);

ret = xt_check_target(&par, size, proto, inv);
if (ret < 0) {
Expand Down Expand Up @@ -494,7 +494,7 @@ __nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,

nft_match_set_mtchk_param(&par, ctx, match, info, &e, proto, inv);

nft_compat_wait_for_destructors();
nft_compat_wait_for_destructors(ctx->net);

return xt_check_match(&par, size, proto, inv);
}
Expand Down
2 changes: 1 addition & 1 deletion net/netfilter/nft_meta.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ nft_meta_get_eval_time(enum nft_meta_keys key,
{
switch (key) {
case NFT_META_TIME_NS:
nft_reg_store64(dest, ktime_get_real_ns());
nft_reg_store64((u64 *)dest, ktime_get_real_ns());
break;
case NFT_META_TIME_DAY:
nft_reg_store8(dest, nft_meta_weekday());
Expand Down
16 changes: 16 additions & 0 deletions net/netfilter/nft_set_hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@
struct nft_rhash {
struct rhashtable ht;
struct delayed_work gc_work;
u32 wq_gc_seq;
};

struct nft_rhash_elem {
struct nft_elem_priv priv;
struct rhash_head node;
u32 wq_gc_seq;
struct nft_set_ext ext;
};

Expand Down Expand Up @@ -337,6 +339,10 @@ static void nft_rhash_gc(struct work_struct *work)
if (!gc)
goto done;

/* Elements never collected use a zero gc worker sequence number. */
if (unlikely(++priv->wq_gc_seq == 0))
priv->wq_gc_seq++;

rhashtable_walk_enter(&priv->ht, &hti);
rhashtable_walk_start(&hti);

Expand All @@ -354,6 +360,14 @@ static void nft_rhash_gc(struct work_struct *work)
goto try_later;
}

/* rhashtable walk is unstable, already seen in this gc run?
* Then, skip this element. In case of (unlikely) sequence
* wraparound and stale element wq_gc_seq, next gc run will
* just find this expired element.
*/
if (he->wq_gc_seq == priv->wq_gc_seq)
continue;

if (nft_set_elem_is_dead(&he->ext))
goto dead_elem;

Expand All @@ -370,6 +384,8 @@ static void nft_rhash_gc(struct work_struct *work)
if (!gc)
goto try_later;

/* annotate gc sequence for this attempt. */
he->wq_gc_seq = priv->wq_gc_seq;
nft_trans_gc_elem_add(gc, he);
}

Expand Down
4 changes: 2 additions & 2 deletions net/netfilter/nft_set_pipapo.c
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,7 @@ static struct nft_elem_priv *
nft_pipapo_get(const struct net *net, const struct nft_set *set,
const struct nft_set_elem *elem, unsigned int flags)
{
static struct nft_pipapo_elem *e;
struct nft_pipapo_elem *e;

e = pipapo_get(net, set, (const u8 *)elem->key.val.data,
nft_genmask_cur(net));
Expand Down Expand Up @@ -1603,7 +1603,7 @@ static void pipapo_gc(const struct nft_set *_set, struct nft_pipapo_match *m)
if (nft_set_elem_expired(&e->ext)) {
priv->dirty = true;

gc = nft_trans_gc_queue_sync(gc, GFP_ATOMIC);
gc = nft_trans_gc_queue_sync(gc, GFP_KERNEL);
if (!gc)
return;

Expand Down