netfilter-devel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Pablo Neira Ayuso <pablo@netfilter.org>
To: Bernhard Thaler <bernhard.thaler@wvnet.at>
Cc: kadlec@blackhole.kfki.hu, netfilter-devel@vger.kernel.org,
	fw@strlen.de, Sven Eckelmann <sven@open-mesh.com>
Subject: Re: [PATCHv3 1/4] netfilter: bridge: detect NAT66 correctly and change MAC address
Date: Fri, 29 May 2015 02:11:12 +0200	[thread overview]
Message-ID: <20150529001112.GA705@salvia> (raw)
In-Reply-To: <1432801419-11832-1-git-send-email-bernhard.thaler@wvnet.at>

On Thu, May 28, 2015 at 10:23:39AM +0200, Bernhard Thaler wrote:
> IPv4 iptables allows to REDIRECT/DNAT/SNAT any traffic over a bridge.
> 
> e.g. REDIRECT
> $ sysctl -w net.bridge.bridge-nf-call-iptables=1
> $ iptables -t nat -A PREROUTING -p tcp -m tcp --dport 8080 \
>   -j REDIRECT --to-ports 81
> 
> This does not work with ip6tables on a bridge in NAT66 scenario
> because the REDIRECT/DNAT/SNAT is not correctly detected.
> 
> The bridge pre-routing (finish) netfilter hook has to check for a possible
> redirect and then fix the destination mac address. This allows to use the
> ip6tables rules for local REDIRECT/DNAT/SNAT REDIRECT similar to the IPv4
> iptables version.
> 
> e.g. REDIRECT
> $ sysctl -w net.bridge.bridge-nf-call-ip6tables=1
> $ ip6tables -t nat -A PREROUTING -p tcp -m tcp --dport 8080 \
>   -j REDIRECT --to-ports 81
> 
> This patch makes it possible to use IPv6 NAT66 on a bridge. It was tested
> on a bridge with two interfaces using SNAT/DNAT NAT66 rules.
> 
> Reported-by: Artie Hamilton <artiemhamilton@yahoo.com>
> Signed-off-by: Sven Eckelmann <sven@open-mesh.com>
> [bernhard.thaler@wvnet.at: rebased, adjust function order]
> [bernhard.thaler@wvnet.at: add indirect call to ip6_route_input()]
> [bernhard.thaler@wvnet.at: rebased]
> Signed-off-by: Bernhard Thaler <bernhard.thaler@wvnet.at>
> ---
> Patch revision history:
> 
> v3
> * re-base again to davem's current net-next
> * changed subject to include netfilter 
> 
> v2
> * re-base again to davem's current net-next
> * add indirect call to ip6_route_input via nf_ipv6_ops to avoid 
>   direct dependency to ipv6.ko just because of function calls
> 
> v1
> * rebase "bridge: Allow to redirect IPv6 traffic to local machine"
>   to davem's current net-next
> * adjust function order to avoid prototype for br_nf_pre_routing_finish_bridge
>    
> (v0)
> * originally there were two patches solving this problem
> * Patch from Sven Eckelmann was chosen to base solution on 
>   see: bridge: Allow to redirect IPv6 traffic to local machine
>   see: bridge: Fix NAT66ed IPv6 packets not being bridged correctly
> 
>  include/linux/netfilter_ipv6.h |    1 +
>  include/linux/skbuff.h         |    7 ++-
>  net/bridge/br_netfilter.c      |  105 ++++++++++++++++++++++++++++------------
>  net/ipv6/netfilter.c           |    1 +
>  4 files changed, 81 insertions(+), 33 deletions(-)
> 
> diff --git a/include/linux/netfilter_ipv6.h b/include/linux/netfilter_ipv6.h
> index 64dad1cc..e2d1969 100644
> --- a/include/linux/netfilter_ipv6.h
> +++ b/include/linux/netfilter_ipv6.h
> @@ -25,6 +25,7 @@ void ipv6_netfilter_fini(void);
>  struct nf_ipv6_ops {
>  	int (*chk_addr)(struct net *net, const struct in6_addr *addr,
>  			const struct net_device *dev, int strict);
> +	void (*route_input)(struct sk_buff *skb);
>  };
>  
>  extern const struct nf_ipv6_ops __rcu *nf_ipv6_ops;
> diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
> index 6b41c15..369643b 100644
> --- a/include/linux/skbuff.h
> +++ b/include/linux/skbuff.h
> @@ -37,6 +37,8 @@
>  #include <net/flow_dissector.h>
>  #include <linux/splice.h>
>  

