diff mbox

Handle routing changes for the MASQUERADE target

Message ID alpine.DEB.2.00.1211302149050.16257@blackhole.kfki.hu
State Superseded
Headers show

Commit Message

Jozsef Kadlecsik Nov. 30, 2012, 8:51 p.m. UTC
When the route changes (backup default route, VPNs) which affect a
masqueraded target, the packets were sent out with the outdated source
address. The patch addresses the issue by comparing the outgoing interface
directly with the masqueraded interface in the nat table.

Events are inefficient in this case, because it'd require adding route
events to the network core and then scanning the whole conntrack table
and re-checking the route for all entry.

Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
---
 include/net/netfilter/nf_nat.h    |   18 ++++++++++++++++++
 net/ipv4/netfilter/iptable_nat.c  |    4 ++++
 net/ipv6/netfilter/ip6table_nat.c |    4 ++++
 3 files changed, 26 insertions(+), 0 deletions(-)

Comments

Florian Westphal Nov. 30, 2012, 9:11 p.m. UTC | #1
Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> wrote:

Hi Jozsef,

> index bd8eea7..fc8f1b8 100644
> --- a/include/net/netfilter/nf_nat.h
> +++ b/include/net/netfilter/nf_nat.h
> @@ -68,4 +68,22 @@ static inline struct nf_conn_nat *nfct_nat(const struct nf_conn *ct)
>  
> +static inline bool nf_nat_oif_changed(const struct sk_buff *skb,
> +				      unsigned int hooknum,
[..]
> +{

If you put the #if IS_ENABLED here, it would no longer be necessary
in the .c files.  nf_nat_oif_changed() would always return false
in the 'no MASQERADE' case.

> +	if (nat->masq_index && hooknum == NF_INET_POST_ROUTING &&
> +	    CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL &&
> +	    nat->masq_index != out->ifindex) {
> +		/* Outgoing interface changed, destroy conntrack. */
> +		nf_ct_kill_acct(cf, ctinfo, skb);
> +		nf_ct_put(ct);

Hmm. Is the nf_ct_put() correct?
nf_ct_kill invokes death_by_timeout(), which also puts ct.

> diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c
> index ac635a7..4ed34ab 100644
> --- a/net/ipv4/netfilter/iptable_nat.c
> +++ b/net/ipv4/netfilter/iptable_nat.c
> @@ -134,6 +134,10 @@ nf_nat_ipv4_fn(unsigned int hooknum,
>  		/* ESTABLISHED */
>  		NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED ||
>  			     ctinfo == IP_CT_ESTABLISHED_REPLY);
> +#if IS_ENABLED(CONFIG_IP_NF_TARGET_MASQUERADE)
> +		if (nf_nat_oif_changed(skb, hooknum, ct, ctinfo, nat, out))
> +			return NF_DROP;
> +#endif
>  	}

I wonder if this:

	if (nf_nat_oif_changed(skb, hooknum, ctinfo, nat, out)) {
		nf_ct_kill_acct(ct, ctinfo, skb);
		return NF_DROP;
	}

Would be clearer (the name 'nf_nat_oif_changed' sounds like it only
performs a test).

Cheers,
Florian
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jozsef Kadlecsik Nov. 30, 2012, 9:27 p.m. UTC | #2
Hi Florian,

On Fri, 30 Nov 2012, Florian Westphal wrote:

> Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> wrote:
> 
> > index bd8eea7..fc8f1b8 100644
> > --- a/include/net/netfilter/nf_nat.h
> > +++ b/include/net/netfilter/nf_nat.h
> > @@ -68,4 +68,22 @@ static inline struct nf_conn_nat *nfct_nat(const struct nf_conn *ct)
> >  
> > +static inline bool nf_nat_oif_changed(const struct sk_buff *skb,
> > +				      unsigned int hooknum,
> [..]
> > +{
> 
> If you put the #if IS_ENABLED here, it would no longer be necessary
> in the .c files.  nf_nat_oif_changed() would always return false
> in the 'no MASQERADE' case.

Yes, you're right - and a few lines are spared again :-).
 
> > +	if (nat->masq_index && hooknum == NF_INET_POST_ROUTING &&
> > +	    CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL &&
> > +	    nat->masq_index != out->ifindex) {
> > +		/* Outgoing interface changed, destroy conntrack. */
> > +		nf_ct_kill_acct(cf, ctinfo, skb);
> > +		nf_ct_put(ct);
> 
> Hmm. Is the nf_ct_put() correct?
> nf_ct_kill invokes death_by_timeout(), which also puts ct.

nf_nat_ipv[46]_fn starts with "nf_ct_get", so that must be released. 
Technically the last nf_ct_put destroys the entry.
 
> > diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c
> > index ac635a7..4ed34ab 100644
> > --- a/net/ipv4/netfilter/iptable_nat.c
> > +++ b/net/ipv4/netfilter/iptable_nat.c
> > @@ -134,6 +134,10 @@ nf_nat_ipv4_fn(unsigned int hooknum,
> >  		/* ESTABLISHED */
> >  		NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED ||
> >  			     ctinfo == IP_CT_ESTABLISHED_REPLY);
> > +#if IS_ENABLED(CONFIG_IP_NF_TARGET_MASQUERADE)
> > +		if (nf_nat_oif_changed(skb, hooknum, ct, ctinfo, nat, out))
> > +			return NF_DROP;
> > +#endif
> >  	}
> 
> I wonder if this:
> 
> 	if (nf_nat_oif_changed(skb, hooknum, ctinfo, nat, out)) {
> 		nf_ct_kill_acct(ct, ctinfo, skb);
> 		return NF_DROP;
> 	}
> 
> Would be clearer (the name 'nf_nat_oif_changed' sounds like it only
> performs a test).

OK, I'll send an updated version.

Best regards,
Jozsef
-
E-mail  : kadlec@blackhole.kfki.hu, kadlecsik.jozsef@wigner.mta.hu
PGP key : http://www.kfki.hu/~kadlec/pgp_public_key.txt
Address : Wigner Research Centre for Physics, Hungarian Academy of Sciences
          H-1525 Budapest 114, POB. 49, Hungary
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Florian Westphal Nov. 30, 2012, 9:40 p.m. UTC | #3
Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> wrote:
> > > +	if (nat->masq_index && hooknum == NF_INET_POST_ROUTING &&
> > > +	    CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL &&
> > > +	    nat->masq_index != out->ifindex) {
> > > +		/* Outgoing interface changed, destroy conntrack. */
> > > +		nf_ct_kill_acct(cf, ctinfo, skb);
> > > +		nf_ct_put(ct);
> > 
> > Hmm. Is the nf_ct_put() correct?
> > nf_ct_kill invokes death_by_timeout(), which also puts ct.
> 
> nf_nat_ipv[46]_fn starts with "nf_ct_get", so that must be released. 

nf_ct_get() does not increase refcount :)

Regards,
Florian
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jozsef Kadlecsik Nov. 30, 2012, 10:28 p.m. UTC | #4
On Fri, 30 Nov 2012, Florian Westphal wrote:

> Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> wrote:
> > > > +	if (nat->masq_index && hooknum == NF_INET_POST_ROUTING &&
> > > > +	    CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL &&
> > > > +	    nat->masq_index != out->ifindex) {
> > > > +		/* Outgoing interface changed, destroy conntrack. */
> > > > +		nf_ct_kill_acct(cf, ctinfo, skb);
> > > > +		nf_ct_put(ct);
> > > 
> > > Hmm. Is the nf_ct_put() correct?
> > > nf_ct_kill invokes death_by_timeout(), which also puts ct.
> > 
> > nf_nat_ipv[46]_fn starts with "nf_ct_get", so that must be released. 
> 
> nf_ct_get() does not increase refcount :)

Ohh, right - I'm blind.

Best regards,
Jozsef
-
E-mail  : kadlec@blackhole.kfki.hu, kadlecsik.jozsef@wigner.mta.hu
PGP key : http://www.kfki.hu/~kadlec/pgp_public_key.txt
Address : Wigner Research Centre for Physics, Hungarian Academy of Sciences
          H-1525 Budapest 114, POB. 49, Hungary
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/include/net/netfilter/nf_nat.h b/include/net/netfilter/nf_nat.h
index bd8eea7..fc8f1b8 100644
--- a/include/net/netfilter/nf_nat.h
+++ b/include/net/netfilter/nf_nat.h
@@ -68,4 +68,22 @@  static inline struct nf_conn_nat *nfct_nat(const struct nf_conn *ct)
 #endif
 }
 
+static inline bool nf_nat_oif_changed(const struct sk_buff *skb,
+				      unsigned int hooknum,
+				      struct nf_conn *ct,
+				      enum ip_conntrack_info ctinfo,
+				      struct nf_conn_nat *nat,
+				      const struct net_device *out)
+{
+	if (nat->masq_index && hooknum == NF_INET_POST_ROUTING &&
+	    CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL &&
+	    nat->masq_index != out->ifindex) {
+		/* Outgoing interface changed, destroy conntrack. */
+		nf_ct_kill_acct(cf, ctinfo, skb);
+		nf_ct_put(ct);
+		return true;
+	}
+	return false;
+}
+
 #endif
diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c
index ac635a7..4ed34ab 100644
--- a/net/ipv4/netfilter/iptable_nat.c
+++ b/net/ipv4/netfilter/iptable_nat.c
@@ -134,6 +134,10 @@  nf_nat_ipv4_fn(unsigned int hooknum,
 		/* ESTABLISHED */
 		NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED ||
 			     ctinfo == IP_CT_ESTABLISHED_REPLY);
+#if IS_ENABLED(CONFIG_IP_NF_TARGET_MASQUERADE)
+		if (nf_nat_oif_changed(skb, hooknum, ct, ctinfo, nat, out))
+			return NF_DROP;
+#endif
 	}
 
 	return nf_nat_packet(ct, ctinfo, hooknum, skb);
diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c
index fa84cf8..b0036a7 100644
--- a/net/ipv6/netfilter/ip6table_nat.c
+++ b/net/ipv6/netfilter/ip6table_nat.c
@@ -137,6 +137,10 @@  nf_nat_ipv6_fn(unsigned int hooknum,
 		/* ESTABLISHED */
 		NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED ||
 			     ctinfo == IP_CT_ESTABLISHED_REPLY);
+#if IS_ENABLED(CONFIG_IP6_NF_TARGET_MASQUERADE)
+		if (nf_nat_oif_changed(skb, hooknum, ct, ctinfo, nat, out))
+			return NF_DROP;
+#endif
 	}
 
 	return nf_nat_packet(ct, ctinfo, hooknum, skb);