From 56be9f4a9a69072cdaa38473155d609aea1ea6e4 Mon Sep 17 00:00:00 2001
From: Jo-Philipp Wich <jow@openwrt.org>
Date: Sat, 9 Aug 2014 14:36:10 +0000
Subject: [PATCH] netfilter: support /proc conntrack flushing of specific ip
 addresses

Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>

SVN-Revision: 42092
---
 .../604-netfilter_conntrack_flush.patch       | 57 +++++++++++++++----
 .../604-netfilter_conntrack_flush.patch       | 57 +++++++++++++++----
 .../604-netfilter_conntrack_flush.patch       | 57 +++++++++++++++----
 .../604-netfilter_conntrack_flush.patch       | 57 +++++++++++++++----
 .../604-netfilter_conntrack_flush.patch       | 57 +++++++++++++++----
 .../604-netfilter_conntrack_flush.patch       | 57 +++++++++++++++----
 6 files changed, 282 insertions(+), 60 deletions(-)

diff --git a/target/linux/generic/patches-3.10/604-netfilter_conntrack_flush.patch b/target/linux/generic/patches-3.10/604-netfilter_conntrack_flush.patch
index ecd46b6a2b..1b5e2abbec 100644
--- a/target/linux/generic/patches-3.10/604-netfilter_conntrack_flush.patch
+++ b/target/linux/generic/patches-3.10/604-netfilter_conntrack_flush.patch
@@ -1,12 +1,35 @@
 --- a/net/netfilter/nf_conntrack_standalone.c
 +++ b/net/netfilter/nf_conntrack_standalone.c
-@@ -268,10 +268,34 @@ static int ct_open(struct inode *inode,
+@@ -17,6 +17,7 @@
+ #include <linux/percpu.h>
+ #include <linux/netdevice.h>
+ #include <linux/security.h>
++#include <linux/inet.h>
+ #include <net/net_namespace.h>
+ #ifdef CONFIG_SYSCTL
+ #include <linux/sysctl.h>
+@@ -268,10 +269,63 @@ static int ct_open(struct inode *inode,
  			sizeof(struct ct_iter_state));
  }
  
