Skip to content

Commit

Permalink
netfilter: nf_tables: add support for dormant tables
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Pablo Neira Ayuso committed Oct 14, 2013
1 parent c54032e commit 9ddf632
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 7 deletions.
11 changes: 11 additions & 0 deletions include/uapi/linux/netfilter/nf_tables.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,25 @@ enum nft_hook_attributes {
};
#define NFTA_HOOK_MAX (__NFTA_HOOK_MAX - 1)

/**
* enum nft_table_flags - nf_tables table flags
*
* @NFT_TABLE_F_DORMANT: this table is not active
*/
enum nft_table_flags {
NFT_TABLE_F_DORMANT = 0x1,
};

/**
* enum nft_table_attributes - nf_tables table netlink attributes
*
* @NFTA_TABLE_NAME: name of the table (NLA_STRING)
* @NFTA_TABLE_FLAGS: bitmask of enum nft_table_flags (NLA_U32)
*/
enum nft_table_attributes {
NFTA_TABLE_UNSPEC,
NFTA_TABLE_NAME,
NFTA_TABLE_FLAGS,
__NFTA_TABLE_MAX
};
#define NFTA_TABLE_MAX (__NFTA_TABLE_MAX - 1)
Expand Down
97 changes: 90 additions & 7 deletions net/netfilter/nf_tables_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ static int nf_tables_chain_type_lookup(const struct nft_af_info *afi,

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

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

if (nla_put_string(skb, NFTA_TABLE_NAME, table->name))
if (nla_put_string(skb, NFTA_TABLE_NAME, table->name) ||
nla_put_be32(skb, NFTA_TABLE_FLAGS, htonl(table->flags)))
goto nla_put_failure;

return nlmsg_end(skb, nlh);
Expand Down Expand Up @@ -301,6 +303,74 @@ static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb,
return err;
}

static int nf_tables_table_enable(struct nft_table *table)
{
struct nft_chain *chain;
int err, i = 0;

list_for_each_entry(chain, &table->chains, list) {
err = nf_register_hook(&nft_base_chain(chain)->ops);
if (err < 0)
goto err;

i++;
}
return 0;
err:
list_for_each_entry(chain, &table->chains, list) {
if (i-- <= 0)
break;

nf_unregister_hook(&nft_base_chain(chain)->ops);
}
return err;
}

static int nf_tables_table_disable(struct nft_table *table)
{
struct nft_chain *chain;

list_for_each_entry(chain, &table->chains, list)
nf_unregister_hook(&nft_base_chain(chain)->ops);

return 0;
}

static int nf_tables_updtable(struct sock *nlsk, struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const nla[],
struct nft_af_info *afi, struct nft_table *table)
{
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
int family = nfmsg->nfgen_family, ret = 0;

if (nla[NFTA_TABLE_FLAGS]) {
__be32 flags;

flags = ntohl(nla_get_be32(nla[NFTA_TABLE_FLAGS]));
if (flags & ~NFT_TABLE_F_DORMANT)
return -EINVAL;

if ((flags & NFT_TABLE_F_DORMANT) &&
!(table->flags & NFT_TABLE_F_DORMANT)) {
ret = nf_tables_table_disable(table);
if (ret >= 0)
table->flags |= NFT_TABLE_F_DORMANT;
} else if (!(flags & NFT_TABLE_F_DORMANT) &&
table->flags & NFT_TABLE_F_DORMANT) {
ret = nf_tables_table_enable(table);
if (ret >= 0)
table->flags &= ~NFT_TABLE_F_DORMANT;
}
if (ret < 0)
goto err;
}

nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family);
err:
return ret;
}

static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const nla[])
Expand Down Expand Up @@ -328,7 +398,7 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
return -EEXIST;
if (nlh->nlmsg_flags & NLM_F_REPLACE)
return -EOPNOTSUPP;
return 0;
return nf_tables_updtable(nlsk, skb, nlh, nla, afi, table);
}

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

if (nla[NFTA_TABLE_FLAGS]) {
__be32 flags;

flags = ntohl(nla_get_be32(nla[NFTA_TABLE_FLAGS]));
if (flags & ~NFT_TABLE_F_DORMANT) {
kfree(table);
return -EINVAL;
}

table->flags |= flags;
}

list_add_tail(&table->list, &afi->tables);
nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family);
return 0;
Expand Down Expand Up @@ -890,17 +972,17 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
chain->handle = nf_tables_alloc_handle(table);
nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN);

list_add_tail(&chain->list, &table->chains);
table->use++;

if (chain->flags & NFT_BASE_CHAIN) {
if (!(table->flags & NFT_TABLE_F_DORMANT) &&
chain->flags & NFT_BASE_CHAIN) {
err = nf_register_hook(&nft_base_chain(chain)->ops);
if (err < 0) {
free_percpu(basechain->stats);
kfree(basechain);
return err;
}
}
list_add_tail(&chain->list, &table->chains);
table->use++;
notify:
nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_NEWCHAIN,
family);
Expand Down Expand Up @@ -948,7 +1030,8 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
list_del(&chain->list);
table->use--;

if (chain->flags & NFT_BASE_CHAIN)
if (!(table->flags & NFT_TABLE_F_DORMANT) &&
chain->flags & NFT_BASE_CHAIN)
nf_unregister_hook(&nft_base_chain(chain)->ops);

nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_DELCHAIN,
Expand Down

0 comments on commit 9ddf632

Please sign in to comment.