[PATCH 3/3] netfilter: nat: don't assign a null snat rule to bridged traffic if no matching

2016-07-30 Thread fxp2001640163
From: Xiaoping Fan 

In some case, bridged packet will come back again for routing. When bridge
netfilter is enabled, a null snat rule is assigned to bridged packet if no
matching in nat chain. Then nat rule matching is skipped when packet comes
back for routing. This result in private IP address exported to public
network. So we don't assign a null snat rule to bridged traffic if no
matching.

Signed-off-by: Xiaoping Fan 
---
 net/ipv4/netfilter/nf_nat_l3proto_ipv4.c | 3 +++
 net/ipv6/netfilter/nf_nat_l3proto_ipv6.c | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c 
b/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c
index 41c7992..151eee6 100644
--- a/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c
+++ b/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c
@@ -315,6 +315,9 @@ nf_nat_ipv4_fn(void *priv, struct sk_buff *skb,
break;
}
 
+   if (nf_nat_is_bridged_pkt(skb))
+   break;
+
ret = nf_nat_alloc_null_binding(ct, state->hook);
if (ret != NF_ACCEPT)
return ret;
diff --git a/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c 
b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c
index dc8df3a..c94eae4 100644
--- a/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c
+++ b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c
@@ -324,6 +324,9 @@ nf_nat_ipv6_fn(void *priv, struct sk_buff *skb,
break;
}
 
+   if (nf_nat_is_bridged_pkt(skb))
+   break;
+
ret = nf_nat_alloc_null_binding(ct, state->hook);
if (ret != NF_ACCEPT)
return ret;
-- 
1.9.1



[PATCH 2/3] netfilter: nat: snat created in route process just apply to routed traffic

2016-07-30 Thread fxp2001640163
From: Xiaoping Fan 

In some situations, packet goes through Linux twice, one for bridging,
another for routing. If snat is created in bridging process, that means
snat rule only matches bridged traffic. If snat is created in routing
process, that means snat rule only matches routed traffic. If we apply
snat to both bridged and routed traffic, traffic will be translated
unexpectedly. So we limit snat created in bridge process to bridged
traffic, snat created in route process to routed traffic.

Signed-off-by: Xiaoping Fan 
---
 include/net/netfilter/nf_nat.h   |  9 +
 net/ipv4/netfilter/nf_nat_l3proto_ipv4.c | 15 ++-
 net/ipv6/netfilter/nf_nat_l3proto_ipv6.c | 15 ++-
 3 files changed, 37 insertions(+), 2 deletions(-)

diff --git a/include/net/netfilter/nf_nat.h b/include/net/netfilter/nf_nat.h
index c327a43..ef6e06c 100644
--- a/include/net/netfilter/nf_nat.h
+++ b/include/net/netfilter/nf_nat.h
@@ -18,12 +18,21 @@ enum nf_nat_manip_type {
 #include 
 #include 
 
+/* Check if packet is a bridged packet when do SNAT */
+#if defined(CONFIG_BRIDGE_NETFILTER)
+#define nf_nat_is_bridged_pkt(SKB) ((SKB)->nf_bridge && \
+   ((SKB)->nf_bridge->physoutdev != NULL))
+#else
+#define nf_nat_is_bridged_pkt(SKB) 0
+#endif
+
 /* per conntrack: nat application helper private data */
 union nf_conntrack_nat_help {
/* insert nat helper private data here */
 #if defined(CONFIG_NF_NAT_PPTP) || defined(CONFIG_NF_NAT_PPTP_MODULE)
struct nf_nat_pptp nat_pptp_info;
 #endif
+   bool snat_in_bridge;
 };
 
 struct nf_conn;
