Skip to content

Commit 9ddf632

Browse files
committed
netfilter: nf_tables: add support for dormant tables
This patch allows you to temporarily disable an entire table. You can change the state of a dormant table via NFT_MSG_NEWTABLE messages. Using this operation you can wake up a table, so their chains are registered. This provides atomicity at chain level. Thus, the rule-set of one chain is applied at once, avoiding any possible intermediate state in every chain. Still, the chains that belongs to a table are registered consecutively. This also allows you to have inactive tables in the kernel. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
1 parent c54032e commit 9ddf632

File tree

2 files changed

+101
-7
lines changed

2 files changed

+101
-7
lines changed

include/uapi/linux/netfilter/nf_tables.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,14 +96,25 @@ enum nft_hook_attributes {
9696
};
9797
#define NFTA_HOOK_MAX (__NFTA_HOOK_MAX - 1)
9898

99+
/**
100+
* enum nft_table_flags - nf_tables table flags
101+
*
102+
* @NFT_TABLE_F_DORMANT: this table is not active
103+
*/
104+
enum nft_table_flags {
105+
NFT_TABLE_F_DORMANT = 0x1,
106+
};
107+
99108
/**
100109
* enum nft_table_attributes - nf_tables table netlink attributes
101110
*
102111
* @NFTA_TABLE_NAME: name of the table (NLA_STRING)
112+
* @NFTA_TABLE_FLAGS: bitmask of enum nft_table_flags (NLA_U32)
103113
*/
104114
enum nft_table_attributes {
105115
NFTA_TABLE_UNSPEC,
106116
NFTA_TABLE_NAME,
117+
NFTA_TABLE_FLAGS,
107118
__NFTA_TABLE_MAX
108119
};
109120
#define NFTA_TABLE_MAX (__NFTA_TABLE_MAX - 1)

net/netfilter/nf_tables_api.c

Lines changed: 90 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ static int nf_tables_chain_type_lookup(const struct nft_af_info *afi,
158158

159159
static const struct nla_policy nft_table_policy[NFTA_TABLE_MAX + 1] = {
160160
[NFTA_TABLE_NAME] = { .type = NLA_STRING },
161+
[NFTA_TABLE_FLAGS] = { .type = NLA_U32 },
161162
};
162163

163164
static int nf_tables_fill_table_info(struct sk_buff *skb, u32 portid, u32 seq,
@@ -177,7 +178,8 @@ static int nf_tables_fill_table_info(struct sk_buff *skb, u32 portid, u32 seq,
177178
nfmsg->version = NFNETLINK_V0;
178179
nfmsg->res_id = 0;
179180

180-
if (nla_put_string(skb, NFTA_TABLE_NAME, table->name))
181+
if (nla_put_string(skb, NFTA_TABLE_NAME, table->name) ||
182+
nla_put_be32(skb, NFTA_TABLE_FLAGS, htonl(table->flags)))
181183
goto nla_put_failure;
182184

183185
return nlmsg_end(skb, nlh);
@@ -301,6 +303,74 @@ static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb,
301303
return err;
302304
}
303305

306+
static int nf_tables_table_enable(struct nft_table *table)
307+
{
308+
struct nft_chain *chain;
309+
int err, i = 0;
310+
311+
list_for_each_entry(chain, &table->chains, list) {
312+
err = nf_register_hook(&nft_base_chain(chain)->ops);
313+
if (err < 0)
314+
goto err;
315+
316+
i++;
317+
}
318+
return 0;
319+
err:
320+
list_for_each_entry(chain, &table->chains, list) {
321+
if (i-- <= 0)
322+
break;
323+
324+
nf_unregister_hook(&nft_base_chain(chain)->ops);
325+
}
326+
return err;
327+
}
328+
329+
static int nf_tables_table_disable(struct nft_table *table)
330+
{
331+
struct nft_chain *chain;
332+
333+
list_for_each_entry(chain, &table->chains, list)
334+
nf_unregister_hook(&nft_base_chain(chain)->ops);
335+
336+
return 0;
337+
}
338+
339+
static int nf_tables_updtable(struct sock *nlsk, struct sk_buff *skb,
340+
const struct nlmsghdr *nlh,
341+
const struct nlattr * const nla[],
342+
struct nft_af_info *afi, struct nft_table *table)
343+
{
344+
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
345+
int family = nfmsg->nfgen_family, ret = 0;
346+
347+
if (nla[NFTA_TABLE_FLAGS]) {
348+
__be32 flags;
349+
350+
flags = ntohl(nla_get_be32(nla[NFTA_TABLE_FLAGS]));
351+
if (flags & ~NFT_TABLE_F_DORMANT)
352+
return -EINVAL;
353+
354+
if ((flags & NFT_TABLE_F_DORMANT) &&
355+
!(table->flags & NFT_TABLE_F_DORMANT)) {
356+
ret = nf_tables_table_disable(table);
357+
if (ret >= 0)
358+
table->flags |= NFT_TABLE_F_DORMANT;
359+
} else if (!(flags & NFT_TABLE_F_DORMANT) &&
360+
table->flags & NFT_TABLE_F_DORMANT) {
361+
ret = nf_tables_table_enable(table);
362+
if (ret >= 0)
363+
table->flags &= ~NFT_TABLE_F_DORMANT;
364+
}
365+
if (ret < 0)
366+
goto err;
367+
}
368+
369+
nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family);
370+
err:
371+
return ret;
372+
}
373+
304374
static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
305375
const struct nlmsghdr *nlh,
306376
const struct nlattr * const nla[])
@@ -328,7 +398,7 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
328398
return -EEXIST;
329399
if (nlh->nlmsg_flags & NLM_F_REPLACE)
330400
return -EOPNOTSUPP;
331-
return 0;
401+
return nf_tables_updtable(nlsk, skb, nlh, nla, afi, table);
332402
}
333403

