Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+101 -7
+11
include/uapi/linux/netfilter/nf_tables.h
··· 97 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 109 * enum nft_table_attributes - nf_tables table netlink attributes 101 110 * 102 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 114 enum nft_table_attributes { 105 115 NFTA_TABLE_UNSPEC, 106 116 NFTA_TABLE_NAME, 117 + NFTA_TABLE_FLAGS, 107 118 __NFTA_TABLE_MAX 108 119 }; 109 120 #define NFTA_TABLE_MAX (__NFTA_TABLE_MAX - 1)
+90 -7
net/netfilter/nf_tables_api.c
··· 158 158 159 159 static const struct nla_policy nft_table_policy[NFTA_TABLE_MAX + 1] = { 160 160 [NFTA_TABLE_NAME] = { .type = NLA_STRING }, 161 + [NFTA_TABLE_FLAGS] = { .type = NLA_U32 }, 161 162 }; 162 163 163 164 static int nf_tables_fill_table_info(struct sk_buff *skb, u32 portid, u32 seq, ··· 178 177 nfmsg->version = NFNETLINK_V0; 179 178 nfmsg->res_id = 0; 180 179 181 - if (nla_put_string(skb, NFTA_TABLE_NAME, table->name)) 180 + if (nla_put_string(skb, NFTA_TABLE_NAME, table->name) || 181 + nla_put_be32(skb, NFTA_TABLE_FLAGS, htonl(table->flags))) 182 182 goto nla_put_failure; 183 183 184 184 return nlmsg_end(skb, nlh); ··· 303 301 return err; 304 302 } 305 303 304 + static int nf_tables_table_enable(struct nft_table *table) 305 + { 306 + struct nft_chain *chain; 307 + int err, i = 0; 308 + 309 + list_for_each_entry(chain, &table->chains, list) { 310 + err = nf_register_hook(&nft_base_chain(chain)->ops); 311 + if (err < 0) 312 + goto err; 313 + 314 + i++; 315 + } 316 + return 0; 317 + err: 318 + list_for_each_entry(chain, &table->chains, list) { 319 + if (i-- <= 0) 320 + break; 321 + 322 + nf_unregister_hook(&nft_base_chain(chain)->ops); 323 + } 324 + return err; 325 + } 326 + 327 + static int nf_tables_table_disable(struct nft_table *table) 328 + { 329 + struct nft_chain *chain; 330 + 331 + list_for_each_entry(chain, &table->chains, list) 332 + nf_unregister_hook(&nft_base_chain(chain)->ops); 333 + 334 + return 0; 335 + } 336 + 337 + static int nf_tables_updtable(struct sock *nlsk, struct sk_buff *skb, 338 + const struct nlmsghdr *nlh, 339 + const struct nlattr * const nla[], 340 + struct nft_af_info *afi, struct nft_table *table) 341 + { 342 + const struct nfgenmsg *nfmsg = nlmsg_data(nlh); 343 + int family = nfmsg->nfgen_family, ret = 0; 344 + 345 + if (nla[NFTA_TABLE_FLAGS]) { 346 + __be32 flags; 347 + 348 + flags = ntohl(nla_get_be32(nla[NFTA_TABLE_FLAGS])); 349 + if (flags & ~NFT_TABLE_F_DORMANT) 350 + return -EINVAL; 351 + 352 + if ((flags & NFT_TABLE_F_DORMANT) && 353 + !(table->flags & NFT_TABLE_F_DORMANT)) { 354 + ret = nf_tables_table_disable(table); 355 + if (ret >= 0) 356 + table->flags |= NFT_TABLE_F_DORMANT; 357 + } else if (!(flags & NFT_TABLE_F_DORMANT) && 358 + table->flags & NFT_TABLE_F_DORMANT) { 359 + ret = nf_tables_table_enable(table); 360 + if (ret >= 0) 361 + table->flags &= ~NFT_TABLE_F_DORMANT; 362 + } 363 + if (ret < 0) 364 + goto err; 365 + } 366 + 367 + nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family); 368 + err: 369 + return ret; 370 + } 371 + 306 372 static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb, 307 373 const struct nlmsghdr *nlh, 308 374 const struct nlattr * const nla[]) ··· 398 328 return -EEXIST; 399 329 if (nlh->nlmsg_flags & NLM_F_REPLACE) 400 330 return -EOPNOTSUPP; 401 - return 0; 331 + return nf_tables_updtable(nlsk, skb, nlh, nla, afi, table); 402 332 } 403 333 404 334 table = kzalloc(sizeof(*table) + nla_len(name), GFP_KERNEL); ··· 408 338 nla_strlcpy(table->name, name, nla_len(name)); 409 339 INIT_LIST_HEAD(&table->chains); 410 340 INIT_LIST_HEAD(&table->sets); 341 + 342 + if (nla[NFTA_TABLE_FLAGS]) { 343 + __be32 flags; 344 + 345 + flags = ntohl(nla_get_be32(nla[NFTA_TABLE_FLAGS])); 346 + if (flags & ~NFT_TABLE_F_DORMANT) { 347 + kfree(table); 348 + return -EINVAL; 349 + } 350 + 351 + table->flags |= flags; 352 + } 411 353 412 354 list_add_tail(&table->list, &afi->tables); 413 355 nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family); ··· 972 890 chain->handle = nf_tables_alloc_handle(table); 973 891 nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN); 974 892 975 - list_add_tail(&chain->list, &table->chains); 976 - table->use++; 977 - 978 - if (chain->flags & NFT_BASE_CHAIN) { 893 + if (!(table->flags & NFT_TABLE_F_DORMANT) && 894 + chain->flags & NFT_BASE_CHAIN) { 979 895 err = nf_register_hook(&nft_base_chain(chain)->ops); 980 896 if (err < 0) { 981 897 free_percpu(basechain->stats); ··· 981 901 return err; 982 902 } 983 903 } 904 + list_add_tail(&chain->list, &table->chains); 905 + table->use++; 984 906 notify: 985 907 nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_NEWCHAIN, 986 908 family); ··· 1030 948 list_del(&chain->list); 1031 949 table->use--; 1032 950 1033 - if (chain->flags & NFT_BASE_CHAIN) 951 + if (!(table->flags & NFT_TABLE_F_DORMANT) && 952 + chain->flags & NFT_BASE_CHAIN) 1034 953 nf_unregister_hook(&nft_base_chain(chain)->ops); 1035 954 1036 955 nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_DELCHAIN,