diff --git a/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c 
b/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c
index f8aad03..41c7992 100644
--- a/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c
+++ b/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c
@@ -14,6 +14,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -277,6 +278,12 @@ nf_nat_ipv4_fn(void *priv, struct sk_buff *skb,
if (nat == NULL)
return NF_ACCEPT;
 
+   if ((maniptype == NF_NAT_MANIP_SRC) &&
+   nf_nat_initialized(ct, maniptype) &&
+   (nat->help.snat_in_bridge != nf_nat_is_bridged_pkt(skb))) {
+   return NF_ACCEPT;
+   }
+
switch (ctinfo) {
case IP_CT_RELATED:
case IP_CT_RELATED_REPLY:
@@ -299,8 +306,14 @@ nf_nat_ipv4_fn(void *priv, struct sk_buff *skb,
if (ret != NF_ACCEPT)
return ret;
 
-   if (nf_nat_initialized(ct, HOOK2MANIP(state->hook)))
+   if (nf_nat_initialized(ct, HOOK2MANIP(state->hook))) {
+   if (maniptype == NF_NAT_MANIP_SRC) {
+   nfct_nat(ct)->help.snat_in_bridge =
+   nf_nat_is_bridged_pkt(skb);
+   }
+
break;
+   }
 
ret = nf_nat_alloc_null_binding(ct, state->hook);
if (ret != NF_ACCEPT)
diff --git a/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c 
b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c
index e0be97e..dc8df3a 100644
--- a/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c
+++ b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c
@@ -12,6 +12,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -281,6 +282,12 @@ nf_nat_ipv6_fn(void *priv, struct sk_buff *skb,
if (nat == NULL)
return NF_ACCEPT;
 
+   if ((maniptype == NF_NAT_MANIP_SRC) &&
+   nf_nat_initialized(ct, maniptype) &&
+   (nat->help.snat_in_bridge != nf_nat_is_bridged_pkt(skb))) {
+   return NF_ACCEPT;
+   }
+
switch (ctinfo) {
case IP_CT_RELATED:
case IP_CT_RELATED_REPLY:
@@ -308,8 +315,14 @@ nf_nat_ipv6_fn(void *priv, struct sk_buff *skb,
if (ret != NF_ACCEPT)
return ret;
 
-   if (nf_nat_initialized(ct, HOOK2MANIP(state->hook)))
+   if (nf_nat_initialized(ct, HOOK2MANIP(state->hook))) {
+   if (maniptype == NF_NAT_MANIP_SRC) {
+   nfct_nat(ct)->help.snat_in_bridge =
+   nf_nat_is_bridged_pkt(skb);
+   }
+
break;
+   }
 
ret = nf_nat_alloc_null_binding(ct, state->hook);
if (ret != NF_ACCEPT)
-- 
1.9.1



[PATCH 1/3] netfilter: nat: update hash bucket if nat changed after ct confirmed

2016-07-30 Thread fxp2001640163
From: Xiaoping Fan 

In some situations, NAT information is created after connection is
confirmed. Since 5 tuple for reply direction is changed when creating
NAT information, so we need to update hash bucket of connection.

Signed-off-by: Xiaoping Fan 
---
 include/net/netfilter/nf_conntrack.h |  5 
 net/netfilter/nf_conntrack_core.c| 51 ++--
 net/netfilter/nf_nat_core.c  |  9 +++
 3 files changed, 63 insertions(+), 2 deletions(-)

diff --git a/include/net/netfilter/nf_conntrack.h 
b/include/net/netfilter/nf_conntrack.h
index 445b019..cc9ba66 100644
--- a/include/net/netfilter/nf_conntrack.h
+++ b/include/net/netfilter/nf_conntrack.h
@@ -191,6 +191,9 @@ void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls);
 void nf_ct_free_hashtable(void *hash, unsigned int size);
 
 int nf_conntrack_hash_check_insert(struct nf_conn *ct);
+void nf_conntrack_ct_hash_bucket_update(struct nf_conn *ct,
+   unsigned int old_hash,
+   unsigned int old_reply_hash);
 bool nf_ct_delete(struct nf_conn *ct, u32 pid, int report);
 
 bool nf_ct_get_tuplepr(const struct sk_buff *skb, unsigned int nhoff,
@@ -305,6 +308,8 @@ int nf_conntrack_set_hashsize(const char *val, struct 
kernel_param *kp);
 int nf_conntrack_hash_resize(unsigned int hashsize);
 extern unsigned int nf_conntrack_htable_size;
 extern unsigned int nf_conntrack_max;
+u_int32_t hash_conntrack(const struct net *net,
+const struct nf_conntrack_tuple *tuple);
 
 struct nf_conn *nf_ct_tmpl_alloc(struct net *net,
 const struct nf_conntrack_zone *zone,
diff --git a/net/netfilter/nf_conntrack_core.c 
b/net/netfilter/nf_conntrack_core.c
index dd2c43a..d4ee145 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -202,11 +202,12 @@ static u32 __hash_conntrack(const struct net *net,
return reciprocal_scale(hash_conntrack_raw(tuple, net), size);
 }
 
-static u32 hash_conntrack(const struct net *net,
- const struct nf_conntrack_tuple *tuple)
+u32 hash_conntrack(const struct net *net,
+  const struct nf_conntrack_tuple *tuple)
 {
return scale_hash(hash_conntrack_raw(tuple, net));
 }
+EXPORT_SYMBOL(hash_conntrack);
 
 bool
 nf_ct_get_tuple(const struct sk_buff *skb,
@@ -636,6 +637,52 @@ out:
 }
 EXPORT_SYMBOL_GPL(nf_conntrack_hash_check_insert);
 
+/* Sometimes reply tuple of ct is changed by nat after ct is confirmed,
+ * hash bucket of ct has to be updated in this situation.
+ */
+void nf_conntrack_ct_hash_bucket_update(struct nf_conn *ct,
+   unsigned int old_hash,
+   unsigned int old_reply_hash)
+{
+   struct net *net;
+   unsigned int hash, reply_hash;
+   unsigned int sequence;
+
+   if (!ct || nf_ct_is_untracked(ct) || !nf_ct_is_confirmed(ct))
+   return;
+
+   net = nf_ct_net(ct);
+
+   local_bh_disable();
+   do {
+   sequence = read_seqcount_begin(_conntrack_generation);
+   } while (nf_conntrack_double_lock(net, old_hash, old_reply_hash, 
sequence));
+
+   /* Remove from confirmed list */
+   hlist_nulls_del_rcu(>tuplehash[IP_CT_DIR_ORIGINAL].hnnode);
+   hlist_nulls_del_rcu(>tuplehash[IP_CT_DIR_REPLY].hnnode);
+
+   nf_conntrack_double_unlock(old_hash, old_reply_hash);
+
+   /* Make changes visible in other cores */
+   smp_wmb();
+
+   do {
+   sequence = read_seqcount_begin(_conntrack_generation);
+   hash = hash_conntrack(net,
+ >tuplehash[IP_CT_DIR_ORIGINAL].tuple);
+   reply_hash = hash_conntrack(net,
+   
>tuplehash[IP_CT_DIR_REPLY].tuple);
+   } while (nf_conntrack_double_lock(net, hash, reply_hash, sequence));
+
+   /* Insert to confirmed list again */
+   __nf_conntrack_hash_insert(ct, hash, reply_hash);
+
+   nf_conntrack_double_unlock(hash, reply_hash);
+   local_bh_enable();
+}
+EXPORT_SYMBOL_GPL(nf_conntrack_ct_hash_bucket_update);
+
 static inline void nf_ct_acct_update(struct nf_conn *ct,
 enum ip_conntrack_info ctinfo,
 unsigned int len)
diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c
index de31818..612d8d57 100644
--- a/net/netfilter/nf_nat_core.c
+++ b/net/netfilter/nf_nat_core.c
@@ -405,8 +405,10 @@ nf_nat_setup_info(struct nf_conn *ct,
  const struct nf_nat_range *range,
  enum nf_nat_manip_type maniptype)
 {
+   struct net *net = nf_ct_net(ct);
struct nf_conntrack_tuple curr_tuple, new_tuple;
struct nf_conn_nat *nat;
+   unsigned int old_hash, old_reply_hash;
 
/* nat helper or nfctnetlink also