Please, remove the empty line above.

> +#include <uapi/linux/in6.h>

I think you can use <linux/in6.h> instead.

> +
>  /* A. Checksumming of received packets by device.
>   *
>   * CHECKSUM_NONE:
> @@ -179,7 +181,10 @@ struct nf_bridge_info {
>  		struct net_device *physoutdev;
>  		char neigh_header[8];
>  	};
> -	__be32			ipv4_daddr;
> +	union {
> +		__be32          ipv4_daddr;
> +		struct in6_addr ipv6_daddr;
> +	};
>  };
>  #endif
>  
> diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c
> index 46660a2..ea8e063 100644
> --- a/net/bridge/br_netfilter.c
> +++ b/net/bridge/br_netfilter.c
> @@ -278,37 +278,6 @@ static void nf_bridge_update_protocol(struct sk_buff *skb)
>  	}
>  }
>  
> -/* PF_BRIDGE/PRE_ROUTING *********************************************/
> -/* Undo the changes made for ip6tables PREROUTING and continue the
> - * bridge PRE_ROUTING hook. */
> -static int br_nf_pre_routing_finish_ipv6(struct sock *sk, struct sk_buff *skb)
> -{
> -	struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
> -	struct rtable *rt;
> -
> -	if (nf_bridge->pkt_otherhost) {
> -		skb->pkt_type = PACKET_OTHERHOST;
> -		nf_bridge->pkt_otherhost = false;
> -	}
> -	nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;
> -
> -	rt = bridge_parent_rtable(nf_bridge->physindev);
> -	if (!rt) {
> -		kfree_skb(skb);
> -		return 0;
> -	}
> -	skb_dst_set_noref(skb, &rt->dst);
> -
> -	skb->dev = nf_bridge->physindev;
> -	nf_bridge_update_protocol(skb);
> -	nf_bridge_push_encap_header(skb);
> -	NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, sk, skb,
> -		       skb->dev, NULL,
> -		       br_handle_frame_finish, 1);
> -
> -	return 0;
> -}
> -
>  /* Obtain the correct destination MAC address, while preserving the original
>   * source MAC address. If we already know this address, we just copy it. If we
>   * don't, we use the neighbour framework to find out. In both cases, we make
> @@ -357,7 +326,75 @@ free_skb:
>  static bool daddr_was_changed(const struct sk_buff *skb,
>  			      const struct nf_bridge_info *nf_bridge)
>  {
> -	return ip_hdr(skb)->daddr != nf_bridge->ipv4_daddr;
> +	if (skb->protocol == htons(ETH_P_IP))
> +		return ip_hdr(skb)->daddr != nf_bridge->ipv4_daddr;
> +
> +	if (skb->protocol == htons(ETH_P_IPV6))
> +		return memcmp(&nf_bridge->ipv6_daddr, &ipv6_hdr(skb)->daddr,
> +			      sizeof(ipv6_hdr(skb)->daddr)) != 0;

could you use a switch for this instead?

> +
> +	return false;
> +}
> +
> +/* PF_BRIDGE/PRE_ROUTING *********************************************/
> +/* Undo the changes made for ip6tables PREROUTING and continue the
> + * bridge PRE_ROUTING hook.
> + */
> +
> +/* see comment for br_nf_pre_routing_finish
> + * same logic is used here but equivalent IPv6 function
> + * ip6_route_input() called indirectly
> + */

I know you're copying and pasting an original comment, but could you
convert this to:

/* PF_BRIDGE/PRE_ROUTING: Undo the changes made for ip6tables
 * PREROUTING and continue the bridge PRE_ROUTING hook. See comment
 * for br_nf_pre_routing_finish(), same logic is used here but
 * equivalent IPv6 function ip6_route_input() called indirectly.
 */

