summaryrefslogtreecommitdiffstats
path: root/netfilter-nf_nat-fix-oops-on-netns-removal.patch
diff options
context:
space:
mode:
Diffstat (limited to 'netfilter-nf_nat-fix-oops-on-netns-removal.patch')
-rw-r--r--netfilter-nf_nat-fix-oops-on-netns-removal.patch98
1 files changed, 98 insertions, 0 deletions
diff --git a/netfilter-nf_nat-fix-oops-on-netns-removal.patch b/netfilter-nf_nat-fix-oops-on-netns-removal.patch
new file mode 100644
index 000000000..4cd521895
--- /dev/null
+++ b/netfilter-nf_nat-fix-oops-on-netns-removal.patch
@@ -0,0 +1,98 @@
+Bugzilla: 1015989
+Upstream-status: 3.16-rc3 and queued for stable
+
+From 945b2b2d259d1a4364a2799e80e8ff32f8c6ee6f Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Sat, 7 Jun 2014 21:17:04 +0200
+Subject: [PATCH] netfilter: nf_nat: fix oops on netns removal
+
+Quoting Samu Kallio:
+
+ Basically what's happening is, during netns cleanup,
+ nf_nat_net_exit gets called before ipv4_net_exit. As I understand
+ it, nf_nat_net_exit is supposed to kill any conntrack entries which
+ have NAT context (through nf_ct_iterate_cleanup), but for some
+ reason this doesn't happen (perhaps something else is still holding
+ refs to those entries?).
+
+ When ipv4_net_exit is called, conntrack entries (including those
+ with NAT context) are cleaned up, but the
+ nat_bysource hashtable is long gone - freed in nf_nat_net_exit. The
+ bug happens when attempting to free a conntrack entry whose NAT hash
+ 'prev' field points to a slot in the freed hash table (head for that
+ bin).
+
+We ignore conntracks with null nat bindings. But this is wrong,
+as these are in bysource hash table as well.
+
+Restore nat-cleaning for the netns-is-being-removed case.
+
+bug:
+https://bugzilla.kernel.org/show_bug.cgi?id=65191
+
+Fixes: c2d421e1718 ('netfilter: nf_nat: fix race when unloading protocol modules')
+Reported-by: Samu Kallio <samu.kallio@aberdeencloud.com>
+Debugged-by: Samu Kallio <samu.kallio@aberdeencloud.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Tested-by: Samu Kallio <samu.kallio@aberdeencloud.com>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+ net/netfilter/nf_nat_core.c | 35 ++++++++++++++++++++++++++++++++++-
+ 1 file changed, 34 insertions(+), 1 deletion(-)
+
+diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c
+index 09096a670c45..a49907b1dabc 100644
+--- a/net/netfilter/nf_nat_core.c
++++ b/net/netfilter/nf_nat_core.c
+@@ -525,6 +525,39 @@ static int nf_nat_proto_remove(struct nf_conn *i, void *data)
+ return i->status & IPS_NAT_MASK ? 1 : 0;
+ }
+
++static int nf_nat_proto_clean(struct nf_conn *ct, void *data)
++{
++ struct nf_conn_nat *nat = nfct_nat(ct);
++
++ if (nf_nat_proto_remove(ct, data))
++ return 1;
++
++ if (!nat || !nat->ct)
++ return 0;
++
++ /* This netns is being destroyed, and conntrack has nat null binding.
++ * Remove it from bysource hash, as the table will be freed soon.
++ *
++ * Else, when the conntrack is destoyed, nf_nat_cleanup_conntrack()
++ * will delete entry from already-freed table.
++ */
++ if (!del_timer(&ct->timeout))
++ return 1;
++
++ spin_lock_bh(&nf_nat_lock);
++ hlist_del_rcu(&nat->bysource);
++ ct->status &= ~IPS_NAT_DONE_MASK;
++ nat->ct = NULL;
++ spin_unlock_bh(&nf_nat_lock);
++
++ add_timer(&ct->timeout);
++
++ /* don't delete conntrack. Although that would make things a lot
++ * simpler, we'd end up flushing all conntracks on nat rmmod.
++ */
++ return 0;
++}
++
+ static void nf_nat_l4proto_clean(u8 l3proto, u8 l4proto)
+ {
+ struct nf_nat_proto_clean clean = {
+@@ -795,7 +828,7 @@ static void __net_exit nf_nat_net_exit(struct net *net)
+ {
+ struct nf_nat_proto_clean clean = {};
+
+- nf_ct_iterate_cleanup(net, &nf_nat_proto_remove, &clean, 0, 0);
++ nf_ct_iterate_cleanup(net, nf_nat_proto_clean, &clean, 0, 0);
+ synchronize_rcu();
+ nf_ct_free_hashtable(net->ct.nat_bysource, net->ct.nat_htable_size);
+ }
+--
+1.9.3
+