-+static int kill_all(struct nf_conn *i, void *data)
++struct kill_request {
++	u16 family;
++	union nf_inet_addr addr;
++};
++
++static int kill_matching(struct nf_conn *i, void *data)
 +{
-+    return 1;
++	struct kill_request *kr = data;
++	struct nf_conntrack_tuple *t = &i->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
++
++	if (!kr->family)
++		return 1;
++
++	if (t->src.l3num != kr->family)
++		return 0;
++
++	return (nf_inet_addr_cmp(&kr->addr, &t->src.u3) ||
++	        nf_inet_addr_cmp(&kr->addr, &t->dst.u3));
 +}
 +
 +static ssize_t ct_file_write(struct file *file, const char __user *buf,
@@ -14,16 +37,30 @@
 +{
 +	struct seq_file *seq = file->private_data;
 +	struct net *net = seq_file_net(seq);
++	struct kill_request kr = { };
++	char req[INET6_ADDRSTRLEN] = { };
 +
-+	if (count) {
-+		char c;
++	if (count == 0)
++		return 0;
 +
-+		if (get_user(c, buf))
-+			return -EFAULT;
++	if (count >= INET6_ADDRSTRLEN)
++		count = INET6_ADDRSTRLEN - 1;
 +
-+		if (c == 'f')
-+			nf_ct_iterate_cleanup(net, kill_all, NULL);
++	if (copy_from_user(req, buf, count))
++		return -EFAULT;
++
++	if (strnchr(req, count, ':')) {
++		kr.family = AF_INET6;
++		if (!in6_pton(req, count, (void *)&kr.addr, '\n', NULL))
++			return -EINVAL;
++	} else if (strnchr(req, count, '.')) {
++		kr.family = AF_INET;
++		if (!in4_pton(req, count, (void *)&kr.addr, '\n', NULL))
++			return -EINVAL;
 +	}
++
++	nf_ct_iterate_cleanup(net, kill_matching, &kr);
++
 +	return count;
 +}
 +
@@ -35,7 +72,7 @@
  	.llseek  = seq_lseek,
  	.release = seq_release_net,
  };
-@@ -373,7 +397,7 @@ static int nf_conntrack_standalone_init_
+@@ -373,7 +427,7 @@ static int nf_conntrack_standalone_init_
  {
  	struct proc_dir_entry *pde;
  
diff --git a/target/linux/generic/patches-3.13/604-netfilter_conntrack_flush.patch b/target/linux/generic/patches-3.13/604-netfilter_conntrack_flush.patch
index 984aed0e41..1b5e2abbec 100644
--- a/target/linux/generic/patches-3.13/604-netfilter_conntrack_flush.patch
+++ b/target/linux/generic/patches-3.13/604-netfilter_conntrack_flush.patch
@@ -1,12 +1,35 @@
 --- a/net/netfilter/nf_conntrack_standalone.c
 +++ b/net/netfilter/nf_conntrack_standalone.c
-@@ -268,10 +268,34 @@ static int ct_open(struct inode *inode,
+@@ -17,6 +17,7 @@
+ #include <linux/percpu.h>
+ #include <linux/netdevice.h>
+ #include <linux/security.h>
++#include <linux/inet.h>
+ #include <net/net_namespace.h>
+ #ifdef CONFIG_SYSCTL
+ #include <linux/sysctl.h>
+@@ -268,10 +269,63 @@ static int ct_open(struct inode *inode,
  			sizeof(struct ct_iter_state));
  }
  
-+static int kill_all(struct nf_conn *i, void *data)
++struct kill_request {
++	u16 family;
++	union nf_inet_addr addr;
++};
++
++static int kill_matching(struct nf_conn *i, void *data)
 +{
-+    return 1;
++	struct kill_request *kr = data;
++	struct nf_conntrack_tuple *t = &i->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
++
++	if (!kr->family)
++		return 1;
++
++	if (t->src.l3num != kr->family)
++		return 0;
++
++	return (nf_inet_addr_cmp(&kr->addr, &t->src.u3) ||
++	        nf_inet_addr_cmp(&kr->addr, &t->dst.u3));
 +}
 +
 +static ssize_t ct_file_write(struct file *file, const char __user *buf,
@@ -14,16 +37,30 @@
 +{
 +	struct seq_file *seq = file->private_data;
 +	struct net *net = seq_file_net(seq);
++	struct kill_request kr = { };
++	char req[INET6_ADDRSTRLEN] = { };
 +
-+	if (count) {
-+		char c;
++	if (count == 0)
++		return 0;
 +
-+		if (get_user(c, buf))
-+			return -EFAULT;
++	if (count >= INET6_ADDRSTRLEN)
++		count = INET6_ADDRSTRLEN - 1;
 +
-+		if (c == 'f')
-+			nf_ct_iterate_cleanup(net, kill_all, NULL, 0, 0);
++	if (copy_from_user(req, buf, count))
++		return -EFAULT;
++
++	if (strnchr(req, count, ':')) {
++		kr.family = AF_INET6;
++		if (!in6_pton(req, count, (void *)&kr.addr, '\n', NULL))
++			return -EINVAL;
++	} else if (strnchr(req, count, '.')) {
++		kr.family = AF_INET;
++		if (!in4_pton(req, count, (void *)&kr.addr, '\n', NULL))
++			return -EINVAL;
 +	}
++
++	nf_ct_iterate_cleanup(net, kill_matching, &kr);
++
 +	return count;
 +}
 +
@@ -35,7 +72,7 @@
  	.llseek  = seq_lseek,
  	.release = seq_release_net,
  };
-@@ -373,7 +397,7 @@ static int nf_conntrack_standalone_init_
+@@ -373,7 +427,7 @@ static int nf_conntrack_standalone_init_
  {
  	struct proc_dir_entry *pde;
  
diff --git a/target/linux/generic/patches-3.14/604-netfilter_conntrack_flush.patch b/target/linux/generic/patches-3.14/604-netfilter_conntrack_flush.patch
index 984aed0e41..1b5e2abbec 100644
--- a/target/linux/generic/patches-3.14/604-netfilter_conntrack_flush.patch
+++ b/target/linux/generic/patches-3.14/604-netfilter_conntrack_flush.patch
@@ -1,12 +1,35 @@
 --- a/net/netfilter/nf_conntrack_standalone.c
 +++ b/net/netfilter/nf_conntrack_standalone.c
-@@ -268,10 +268,34 @@ static int ct_open(struct inode *inode,
+@@ -17,6 +17,7 @@
+ #include <linux/percpu.h>
+ #include <linux/netdevice.h>
+ #include <linux/security.h>
++#include <linux/inet.h>
+ #include <net/net_namespace.h>
+ #ifdef CONFIG_SYSCTL
+ #include <linux/sysctl.h>
+@@ -268,10 +269,63 @@ static int ct_open(struct inode *inode,
  			sizeof(struct ct_iter_state));
  }
  
-+static int kill_all(struct nf_conn *i, void *data)
++struct kill_request {
++	u16 family;
++	union nf_inet_addr addr;
++};
++
++static int kill_matching(struct nf_conn *i, void *data)
 +{
-+    return 1;
++	struct kill_request *kr = data;
++	struct nf_conntrack_tuple *t = &i->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
++
++	if (!kr->family)
++		return 1;
++
++	if (t->src.l3num != kr->family)
++		return 0;
++
++	return (nf_inet_addr_cmp(&kr->addr, &t->src.u3) ||
++	        nf_inet_addr_cmp(&kr->addr, &t->dst.u3));
 +}
 +
 +static ssize_t ct_file_write(struct file *file, const char __user *buf,
@@ -14,16 +37,30 @@
 +{
 +	struct seq_file *seq = file->private_data;
 +	struct net *net = seq_file_net(seq);
++	struct kill_request kr = { };
++	char req[INET6_ADDRSTRLEN] = { };
 +
-+	if (count) {
-+		char c;
++	if (count == 0)
++		return 0;
 +
-+		if (get_user(c, buf))
-+			return -EFAULT;
++	if (count >= INET6_ADDRSTRLEN)
++		count = INET6_ADDRSTRLEN - 1;
 +
-+		if (c == 'f')
-+			nf_ct_iterate_cleanup(net, kill_all, NULL, 0, 0);
++	if (copy_from_user(req, buf, count))
++		return -EFAULT;
++
++	if (strnchr(req, count, ':')) {
++		kr.family = AF_INET6;
++		if (!in6_pton(req, count, (void *)&kr.addr, '\n', NULL))
++			return -EINVAL;
++	} else if (strnchr(req, count, '.')) {
++		kr.family = AF_INET;
++		if (!in4_pton(req, count, (void *)&kr.addr, '\n', NULL))
++			return -EINVAL;
 +	}
++
++	nf_ct_iterate_cleanup(net, kill_matching, &kr);
++
 +	return count;
 +}
 +
@@ -35,7 +72,7 @@
  	.llseek  = seq_lseek,
  	.release = seq_release_net,
  };
-@@ -373,7 +397,7 @@ static int nf_conntrack_standalone_init_
+@@ -373,7 +427,7 @@ static int nf_conntrack_standalone_init_
  {
  	struct proc_dir_entry *pde;
  
diff --git a/target/linux/generic/patches-3.3/604-netfilter_conntrack_flush.patch b/target/linux/generic/patches-3.3/604-netfilter_conntrack_flush.patch
index 3ee5e49829..3f510a9ad2 100644
--- a/target/linux/generic/patches-3.3/604-netfilter_conntrack_flush.patch
+++ b/target/linux/generic/patches-3.3/604-netfilter_conntrack_flush.patch
@@ -1,12 +1,35 @@
 --- a/net/netfilter/nf_conntrack_standalone.c
 +++ b/net/netfilter/nf_conntrack_standalone.c
-@@ -267,10 +267,34 @@ static int ct_open(struct inode *inode,
+@@ -16,6 +16,7 @@
+ #include <linux/percpu.h>
+ #include <linux/netdevice.h>
+ #include <linux/security.h>
++#include <linux/inet.h>
+ #include <net/net_namespace.h>
+ #ifdef CONFIG_SYSCTL
+ #include <linux/sysctl.h>
+@@ -267,10 +268,63 @@ static int ct_open(struct inode *inode,
  			sizeof(struct ct_iter_state));
  }
  
-+static int kill_all(struct nf_conn *i, void *data)
++struct kill_request {
++	u16 family;
++	union nf_inet_addr addr;
++};
++
++static int kill_matching(struct nf_conn *i, void *data)
 +{
-+    return 1;
++	struct kill_request *kr = data;
++	struct nf_conntrack_tuple *t = &i->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
++
++	if (!kr->family)
++		return 1;
++
++	if (t->src.l3num != kr->family)
++		return 0;
++
++	return (nf_inet_addr_cmp(&kr->addr, &t->src.u3) ||
++	        nf_inet_addr_cmp(&kr->addr, &t->dst.u3));
 +}
 +
 +static ssize_t ct_file_write(struct file *file, const char __user *buf,
@@ -14,16 +37,30 @@
 +{
 +	struct seq_file *seq = file->private_data;
 +	struct net *net = seq_file_net(seq);
++	struct kill_request kr = { };
++	char req[INET6_ADDRSTRLEN] = { };
 +
-+	if (count) {
-+		char c;
++	if (count == 0)
++		return 0;
 +
-+		if (get_user(c, buf))
-+			return -EFAULT;
++	if (count >= INET6_ADDRSTRLEN)
++		count = INET6_ADDRSTRLEN - 1;
 +
-+		if (c == 'f')
-+			nf_ct_iterate_cleanup(net, kill_all, NULL);
++	if (copy_from_user(req, buf, count))
++		return -EFAULT;
++
++	if (strnchr(req, count, ':')) {
++		kr.family = AF_INET6;
++		if (!in6_pton(req, count, (void *)&kr.addr, '\n', NULL))
++			return -EINVAL;
++	} else if (strnchr(req, count, '.')) {
++		kr.family = AF_INET;
++		if (!in4_pton(req, count, (void *)&kr.addr, '\n', NULL))
++			return -EINVAL;
 +	}
++
++	nf_ct_iterate_cleanup(net, kill_matching, &kr);
++
 +	return count;
 +}
 +
@@ -35,7 +72,7 @@
  	.llseek  = seq_lseek,
  	.release = seq_release_net,
  };
-@@ -372,7 +396,7 @@ static int nf_conntrack_standalone_init_
+@@ -372,7 +426,7 @@ static int nf_conntrack_standalone_init_
  {
  	struct proc_dir_entry *pde;
  
diff --git a/target/linux/generic/patches-3.8/604-netfilter_conntrack_flush.patch b/target/linux/generic/patches-3.8/604-netfilter_conntrack_flush.patch
index 3ee5e49829..3f510a9ad2 100644
--- a/target/linux/generic/patches-3.8/604-netfilter_conntrack_flush.patch
+++ b/target/linux/generic/patches-3.8/604-netfilter_conntrack_flush.patch
@@ -1,12 +1,35 @@
 --- a/net/netfilter/nf_conntrack_standalone.c
 +++ b/net/netfilter/nf_conntrack_standalone.c
-@@ -267,10 +267,34 @@ static int ct_open(struct inode *inode,
+@@ -16,6 +16,7 @@
+ #include <linux/percpu.h>
+ #include <linux/netdevice.h>
+ #include <linux/security.h>
++#include <linux/inet.h>
+ #include <net/net_namespace.h>
+ #ifdef CONFIG_SYSCTL
+ #include <linux/sysctl.h>
+@@ -267,10 +268,63 @@ static int ct_open(struct inode *inode,
  			sizeof(struct ct_iter_state));
  }
  
-+static int kill_all(struct nf_conn *i, void *data)
++struct kill_request {
++	u16 family;
++	union nf_inet_addr addr;
++};
++
++static int kill_matching(struct nf_conn *i, void *data)
 +{
-+    return 1;
++	struct kill_request *kr = data;
++	struct nf_conntrack_tuple *t = &i->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
++
++	if (!kr->family)
++		return 1;
++
++	if (t->src.l3num != kr->family)
++		return 0;
++
++	return (nf_inet_addr_cmp(&kr->addr, &t->src.u3) ||
++	        nf_inet_addr_cmp(&kr->addr, &t->dst.u3));
 +}
 +
 +static ssize_t ct_file_write(struct file *file, const char __user *buf,
@@ -14,16 +37,30 @@
 +{
 +	struct seq_file *seq = file->private_data;
 +	struct net *net = seq_file_net(seq);
++	struct kill_request kr = { };
++	char req[INET6_ADDRSTRLEN] = { };
 +
-+	if (count) {
-+		char c;
++	if (count == 0)
++		return 0;
 +
-+		if (get_user(c, buf))
-+			return -EFAULT;
++	if (count >= INET6_ADDRSTRLEN)
++		count = INET6_ADDRSTRLEN - 1;
 +
-+		if (c == 'f')
-+			nf_ct_iterate_cleanup(net, kill_all, NULL);
++	if (copy_from_user(req, buf, count))
++		return -EFAULT;
++
++	if (strnchr(req, count, ':')) {
++		kr.family = AF_INET6;
++		if (!in6_pton(req, count, (void *)&kr.addr, '\n', NULL))
++			return -EINVAL;
++	} else if (strnchr(req, count, '.')) {
++		kr.family = AF_INET;
++		if (!in4_pton(req, count, (void *)&kr.addr, '\n', NULL))
++			return -EINVAL;
 +	}
++
++	nf_ct_iterate_cleanup(net, kill_matching, &kr);
++
 +	return count;
 +}
 +
@@ -35,7 +72,7 @@
  	.llseek  = seq_lseek,
  	.release = seq_release_net,
  };
-@@ -372,7 +396,7 @@ static int nf_conntrack_standalone_init_
+@@ -372,7 +426,7 @@ static int nf_conntrack_standalone_init_
  {
  	struct proc_dir_entry *pde;
  
diff --git a/target/linux/generic/patches-3.9/604-netfilter_conntrack_flush.patch b/target/linux/generic/patches-3.9/604-netfilter_conntrack_flush.patch
index be37095c89..39a3e71e21 100644
--- a/target/linux/generic/patches-3.9/604-netfilter_conntrack_flush.patch
+++ b/target/linux/generic/patches-3.9/604-netfilter_conntrack_flush.patch
@@ -1,12 +1,35 @@
 --- a/net/netfilter/nf_conntrack_standalone.c
 +++ b/net/netfilter/nf_conntrack_standalone.c
-@@ -267,10 +267,34 @@ static int ct_open(struct inode *inode,
+@@ -16,6 +16,7 @@
+ #include <linux/percpu.h>
+ #include <linux/netdevice.h>
+ #include <linux/security.h>
++#include <linux/inet.h>
+ #include <net/net_namespace.h>
+ #ifdef CONFIG_SYSCTL
+ #include <linux/sysctl.h>
+@@ -267,10 +268,63 @@ static int ct_open(struct inode *inode,
  			sizeof(struct ct_iter_state));
  }
  
-+static int kill_all(struct nf_conn *i, void *data)
++struct kill_request {
++	u16 family;
++	union nf_inet_addr addr;
++};
++
++static int kill_matching(struct nf_conn *i, void *data)
 +{
-+    return 1;
++	struct kill_request *kr = data;
++	struct nf_conntrack_tuple *t = &i->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
++
++	if (!kr->family)
++		return 1;
++
++	if (t->src.l3num != kr->family)
++		return 0;
++
++	return (nf_inet_addr_cmp(&kr->addr, &t->src.u3) ||
++	        nf_inet_addr_cmp(&kr->addr, &t->dst.u3));
 +}
 +
 +static ssize_t ct_file_write(struct file *file, const char __user *buf,
@@ -14,16 +37,30 @@
 +{
 +	struct seq_file *seq = file->private_data;
 +	struct net *net = seq_file_net(seq);
++	struct kill_request kr = { };
++	char req[INET6_ADDRSTRLEN] = { };
 +
-+	if (count) {
-+		char c;
++	if (count == 0)
++		return 0;
 +
-+		if (get_user(c, buf))
-+			return -EFAULT;
++	if (count >= INET6_ADDRSTRLEN)
++		count = INET6_ADDRSTRLEN - 1;
 +
-+		if (c == 'f')
-+			nf_ct_iterate_cleanup(net, kill_all, NULL);
++	if (copy_from_user(req, buf, count))
++		return -EFAULT;
++
++	if (strnchr(req, count, ':')) {
++		kr.family = AF_INET6;
++		if (!in6_pton(req, count, (void *)&kr.addr, '\n', NULL))
++			return -EINVAL;
++	} else if (strnchr(req, count, '.')) {
++		kr.family = AF_INET;
++		if (!in4_pton(req, count, (void *)&kr.addr, '\n', NULL))
++			return -EINVAL;
 +	}
++
++	nf_ct_iterate_cleanup(net, kill_matching, &kr);
++
 +	return count;
 +}
 +
@@ -35,7 +72,7 @@
  	.llseek  = seq_lseek,
  	.release = seq_release_net,
  };
-@@ -372,7 +396,7 @@ static int nf_conntrack_standalone_init_
+@@ -372,7 +426,7 @@ static int nf_conntrack_standalone_init_
  {
  	struct proc_dir_entry *pde;
  
-- 
2.25.1