> +static int br_nf_pre_routing_finish_ipv6(struct sock *sk, struct sk_buff *skb)
> +{
> +	struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
> +	struct rtable *rt;
> +	struct net_device *dev = skb->dev;
> +	const struct nf_ipv6_ops *v6ops = nf_get_ipv6_ops();
> +
> +	if (nf_bridge->pkt_otherhost) {
> +		skb->pkt_type = PACKET_OTHERHOST;
> +		nf_bridge->pkt_otherhost = false;
> +	}
> +	nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;

I already suggested that this toggle is sloppy, I'd suggest:

        nf_bridge->mask &= ~BRNF_NF_BRIDGE_PREROUTING.

to unset this bit.

> +
> +	if (daddr_was_changed(skb, nf_bridge)) {
> +		skb_dst_drop(skb);
> +		v6ops->route_input(skb);
> +
> +		if (skb_dst(skb)->error) {
> +			kfree_skb(skb);
> +			return 0;
> +		}
> +
> +		if (skb_dst(skb)->dev == dev) {
> +			skb->dev = nf_bridge->physindev;
> +			nf_bridge_update_protocol(skb);
> +			nf_bridge_push_encap_header(skb);
> +			NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING,
> +				       sk, skb, skb->dev, NULL,
> +				       br_nf_pre_routing_finish_bridge,
> +				       1);
> +			return 0;
> +		}
> +		ether_addr_copy(eth_hdr(skb)->h_dest, dev->dev_addr);
> +		skb->pkt_type = PACKET_HOST;
> +	} else {
> +		rt = bridge_parent_rtable(nf_bridge->physindev);
> +		if (!rt) {
> +			kfree_skb(skb);
> +			return 0;
> +		}
> +		skb_dst_set_noref(skb, &rt->dst);
> +	}
> +
> +	skb->dev = nf_bridge->physindev;
> +	nf_bridge_update_protocol(skb);
> +	nf_bridge_push_encap_header(skb);
> +	NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, sk, skb,
> +		       skb->dev, NULL,
> +		       br_handle_frame_finish, 1);
> +	return 0;
>  }
>  
>  /* This requires some explaining. If DNAT has taken place,
> @@ -578,6 +615,7 @@ static unsigned int br_nf_pre_routing_ipv6(const struct nf_hook_ops *ops,
>  					   struct sk_buff *skb,
>  					   const struct nf_hook_state *state)
>  {
> +	struct nf_bridge_info *nf_bridge;
>  	const struct ipv6hdr *hdr;
>  	u32 pkt_len;
>  
> @@ -609,6 +647,9 @@ static unsigned int br_nf_pre_routing_ipv6(const struct nf_hook_ops *ops,
>  	if (!setup_pre_routing(skb))
>  		return NF_DROP;
>  
> +	nf_bridge = nf_bridge_info_get(skb);
> +	nf_bridge->ipv6_daddr = ipv6_hdr(skb)->daddr;
> +
>  	skb->protocol = htons(ETH_P_IPV6);
>  	NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, state->sk, skb,
>  		skb->dev, NULL,
> diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c
> index d958718..bbca09f 100644
> --- a/net/ipv6/netfilter.c
> +++ b/net/ipv6/netfilter.c
> @@ -191,6 +191,7 @@ static __sum16 nf_ip6_checksum_partial(struct sk_buff *skb, unsigned int hook,
>  
>  static const struct nf_ipv6_ops ipv6ops = {
>  	.chk_addr	= ipv6_chk_addr,
> +	.route_input    = ip6_route_input
>  };
>  
>  static const struct nf_afinfo nf_ip6_afinfo = {
> -- 
> 1.7.10.4
> 

      reply	other threads:[~2015-05-29  0:06 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-05-28  8:23 [PATCHv3 1/4] netfilter: bridge: detect NAT66 correctly and change MAC address Bernhard Thaler
2015-05-29  0:11 ` Pablo Neira Ayuso [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20150529001112.GA705@salvia \
    --to=pablo@netfilter.org \
    --cc=bernhard.thaler@wvnet.at \
    --cc=fw@strlen.de \
    --cc=kadlec@blackhole.kfki.hu \
    --cc=netfilter-devel@vger.kernel.org \
    --cc=sven@open-mesh.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).