334404
table = kzalloc(sizeof(*table) + nla_len(name), GFP_KERNEL);
@@ -339,6 +409,18 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
339409
INIT_LIST_HEAD(&table->chains);
340410
INIT_LIST_HEAD(&table->sets);
341411

412+
if (nla[NFTA_TABLE_FLAGS]) {
413+
__be32 flags;
414+
415+
flags = ntohl(nla_get_be32(nla[NFTA_TABLE_FLAGS]));
416+
if (flags & ~NFT_TABLE_F_DORMANT) {
417+
kfree(table);
418+
return -EINVAL;
419+
}
420+
421+
table->flags |= flags;
422+
}
423+
342424
list_add_tail(&table->list, &afi->tables);
343425
nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family);
344426
return 0;
@@ -890,17 +972,17 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
890972
chain->handle = nf_tables_alloc_handle(table);
891973
nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN);
892974

893-
list_add_tail(&chain->list, &table->chains);
894-
table->use++;
895-
896-
if (chain->flags & NFT_BASE_CHAIN) {
975+
if (!(table->flags & NFT_TABLE_F_DORMANT) &&
976+
chain->flags & NFT_BASE_CHAIN) {
897977
err = nf_register_hook(&nft_base_chain(chain)->ops);
898978
if (err < 0) {
899979
free_percpu(basechain->stats);
900980
kfree(basechain);
901981
return err;
902982
}
903983
}
984+
list_add_tail(&chain->list, &table->chains);
985+
table->use++;
904986
notify:
905987
nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_NEWCHAIN,
906988
family);
@@ -948,7 +1030,8 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
9481030
list_del(&chain->list);
9491031
table->use--;
9501032

951-
if (chain->flags & NFT_BASE_CHAIN)
1033+
if (!(table->flags & NFT_TABLE_F_DORMANT) &&
1034+
chain->flags & NFT_BASE_CHAIN)
9521035
nf_unregister_hook(&nft_base_chain(chain)->ops);
9531036

9541037
nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_DELCHAIN,

0 commit comments

Comments
 (0)