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

netfilter: nf_tables: enable conntrack if NAT chain is registered

Register conntrack hooks if the user adds NAT chains. Users get confused
with the existing behaviour since they will see no packets hitting this
chain until they add the first rule that refers to conntrack.

This patch adds new ->init() and ->free() indirections to chain types
that can be used by NAT chains to invoke the conntrack dependency.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

+45 -7
+4
include/net/netfilter/nf_tables.h
··· 884 884 * @owner: module owner 885 885 * @hook_mask: mask of valid hooks 886 886 * @hooks: array of hook functions 887 + * @init: chain initialization function 888 + * @free: chain release function 887 889 */ 888 890 struct nft_chain_type { 889 891 const char *name; ··· 894 892 struct module *owner; 895 893 unsigned int hook_mask; 896 894 nf_hookfn *hooks[NF_MAX_HOOKS]; 895 + int (*init)(struct nft_ctx *ctx); 896 + void (*free)(struct nft_ctx *ctx); 897 897 }; 898 898 899 899 int nft_chain_validate_dependency(const struct nft_chain *chain,
+12
net/ipv4/netfilter/nft_chain_nat_ipv4.c
··· 67 67 return nf_nat_ipv4_local_fn(priv, skb, state, nft_nat_do_chain); 68 68 } 69 69 70 + static int nft_nat_ipv4_init(struct nft_ctx *ctx) 71 + { 72 + return nf_ct_netns_get(ctx->net, ctx->family); 73 + } 74 + 75 + static void nft_nat_ipv4_free(struct nft_ctx *ctx) 76 + { 77 + nf_ct_netns_put(ctx->net, ctx->family); 78 + } 79 + 70 80 static const struct nft_chain_type nft_chain_nat_ipv4 = { 71 81 .name = "nat", 72 82 .type = NFT_CHAIN_T_NAT, ··· 92 82 [NF_INET_LOCAL_OUT] = nft_nat_ipv4_local_fn, 93 83 [NF_INET_LOCAL_IN] = nft_nat_ipv4_fn, 94 84 }, 85 + .init = nft_nat_ipv4_init, 86 + .free = nft_nat_ipv4_free, 95 87 }; 96 88 97 89 static int __init nft_chain_nat_init(void)
+12
net/ipv6/netfilter/nft_chain_nat_ipv6.c
··· 65 65 return nf_nat_ipv6_local_fn(priv, skb, state, nft_nat_do_chain); 66 66 } 67 67 68 + static int nft_nat_ipv6_init(struct nft_ctx *ctx) 69 + { 70 + return nf_ct_netns_get(ctx->net, ctx->family); 71 + } 72 + 73 + static void nft_nat_ipv6_free(struct nft_ctx *ctx) 74 + { 75 + nf_ct_netns_put(ctx->net, ctx->family); 76 + } 77 + 68 78 static const struct nft_chain_type nft_chain_nat_ipv6 = { 69 79 .name = "nat", 70 80 .type = NFT_CHAIN_T_NAT, ··· 90 80 [NF_INET_LOCAL_OUT] = nft_nat_ipv6_local_fn, 91 81 [NF_INET_LOCAL_IN] = nft_nat_ipv6_fn, 92 82 }, 83 + .init = nft_nat_ipv6_init, 84 + .free = nft_nat_ipv6_free, 93 85 }; 94 86 95 87 static int __init nft_chain_nat_ipv6_init(void)
+17 -7
net/netfilter/nf_tables_api.c
··· 1211 1211 rcu_assign_pointer(chain->stats, newstats); 1212 1212 } 1213 1213 1214 - static void nf_tables_chain_destroy(struct nft_chain *chain) 1214 + static void nf_tables_chain_destroy(struct nft_ctx *ctx) 1215 1215 { 1216 + struct nft_chain *chain = ctx->chain; 1217 + 1216 1218 BUG_ON(chain->use > 0); 1217 1219 1218 1220 if (nft_is_base_chain(chain)) { 1219 1221 struct nft_base_chain *basechain = nft_base_chain(chain); 1220 1222 1223 + if (basechain->type->free) 1224 + basechain->type->free(ctx); 1221 1225 module_put(basechain->type->owner); 1222 1226 free_percpu(basechain->stats); 1223 1227 if (basechain->stats) ··· 1358 1354 } 1359 1355 1360 1356 basechain->type = hook.type; 1357 + if (basechain->type->init) 1358 + basechain->type->init(ctx); 1359 + 1361 1360 chain = &basechain->chain; 1362 1361 1363 1362 ops = &basechain->ops; ··· 1381 1374 if (chain == NULL) 1382 1375 return -ENOMEM; 1383 1376 } 1377 + ctx->chain = chain; 1378 + 1384 1379 INIT_LIST_HEAD(&chain->rules); 1385 1380 chain->handle = nf_tables_alloc_handle(table); 1386 1381 chain->table = table; ··· 1396 1387 if (err < 0) 1397 1388 goto err1; 1398 1389 1399 - ctx->chain = chain; 1400 1390 err = nft_trans_chain_add(ctx, NFT_MSG_NEWCHAIN); 1401 1391 if (err < 0) 1402 1392 goto err2; ··· 1407 1399 err2: 1408 1400 nf_tables_unregister_hook(net, table, chain); 1409 1401 err1: 1410 - nf_tables_chain_destroy(chain); 1402 + nf_tables_chain_destroy(ctx); 1411 1403 1412 1404 return err; 1413 1405 } ··· 5686 5678 nf_tables_table_destroy(&trans->ctx); 5687 5679 break; 5688 5680 case NFT_MSG_DELCHAIN: 5689 - nf_tables_chain_destroy(trans->ctx.chain); 5681 + nf_tables_chain_destroy(&trans->ctx); 5690 5682 break; 5691 5683 case NFT_MSG_DELRULE: 5692 5684 nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans)); ··· 5857 5849 nf_tables_table_destroy(&trans->ctx); 5858 5850 break; 5859 5851 case NFT_MSG_NEWCHAIN: 5860 - nf_tables_chain_destroy(trans->ctx.chain); 5852 + nf_tables_chain_destroy(&trans->ctx); 5861 5853 break; 5862 5854 case NFT_MSG_NEWRULE: 5863 5855 nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans)); ··· 6507 6499 } 6508 6500 list_del(&ctx->chain->list); 6509 6501 ctx->table->use--; 6510 - nf_tables_chain_destroy(ctx->chain); 6502 + nf_tables_chain_destroy(ctx); 6511 6503 6512 6504 return 0; 6513 6505 } ··· 6523 6515 struct nft_set *set, *ns; 6524 6516 struct nft_ctx ctx = { 6525 6517 .net = net, 6518 + .family = NFPROTO_NETDEV, 6526 6519 }; 6527 6520 6528 6521 list_for_each_entry_safe(table, nt, &net->nft.tables, list) { ··· 6560 6551 nft_obj_destroy(obj); 6561 6552 } 6562 6553 list_for_each_entry_safe(chain, nc, &table->chains, list) { 6554 + ctx.chain = chain; 6563 6555 list_del(&chain->list); 6564 6556 table->use--; 6565 - nf_tables_chain_destroy(chain); 6557 + nf_tables_chain_destroy(&ctx); 6566 6558 } 6567 6559 list_del(&table->list); 6568 6560 nf_tables_table_destroy(&ctx);