netfilter: nf_tables: release objects on netns destruction
authorPablo Neira Ayuso <pablo@netfilter.org>
Tue, 15 Dec 2015 18:39:32 +0000 (19:39 +0100)
committerPablo Neira Ayuso <pablo@netfilter.org>
Mon, 28 Dec 2015 17:34:35 +0000 (18:34 +0100)
We have to release the existing objects on netns removal otherwise we
leak them. Chains are unregistered in first place to make sure no
packets are walking on our rules and sets anymore.

The object release happens by when we unregister the family via
nft_release_afinfo() which is called from nft_unregister_afinfo() from
the corresponding __net_exit path in every family.

Reported-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/net/netfilter/nf_tables.h
net/bridge/netfilter/nf_tables_bridge.c
net/ipv4/netfilter/nf_tables_arp.c
net/ipv4/netfilter/nf_tables_ipv4.c
net/ipv6/netfilter/nf_tables_ipv6.c
net/netfilter/nf_tables_api.c
net/netfilter/nf_tables_inet.c
net/netfilter/nf_tables_netdev.c

index b313cda4919462532da30a1603fe6374eeca9af0..a50f139ce087b9e3933d9a699d8390e04cabe19d 100644 (file)
@@ -880,7 +880,7 @@ struct nft_af_info {
 };
 
 int nft_register_afinfo(struct net *, struct nft_af_info *);
-void nft_unregister_afinfo(struct nft_af_info *);
+void nft_unregister_afinfo(struct net *, struct nft_af_info *);
 
 int nft_register_chain_type(const struct nf_chain_type *);
 void nft_unregister_chain_type(const struct nf_chain_type *);
index 62f6b1b195897fd2d982b4849c7e139dc12ddfc2..7fcdd7261d880276f5d21a7b5bbdaf4622dbdfbf 100644 (file)
@@ -141,7 +141,7 @@ err:
 
 static void nf_tables_bridge_exit_net(struct net *net)
 {
-       nft_unregister_afinfo(net->nft.bridge);
+       nft_unregister_afinfo(net, net->nft.bridge);
        kfree(net->nft.bridge);
 }
 
index 9d09d4f59545ccaaf0e9437bb36a8274a50bed4f..cd84d4295a2027642f46523fd105068144abfdfd 100644 (file)
@@ -57,7 +57,7 @@ err:
 
 static void nf_tables_arp_exit_net(struct net *net)
 {
-       nft_unregister_afinfo(net->nft.arp);
+       nft_unregister_afinfo(net, net->nft.arp);
        kfree(net->nft.arp);
 }
 
index ca9dc3c46c4fd6e0af9ba01a0afc5e00f963374f..e44ba3b12fbb03ac4f34626c35fa31a23f3633fa 100644 (file)
@@ -78,7 +78,7 @@ err:
 
 static void nf_tables_ipv4_exit_net(struct net *net)
 {
-       nft_unregister_afinfo(net->nft.ipv4);
+       nft_unregister_afinfo(net, net->nft.ipv4);
        kfree(net->nft.ipv4);
 }
 
index 120ea9131be030d6849d54275179c0c8e36429f6..30b22f4dff55e7cfc5f83a3549192f54615c8701 100644 (file)
@@ -77,7 +77,7 @@ err:
 
 static void nf_tables_ipv6_exit_net(struct net *net)
 {
-       nft_unregister_afinfo(net->nft.ipv6);
+       nft_unregister_afinfo(net, net->nft.ipv6);
        kfree(net->nft.ipv6);
 }
 
index 4a23f77c363a3009462f2404bede01439ba53edf..8522731102756c2afe38f04d7147c9e8e4216f0e 100644 (file)
@@ -41,6 +41,8 @@ int nft_register_afinfo(struct net *net, struct nft_af_info *afi)
 }
 EXPORT_SYMBOL_GPL(nft_register_afinfo);
 
