Commit 38e029f1 authored by Pablo Neira Ayuso's avatar Pablo Neira Ayuso

netfilter: nf_tables: set NLM_F_DUMP_INTR if netlink dumping is stale

An updater may interfer with the dumping of any of the object lists.
Fix this by using a per-net generation counter and use the
nl_dump_check_consistent() interface so the NLM_F_DUMP_INTR flag is set
to notify userspace that it has to restart the dump since an updater
has interfered.

This patch also replaces the existing consistency checking code in the
rule dumping path since it is broken. Basically, the value that the
dump callback returns is not propagated to userspace via
netlink_dump_start().
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent e688a7f8
...@@ -13,8 +13,8 @@ struct netns_nftables { ...@@ -13,8 +13,8 @@ struct netns_nftables {
struct nft_af_info *inet; struct nft_af_info *inet;
struct nft_af_info *arp; struct nft_af_info *arp;
struct nft_af_info *bridge; struct nft_af_info *bridge;
unsigned int base_seq;
u8 gencursor; u8 gencursor;
u8 genctr;
}; };
#endif #endif
...@@ -278,6 +278,8 @@ static int nf_tables_dump_tables(struct sk_buff *skb, ...@@ -278,6 +278,8 @@ static int nf_tables_dump_tables(struct sk_buff *skb,
int family = nfmsg->nfgen_family; int family = nfmsg->nfgen_family;
rcu_read_lock(); rcu_read_lock();
cb->seq = net->nft.base_seq;
list_for_each_entry_rcu(afi, &net->nft.af_info, list) { list_for_each_entry_rcu(afi, &net->nft.af_info, list) {
if (family != NFPROTO_UNSPEC && family != afi->family) if (family != NFPROTO_UNSPEC && family != afi->family)
continue; continue;
...@@ -295,6 +297,8 @@ static int nf_tables_dump_tables(struct sk_buff *skb, ...@@ -295,6 +297,8 @@ static int nf_tables_dump_tables(struct sk_buff *skb,
NLM_F_MULTI, NLM_F_MULTI,
afi->family, table) < 0) afi->family, table) < 0)
goto done; goto done;
nl_dump_check_consistent(cb, nlmsg_hdr(skb));
cont: cont:
idx++; idx++;
} }
...@@ -767,6 +771,8 @@ static int nf_tables_dump_chains(struct sk_buff *skb, ...@@ -767,6 +771,8 @@ static int nf_tables_dump_chains(struct sk_buff *skb,
int family = nfmsg->nfgen_family; int family = nfmsg->nfgen_family;
rcu_read_lock(); rcu_read_lock();
cb->seq = net->nft.base_seq;
list_for_each_entry_rcu(afi, &net->nft.af_info, list) { list_for_each_entry_rcu(afi, &net->nft.af_info, list) {
if (family != NFPROTO_UNSPEC && family != afi->family) if (family != NFPROTO_UNSPEC && family != afi->family)
continue; continue;
...@@ -784,6 +790,8 @@ static int nf_tables_dump_chains(struct sk_buff *skb, ...@@ -784,6 +790,8 @@ static int nf_tables_dump_chains(struct sk_buff *skb,
NLM_F_MULTI, NLM_F_MULTI,
afi->family, table, chain) < 0) afi->family, table, chain) < 0)
goto done; goto done;
nl_dump_check_consistent(cb, nlmsg_hdr(skb));
cont: cont:
idx++; idx++;
} }
...@@ -1555,10 +1563,10 @@ static int nf_tables_dump_rules(struct sk_buff *skb, ...@@ -1555,10 +1563,10 @@ static int nf_tables_dump_rules(struct sk_buff *skb,
unsigned int idx = 0, s_idx = cb->args[0]; unsigned int idx = 0, s_idx = cb->args[0];
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family; int family = nfmsg->nfgen_family;
u8 genctr = ACCESS_ONCE(net->nft.genctr);
u8 gencursor = ACCESS_ONCE(net->nft.gencursor);
rcu_read_lock(); rcu_read_lock();
cb->seq = net->nft.base_seq;
list_for_each_entry_rcu(afi, &net->nft.af_info, list) { list_for_each_entry_rcu(afi, &net->nft.af_info, list) {
if (family != NFPROTO_UNSPEC && family != afi->family) if (family != NFPROTO_UNSPEC && family != afi->family)
continue; continue;
...@@ -1579,6 +1587,8 @@ static int nf_tables_dump_rules(struct sk_buff *skb, ...@@ -1579,6 +1587,8 @@ static int nf_tables_dump_rules(struct sk_buff *skb,
NLM_F_MULTI | NLM_F_APPEND, NLM_F_MULTI | NLM_F_APPEND,
afi->family, table, chain, rule) < 0) afi->family, table, chain, rule) < 0)
goto done; goto done;
nl_dump_check_consistent(cb, nlmsg_hdr(skb));
cont: cont:
idx++; idx++;
} }
...@@ -1588,10 +1598,6 @@ static int nf_tables_dump_rules(struct sk_buff *skb, ...@@ -1588,10 +1598,6 @@ static int nf_tables_dump_rules(struct sk_buff *skb,
done: done:
rcu_read_unlock(); rcu_read_unlock();
/* Invalidate this dump, a transition to the new generation happened */
if (gencursor != net->nft.gencursor || genctr != net->nft.genctr)
return -EBUSY;
cb->args[0] = idx; cb->args[0] = idx;
return skb->len; return skb->len;
} }
...@@ -2244,6 +2250,8 @@ static int nf_tables_dump_sets_table(struct nft_ctx *ctx, struct sk_buff *skb, ...@@ -2244,6 +2250,8 @@ static int nf_tables_dump_sets_table(struct nft_ctx *ctx, struct sk_buff *skb,
return skb->len; return skb->len;
rcu_read_lock(); rcu_read_lock();
cb->seq = ctx->net->nft.base_seq;
list_for_each_entry_rcu(set, &ctx->table->sets, list) { list_for_each_entry_rcu(set, &ctx->table->sets, list) {
if (idx < s_idx) if (idx < s_idx)
goto cont; goto cont;
...@@ -2252,6 +2260,7 @@ static int nf_tables_dump_sets_table(struct nft_ctx *ctx, struct sk_buff *skb, ...@@ -2252,6 +2260,7 @@ static int nf_tables_dump_sets_table(struct nft_ctx *ctx, struct sk_buff *skb,
cb->args[0] = idx; cb->args[0] = idx;
goto done; goto done;
} }
nl_dump_check_consistent(cb, nlmsg_hdr(skb));
cont: cont:
idx++; idx++;
} }
...@@ -2272,6 +2281,8 @@ static int nf_tables_dump_sets_family(struct nft_ctx *ctx, struct sk_buff *skb, ...@@ -2272,6 +2281,8 @@ static int nf_tables_dump_sets_family(struct nft_ctx *ctx, struct sk_buff *skb,
return skb->len; return skb->len;
rcu_read_lock(); rcu_read_lock();
cb->seq = ctx->net->nft.base_seq;
list_for_each_entry_rcu(table, &ctx->afi->tables, list) { list_for_each_entry_rcu(table, &ctx->afi->tables, list) {
if (cur_table) { if (cur_table) {
if (cur_table != table) if (cur_table != table)
...@@ -2290,6 +2301,7 @@ static int nf_tables_dump_sets_family(struct nft_ctx *ctx, struct sk_buff *skb, ...@@ -2290,6 +2301,7 @@ static int nf_tables_dump_sets_family(struct nft_ctx *ctx, struct sk_buff *skb,
cb->args[2] = (unsigned long) table; cb->args[2] = (unsigned long) table;
goto done; goto done;
} }
nl_dump_check_consistent(cb, nlmsg_hdr(skb));
cont: cont:
idx++; idx++;
} }
...@@ -2314,6 +2326,8 @@ static int nf_tables_dump_sets_all(struct nft_ctx *ctx, struct sk_buff *skb, ...@@ -2314,6 +2326,8 @@ static int nf_tables_dump_sets_all(struct nft_ctx *ctx, struct sk_buff *skb,
return skb->len; return skb->len;
rcu_read_lock(); rcu_read_lock();
cb->seq = net->nft.base_seq;
list_for_each_entry_rcu(afi, &net->nft.af_info, list) { list_for_each_entry_rcu(afi, &net->nft.af_info, list) {
if (cur_family) { if (cur_family) {
if (afi->family != cur_family) if (afi->family != cur_family)
...@@ -2344,6 +2358,7 @@ static int nf_tables_dump_sets_all(struct nft_ctx *ctx, struct sk_buff *skb, ...@@ -2344,6 +2358,7 @@ static int nf_tables_dump_sets_all(struct nft_ctx *ctx, struct sk_buff *skb,
cb->args[3] = afi->family; cb->args[3] = afi->family;
goto done; goto done;
} }
nl_dump_check_consistent(cb, nlmsg_hdr(skb));
cont: cont:
idx++; idx++;
} }
...@@ -3361,7 +3376,7 @@ static int nf_tables_commit(struct sk_buff *skb) ...@@ -3361,7 +3376,7 @@ static int nf_tables_commit(struct sk_buff *skb)
struct nft_set *set; struct nft_set *set;
/* Bump generation counter, invalidate any dump in progress */ /* Bump generation counter, invalidate any dump in progress */
net->nft.genctr++; while (++net->nft.base_seq == 0);
/* A new generation has just started */ /* A new generation has just started */
net->nft.gencursor = gencursor_next(net); net->nft.gencursor = gencursor_next(net);
...@@ -3966,6 +3981,7 @@ static int nf_tables_init_net(struct net *net) ...@@ -3966,6 +3981,7 @@ static int nf_tables_init_net(struct net *net)
{ {
INIT_LIST_HEAD(&net->nft.af_info); INIT_LIST_HEAD(&net->nft.af_info);
INIT_LIST_HEAD(&net->nft.commit_list); INIT_LIST_HEAD(&net->nft.commit_list);
net->nft.base_seq = 1;
return 0; return 0;
} }
......
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