aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2013-10-10 07:26:33 -0400
committerPablo Neira Ayuso <pablo@netfilter.org>2013-10-14 12:00:57 -0400
commit9ddf63235749a9efa1fad2eeb74be2ee9b580f8d (patch)
tree2606a4fffe7739aa342682f48bd9817bafeeb4ce
parentc54032e05bfcbb261f47aaadf8476e864e8712f4 (diff)
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>
-rw-r--r--include/uapi/linux/netfilter/nf_tables.h11
-rw-r--r--net/netfilter/nf_tables_api.c97
2 files changed, 101 insertions, 7 deletions
diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
index 1563875e6942..a9c4bce1988f 100644
--- a/include/uapi/linux/netfilter/nf_tables.h
+++ b/include/uapi/linux/netfilter/nf_tables.h
@@ -97,13 +97,24 @@ enum nft_hook_attributes {
97#define NFTA_HOOK_MAX (__NFTA_HOOK_MAX - 1) 97#define NFTA_HOOK_MAX (__NFTA_HOOK_MAX - 1)
98 98
99/** 99/**
100 * enum nft_table_flags - nf_tables table flags
101 *
102 * @NFT_TABLE_F_DORMANT: this table is not active
103 */
104enum nft_table_flags {
105 NFT_TABLE_F_DORMANT = 0x1,
106};
107
108/**
100 * enum nft_table_attributes - nf_tables table netlink attributes 109 * enum nft_table_attributes - nf_tables table netlink attributes
101 * 110 *
102 * @NFTA_TABLE_NAME: name of the table (NLA_STRING) 111 * @NFTA_TABLE_NAME: name of the table (NLA_STRING)
112 * @NFTA_TABLE_FLAGS: bitmask of enum nft_table_flags (NLA_U32)
103 */ 113 */
104enum nft_table_attributes { 114enum nft_table_attributes {
105 NFTA_TABLE_UNSPEC, 115 NFTA_TABLE_UNSPEC,
106 NFTA_TABLE_NAME, 116 NFTA_TABLE_NAME,
117 NFTA_TABLE_FLAGS,
107 __NFTA_TABLE_MAX 118 __NFTA_TABLE_MAX
108}; 119};
109#define NFTA_TABLE_MAX (__NFTA_TABLE_MAX - 1) 120#define NFTA_TABLE_MAX (__NFTA_TABLE_MAX - 1)
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 61e017b349cb..a4dd7ce5ec3e 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -158,6 +158,7 @@ static int nf_tables_chain_type_lookup(const struct nft_af_info *afi,
158 158
159static const struct nla_policy nft_table_policy[NFTA_TABLE_MAX + 1] = { 159static const struct nla_policy nft_table_policy[NFTA_TABLE_MAX + 1] = {
160 [NFTA_TABLE_NAME] = { .type = NLA_STRING }, 160 [NFTA_TABLE_NAME] = { .type = NLA_STRING },
161 [NFTA_TABLE_FLAGS] = { .type = NLA_U32 },
161}; 162};
162 163
163static int nf_tables_fill_table_info(struct sk_buff *skb, u32 portid, u32 seq, 164static 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,
177 nfmsg->version = NFNETLINK_V0; 178 nfmsg->version = NFNETLINK_V0;
178 nfmsg->res_id = 0; 179 nfmsg->res_id = 0;
179 180
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)))
181 goto nla_put_failure; 183 goto nla_put_failure;
182 184
183 return nlmsg_end(skb, nlh); 185 return nlmsg_end(skb, nlh);
@@ -301,6 +303,74 @@ err:
301 return err; 303 return err;
302} 304}
303 305
306static 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;
319err:
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
329static 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
339static 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);
370err:
371 return ret;
372}
373
304static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb, 374static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
305 const struct nlmsghdr *nlh, 375 const struct nlmsghdr *nlh,
306 const struct nlattr * const nla[]) 376 const struct nlattr * const nla[])
@@ -328,7 +398,7 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
328 return -EEXIST; 398 return -EEXIST;
329 if (nlh->nlmsg_flags & NLM_F_REPLACE) 399 if (nlh->nlmsg_flags & NLM_F_REPLACE)
330 return -EOPNOTSUPP; 400 return -EOPNOTSUPP;
331 return 0; 401 return nf_tables_updtable(nlsk, skb, nlh, nla, afi, table);
332 } 402 }
333 403
334 table = kzalloc(sizeof(*table) + nla_len(name), GFP_KERNEL); 404 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,
339 INIT_LIST_HEAD(&table->chains); 409 INIT_LIST_HEAD(&table->chains);
340 INIT_LIST_HEAD(&table->sets); 410 INIT_LIST_HEAD(&table->sets);
341 411
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
342 list_add_tail(&table->list, &afi->tables); 424 list_add_tail(&table->list, &afi->tables);
343 nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family); 425 nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family);
344 return 0; 426 return 0;
@@ -890,10 +972,8 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
890 chain->handle = nf_tables_alloc_handle(table); 972 chain->handle = nf_tables_alloc_handle(table);
891 nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN); 973 nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN);
892 974
893 list_add_tail(&chain->list, &table->chains); 975 if (!(table->flags & NFT_TABLE_F_DORMANT) &&
894 table->use++; 976 chain->flags & NFT_BASE_CHAIN) {
895
896 if (chain->flags & NFT_BASE_CHAIN) {
897 err = nf_register_hook(&nft_base_chain(chain)->ops); 977 err = nf_register_hook(&nft_base_chain(chain)->ops);
898 if (err < 0) { 978 if (err < 0) {
899 free_percpu(basechain->stats); 979 free_percpu(basechain->stats);
@@ -901,6 +981,8 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
901 return err; 981 return err;
902 } 982 }
903 } 983 }
984 list_add_tail(&chain->list, &table->chains);
985 table->use++;
904notify: 986notify:
905 nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_NEWCHAIN, 987 nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_NEWCHAIN,
906 family); 988 family);
@@ -948,7 +1030,8 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
948 list_del(&chain->list); 1030 list_del(&chain->list);
949 table->use--; 1031 table->use--;
950 1032
951 if (chain->flags & NFT_BASE_CHAIN) 1033 if (!(table->flags & NFT_TABLE_F_DORMANT) &&
1034 chain->flags & NFT_BASE_CHAIN)
952 nf_unregister_hook(&nft_base_chain(chain)->ops); 1035 nf_unregister_hook(&nft_base_chain(chain)->ops);
953 1036
954 nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_DELCHAIN, 1037 nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_DELCHAIN,