+static void __nft_release_afinfo(struct net *net, struct nft_af_info *afi);
+
 /**
  *     nft_unregister_afinfo - unregister nf_tables address family info
  *
@@ -48,9 +50,10 @@ EXPORT_SYMBOL_GPL(nft_register_afinfo);
  *
  *     Unregister the address family for use with nf_tables.
  */
-void nft_unregister_afinfo(struct nft_af_info *afi)
+void nft_unregister_afinfo(struct net *net, struct nft_af_info *afi)
 {
        nfnl_lock(NFNL_SUBSYS_NFTABLES);
+       __nft_release_afinfo(net, afi);
        list_del_rcu(&afi->list);
        nfnl_unlock(NFNL_SUBSYS_NFTABLES);
 }
@@ -4579,7 +4582,7 @@ int nft_data_dump(struct sk_buff *skb, int attr, const struct nft_data *data,
 }
 EXPORT_SYMBOL_GPL(nft_data_dump);
 
-static int nf_tables_init_net(struct net *net)
+static int __net_init nf_tables_init_net(struct net *net)
 {
        INIT_LIST_HEAD(&net->nft.af_info);
        INIT_LIST_HEAD(&net->nft.commit_list);
@@ -4587,6 +4590,46 @@ static int nf_tables_init_net(struct net *net)
        return 0;
 }
 
+/* Called by nft_unregister_afinfo() from __net_exit path, nfnl_lock is held. */
+static void __nft_release_afinfo(struct net *net, struct nft_af_info *afi)
+{
+       struct nft_table *table, *nt;
+       struct nft_chain *chain, *nc;
+       struct nft_rule *rule, *nr;
+       struct nft_set *set, *ns;
+       struct nft_ctx ctx = {
+               .net    = net,
+               .afi    = afi,
+       };
+
+       list_for_each_entry_safe(table, nt, &afi->tables, list) {
+               list_for_each_entry(chain, &table->chains, list)
+                       nf_tables_unregister_hooks(table, chain, afi->nops);
+               /* No packets are walking on these chains anymore. */
+               ctx.table = table;
+               list_for_each_entry(chain, &table->chains, list) {
+                       ctx.chain = chain;
+                       list_for_each_entry_safe(rule, nr, &chain->rules, list) {
+                               list_del(&rule->list);
+                               chain->use--;
+                               nf_tables_rule_destroy(&ctx, rule);
+                       }
+               }
+               list_for_each_entry_safe(set, ns, &table->sets, list) {
+                       list_del(&set->list);
+                       table->use--;
+                       nft_set_destroy(set);
+               }
+               list_for_each_entry_safe(chain, nc, &table->chains, list) {
+                       list_del(&chain->list);
+                       table->use--;
+                       nf_tables_chain_destroy(chain);
+               }
+               list_del(&table->list);
+               nf_tables_table_destroy(&ctx);
+       }
+}
+
 static struct pernet_operations nf_tables_net_ops = {
        .init   = nf_tables_init_net,
 };
index 9dd2d216cfc14c7e3e2166757ae65924341bd116..6b5f76295d3d534f3c4255d037ea0b30bfdbb22f 100644 (file)
@@ -57,7 +57,7 @@ err:
 
 static void __net_exit nf_tables_inet_exit_net(struct net *net)
 {
-       nft_unregister_afinfo(net->nft.inet);
+       nft_unregister_afinfo(net, net->nft.inet);
        kfree(net->nft.inet);
 }
 
index 7b9c053ba75072276ee9227ea8d1e67ce3307715..2bfd1fbccec8f526a814118cdeb0872a60eb1ee4 100644 (file)
@@ -139,7 +139,7 @@ err:
 
 static void nf_tables_netdev_exit_net(struct net *net)
 {
-       nft_unregister_afinfo(net->nft.netdev);
+       nft_unregister_afinfo(net, net->nft.netdev);
        kfree(net->nft.netdev);
 }