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

netfilter: nf_tables: use dedicated mutex to guard transactions

Continue to use nftnl subsys mutex to protect (un)registration of hook types,
expressions and so on, but force batch operations to do their own
locking.

This allows distinct net namespaces to perform transactions in parallel.

Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Florian Westphal and committed by
Pablo Neira Ayuso
f102d66b 2a43ecf9

+77 -28
+1
include/net/netns/nftables.h
··· 7 7 struct netns_nftables { 8 8 struct list_head tables; 9 9 struct list_head commit_list; 10 + struct mutex commit_mutex; 10 11 unsigned int base_seq; 11 12 u8 gencursor; 12 13 u8 validate_state;
+69 -19
net/netfilter/nf_tables_api.c
··· 480 480 if (WARN(ret >= MODULE_NAME_LEN, "truncated: '%s' (len %d)", module_name, ret)) 481 481 return; 482 482 483 - nfnl_unlock(NFNL_SUBSYS_NFTABLES); 483 + mutex_unlock(&net->nft.commit_mutex); 484 484 request_module("%s", module_name); 485 - nfnl_lock(NFNL_SUBSYS_NFTABLES); 485 + mutex_lock(&net->nft.commit_mutex); 486 486 } 487 487 #endif 488 + 489 + static void lockdep_nfnl_nft_mutex_not_held(void) 490 + { 491 + #ifdef CONFIG_PROVE_LOCKING 492 + WARN_ON_ONCE(lockdep_nfnl_is_held(NFNL_SUBSYS_NFTABLES)); 493 + #endif 494 + } 488 495 489 496 static const struct nft_chain_type * 490 497 nf_tables_chain_type_lookup(struct net *net, const struct nlattr *nla, ··· 502 495 type = __nf_tables_chain_type_lookup(nla, family); 503 496 if (type != NULL) 504 497 return type; 498 + 499 + lockdep_nfnl_nft_mutex_not_held(); 505 500 #ifdef CONFIG_MODULES 506 501 if (autoload) { 507 502 nft_request_module(net, "nft-chain-%u-%.*s", family, ··· 811 802 struct nft_ctx ctx; 812 803 int err; 813 804 805 + lockdep_assert_held(&net->nft.commit_mutex); 814 806 attr = nla[NFTA_TABLE_NAME]; 815 807 table = nft_table_lookup(net, attr, family, genmask); 816 808 if (IS_ERR(table)) { ··· 1052 1042 return ERR_PTR(-ENOENT); 1053 1043 } 1054 1044 1055 - static struct nft_chain *nft_chain_lookup(struct nft_table *table, 1045 + static bool lockdep_commit_lock_is_held(struct net *net) 1046 + { 1047 + #ifdef CONFIG_PROVE_LOCKING 1048 + return lockdep_is_held(&net->nft.commit_mutex); 1049 + #else 1050 + return true; 1051 + #endif 1052 + } 1053 + 1054 + static struct nft_chain *nft_chain_lookup(struct net *net, 1055 + struct nft_table *table, 1056 1056 const struct nlattr *nla, u8 genmask) 1057 1057 { 1058 1058 char search[NFT_CHAIN_MAXNAMELEN + 1]; ··· 1075 1055 nla_strlcpy(search, nla, sizeof(search)); 1076 1056 1077 1057 WARN_ON(!rcu_read_lock_held() && 1078 - !lockdep_nfnl_is_held(NFNL_SUBSYS_NFTABLES)); 1058 + !lockdep_commit_lock_is_held(net)); 1079 1059 1080 1060 chain = ERR_PTR(-ENOENT); 1081 1061 rcu_read_lock(); ··· 1315 1295 return PTR_ERR(table); 1316 1296 } 1317 1297 1318 - chain = nft_chain_lookup(table, nla[NFTA_CHAIN_NAME], genmask); 1298 + chain = nft_chain_lookup(net, table, nla[NFTA_CHAIN_NAME], genmask); 1319 1299 if (IS_ERR(chain)) { 1320 1300 NL_SET_BAD_ATTR(extack, nla[NFTA_CHAIN_NAME]); 1321 1301 return PTR_ERR(chain); ··· 1447 1427 const struct nft_chain_type *type; 1448 1428 struct net_device *dev; 1449 1429 int err; 1430 + 1431 + lockdep_assert_held(&net->nft.commit_mutex); 1432 + lockdep_nfnl_nft_mutex_not_held(); 1450 1433 1451 1434 err = nla_parse_nested(ha, NFTA_HOOK_MAX, nla[NFTA_CHAIN_HOOK], 1452 1435 nft_hook_policy, NULL); ··· 1685 1662 nla[NFTA_CHAIN_NAME]) { 1686 1663 struct nft_chain *chain2; 1687 1664 1688 - chain2 = nft_chain_lookup(table, nla[NFTA_CHAIN_NAME], genmask); 1665 + chain2 = nft_chain_lookup(ctx->net, table, 1666 + nla[NFTA_CHAIN_NAME], genmask); 1689 1667 if (!IS_ERR(chain2)) 1690 1668 return -EEXIST; 1691 1669 } ··· 1748 1724 1749 1725 create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false; 1750 1726 1727 + lockdep_assert_held(&net->nft.commit_mutex); 1728 + 1751 1729 table = nft_table_lookup(net, nla[NFTA_CHAIN_TABLE], family, genmask); 1752 1730 if (IS_ERR(table)) { 1753 1731 NL_SET_BAD_ATTR(extack, nla[NFTA_CHAIN_TABLE]); ··· 1768 1742 } 1769 1743 attr = nla[NFTA_CHAIN_HANDLE]; 1770 1744 } else { 1771 - chain = nft_chain_lookup(table, attr, genmask); 1745 + chain = nft_chain_lookup(net, table, attr, genmask); 1772 1746 if (IS_ERR(chain)) { 1773 1747 if (PTR_ERR(chain) != -ENOENT) { 1774 1748 NL_SET_BAD_ATTR(extack, attr); ··· 1846 1820 chain = nft_chain_lookup_byhandle(table, handle, genmask); 1847 1821 } else { 1848 1822 attr = nla[NFTA_CHAIN_NAME]; 1849 - chain = nft_chain_lookup(table, attr, genmask); 1823 + chain = nft_chain_lookup(net, table, attr, genmask); 1850 1824 } 1851 1825 if (IS_ERR(chain)) { 1852 1826 NL_SET_BAD_ATTR(extack, attr); ··· 1944 1918 if (type != NULL && try_module_get(type->owner)) 1945 1919 return type; 1946 1920 1921 + lockdep_nfnl_nft_mutex_not_held(); 1947 1922 #ifdef CONFIG_MODULES 1948 1923 if (type == NULL) { 1949 1924 nft_request_module(net, "nft-expr-%u-%.*s", family, ··· 2379 2352 return PTR_ERR(table); 2380 2353 } 2381 2354 2382 - chain = nft_chain_lookup(table, nla[NFTA_RULE_CHAIN], genmask); 2355 + chain = nft_chain_lookup(net, table, nla[NFTA_RULE_CHAIN], genmask); 2383 2356 if (IS_ERR(chain)) { 2384 2357 NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN]); 2385 2358 return PTR_ERR(chain); ··· 2413 2386 { 2414 2387 struct nft_expr *expr; 2415 2388 2389 + lockdep_assert_held(&ctx->net->nft.commit_mutex); 2416 2390 /* 2417 2391 * Careful: some expressions might not be initialized in case this 2418 2392 * is called on error from nf_tables_newrule(). ··· 2504 2476 bool create; 2505 2477 u64 handle, pos_handle; 2506 2478 2479 + lockdep_assert_held(&net->nft.commit_mutex); 2480 + 2507 2481 create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false; 2508 2482 2509 2483 table = nft_table_lookup(net, nla[NFTA_RULE_TABLE], family, genmask); ··· 2514 2484 return PTR_ERR(table); 2515 2485 } 2516 2486 2517 - chain = nft_chain_lookup(table, nla[NFTA_RULE_CHAIN], genmask); 2487 + chain = nft_chain_lookup(net, table, nla[NFTA_RULE_CHAIN], genmask); 2518 2488 if (IS_ERR(chain)) { 2519 2489 NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN]); 2520 2490 return PTR_ERR(chain); ··· 2714 2684 } 2715 2685 2716 2686 if (nla[NFTA_RULE_CHAIN]) { 2717 - chain = nft_chain_lookup(table, nla[NFTA_RULE_CHAIN], genmask); 2687 + chain = nft_chain_lookup(net, table, nla[NFTA_RULE_CHAIN], 2688 + genmask); 2718 2689 if (IS_ERR(chain)) { 2719 2690 NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN]); 2720 2691 return PTR_ERR(chain); ··· 2807 2776 const struct nft_set_type *type; 2808 2777 u32 flags = 0; 2809 2778 2779 + lockdep_assert_held(&ctx->net->nft.commit_mutex); 2780 + lockdep_nfnl_nft_mutex_not_held(); 2810 2781 #ifdef CONFIG_MODULES 2811 2782 if (list_empty(&nf_tables_set_types)) { 2812 2783 nft_request_module(ctx->net, "nft-set"); ··· 4853 4820 if (type != NULL && try_module_get(type->owner)) 4854 4821 return type; 4855 4822 4823 + lockdep_nfnl_nft_mutex_not_held(); 4856 4824 #ifdef CONFIG_MODULES 4857 4825 if (type == NULL) { 4858 4826 nft_request_module(net, "nft-obj-%u", objtype); ··· 5413 5379 if (type != NULL && try_module_get(type->owner)) 5414 5380 return type; 5415 5381 5382 + lockdep_nfnl_nft_mutex_not_held(); 5416 5383 #ifdef CONFIG_MODULES 5417 5384 if (type == NULL) { 5418 5385 nft_request_module(net, "nf-flowtable-%u", family); ··· 6267 6232 next_genbit = nft_gencursor_next(net); 6268 6233 6269 6234 g0 = rcu_dereference_protected(chain->rules_gen_0, 6270 - lockdep_nfnl_is_held(NFNL_SUBSYS_NFTABLES)); 6235 + lockdep_commit_lock_is_held(net)); 6271 6236 g1 = rcu_dereference_protected(chain->rules_gen_1, 6272 - lockdep_nfnl_is_held(NFNL_SUBSYS_NFTABLES)); 6237 + lockdep_commit_lock_is_held(net)); 6273 6238 6274 6239 /* No changes to this chain? */ 6275 6240 if (chain->rules_next == NULL) { ··· 6477 6442 6478 6443 nf_tables_commit_release(net); 6479 6444 nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN); 6445 + mutex_unlock(&net->nft.commit_mutex); 6480 6446 6481 6447 return 0; 6482 6448 } ··· 6629 6593 6630 6594 static int nf_tables_abort(struct net *net, struct sk_buff *skb) 6631 6595 { 6632 - return __nf_tables_abort(net); 6596 + int ret = __nf_tables_abort(net); 6597 + 6598 + mutex_unlock(&net->nft.commit_mutex); 6599 + 6600 + return ret; 6633 6601 } 6634 6602 6635 6603 static bool nf_tables_valid_genid(struct net *net, u32 genid) 6636 6604 { 6637 - return genid == 0 || net->nft.base_seq == genid; 6605 + bool genid_ok; 6606 + 6607 + mutex_lock(&net->nft.commit_mutex); 6608 + 6609 + genid_ok = genid == 0 || net->nft.base_seq == genid; 6610 + if (!genid_ok) 6611 + mutex_unlock(&net->nft.commit_mutex); 6612 + 6613 + /* else, commit mutex has to be released by commit or abort function */ 6614 + return genid_ok; 6638 6615 } 6639 6616 6640 6617 static const struct nfnetlink_subsystem nf_tables_subsys = { ··· 6986 6937 case NFT_GOTO: 6987 6938 if (!tb[NFTA_VERDICT_CHAIN]) 6988 6939 return -EINVAL; 6989 - chain = nft_chain_lookup(ctx->table, tb[NFTA_VERDICT_CHAIN], 6990 - genmask); 6940 + chain = nft_chain_lookup(ctx->net, ctx->table, 6941 + tb[NFTA_VERDICT_CHAIN], genmask); 6991 6942 if (IS_ERR(chain)) 6992 6943 return PTR_ERR(chain); 6993 6944 if (nft_is_base_chain(chain)) ··· 7232 7183 { 7233 7184 INIT_LIST_HEAD(&net->nft.tables); 7234 7185 INIT_LIST_HEAD(&net->nft.commit_list); 7186 + mutex_init(&net->nft.commit_mutex); 7235 7187 net->nft.base_seq = 1; 7236 7188 net->nft.validate_state = NFT_VALIDATE_SKIP; 7237 7189 ··· 7241 7191 7242 7192 static void __net_exit nf_tables_exit_net(struct net *net) 7243 7193 { 7244 - nfnl_lock(NFNL_SUBSYS_NFTABLES); 7194 + mutex_lock(&net->nft.commit_mutex); 7245 7195 if (!list_empty(&net->nft.commit_list)) 7246 7196 __nf_tables_abort(net); 7247 7197 __nft_release_tables(net); 7248 - nfnl_unlock(NFNL_SUBSYS_NFTABLES); 7198 + mutex_unlock(&net->nft.commit_mutex); 7249 7199 WARN_ON_ONCE(!list_empty(&net->nft.tables)); 7250 7200 } 7251 7201
+3 -7
net/netfilter/nfnetlink.c
··· 350 350 return kfree_skb(skb); 351 351 } 352 352 353 + nfnl_unlock(subsys_id); 354 + 353 355 while (skb->len >= nlmsg_total_size(0)) { 354 356 int msglen, type; 355 357 ··· 473 471 } 474 472 done: 475 473 if (status & NFNL_BATCH_REPLAY) { 476 - const struct nfnetlink_subsystem *ss2; 477 - 478 - ss2 = nfnl_dereference_protected(subsys_id); 479 - if (ss2 == ss) 480 - ss->abort(net, oskb); 474 + ss->abort(net, oskb); 481 475 nfnl_err_reset(&err_list); 482 - nfnl_unlock(subsys_id); 483 476 kfree_skb(skb); 484 477 module_put(ss->owner); 485 478 goto replay; ··· 494 497 ss->cleanup(net); 495 498 496 499 nfnl_err_deliver(&err_list, oskb); 497 - nfnl_unlock(subsys_id); 498 500 kfree_skb(skb); 499 501 module_put(ss->owner); 500 502 }
+2 -2
net/netfilter/nft_chain_filter.c
··· 322 322 if (!ctx.net) 323 323 return NOTIFY_DONE; 324 324 325 - nfnl_lock(NFNL_SUBSYS_NFTABLES); 325 + mutex_lock(&ctx.net->nft.commit_mutex); 326 326 list_for_each_entry(table, &ctx.net->nft.tables, list) { 327 327 if (table->family != NFPROTO_NETDEV) 328 328 continue; ··· 337 337 nft_netdev_event(event, dev, &ctx); 338 338 } 339 339 } 340 - nfnl_unlock(NFNL_SUBSYS_NFTABLES); 340 + mutex_unlock(&ctx.net->nft.commit_mutex); 341 341 put_net(ctx.net); 342 342 343 343 return NOTIFY_DONE;
+2
net/netfilter/nft_dynset.c
··· 118 118 u64 timeout; 119 119 int err; 120 120 121 + lockdep_assert_held(&ctx->net->nft.commit_mutex); 122 + 121 123 if (tb[NFTA_DYNSET_SET_NAME] == NULL || 122 124 tb[NFTA_DYNSET_OP] == NULL || 123 125 tb[NFTA_DYNSET_SREG_KEY] == NULL)