diff options
| author | Pablo Neira Ayuso <pablo@netfilter.org> | 2013-10-10 07:26:33 -0400 |
|---|---|---|
| committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2013-10-14 12:00:57 -0400 |
| commit | 9ddf63235749a9efa1fad2eeb74be2ee9b580f8d (patch) | |
| tree | 2606a4fffe7739aa342682f48bd9817bafeeb4ce | |
| parent | c54032e05bfcbb261f47aaadf8476e864e8712f4 (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.h | 11 | ||||
| -rw-r--r-- | net/netfilter/nf_tables_api.c | 97 |
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 | */ | ||
| 104 | enum 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 | */ |
| 104 | enum nft_table_attributes { | 114 | enum 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 | ||
| 159 | static const struct nla_policy nft_table_policy[NFTA_TABLE_MAX + 1] = { | 159 | static 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 | ||
| 163 | static int nf_tables_fill_table_info(struct sk_buff *skb, u32 portid, u32 seq, | 164 | 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, | |||
| 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 | ||
| 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 | |||
| 304 | static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb, | 374 | static 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++; | ||
| 904 | notify: | 986 | notify: |
| 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, |
