diff mbox

net: gre: provide multicast mappings for ipv4 and ipv6

Message ID 1301388053-6083-1-git-send-email-timo.teras@iki.fi
State Accepted, archived
Delegated to: David Miller
Headers show

Commit Message

Timo Teras March 29, 2011, 8:40 a.m. UTC
My commit 6d55cb91a0020ac0 (gre: fix hard header destination
address checking) broke multicast.

The reason is that ip_gre used to get ipgre_header() calls with
zero destination if we have NOARP or multicast destination. Instead
the actual target was decided at ipgre_tunnel_xmit() time based on
per-protocol dissection.

Instead of allowing the "abuse" of ->header() calls with invalid
destination, this creates multicast mappings for ip_gre. This also
fixes "ip neigh show nud noarp" to display the proper multicast
mappings used by the gre device.

Reported-by: Doug Kehn <rdkehn@yahoo.com>
Signed-off-by: Timo Teräs <timo.teras@iki.fi>
---
Compile tested only. Doug tested IPv4 side with the earlier patch.

The IPv6 side needs a review. I'm not sure if mapped IPv4 multicast
addresses are intrepreted as multicast addresses by ndisc code or
if we could map real IPv6 multicast addresses to IPv4 multicast
addresses.

 include/net/if_inet6.h |   16 ++++++++++++++++
 include/net/ip.h       |    8 ++++++++
 net/ipv4/arp.c         |    3 +++
 net/ipv6/ndisc.c       |    2 ++
 4 files changed, 29 insertions(+), 0 deletions(-)

Comments

Eric Dumazet March 29, 2011, 9:11 a.m. UTC | #1
Le mardi 29 mars 2011 à 11:40 +0300, Timo Teräs a écrit :
> My commit 6d55cb91a0020ac0 (gre: fix hard header destination
> address checking) broke multicast.
> 
> The reason is that ip_gre used to get ipgre_header() calls with
> zero destination if we have NOARP or multicast destination. Instead
> the actual target was decided at ipgre_tunnel_xmit() time based on
> per-protocol dissection.
> 
> Instead of allowing the "abuse" of ->header() calls with invalid
> destination, this creates multicast mappings for ip_gre. This also
> fixes "ip neigh show nud noarp" to display the proper multicast
> mappings used by the gre device.
> 
> Reported-by: Doug Kehn <rdkehn@yahoo.com>
> Signed-off-by: Timo Teräs <timo.teras@iki.fi>
> ---
> Compile tested only. Doug tested IPv4 side with the earlier patch.


> +static inline int ipv6_ipgre_mc_map(const struct in6_addr *addr,
> +				    const unsigned char *broadcast, char *buf)
> +{
> +	if ((broadcast[0] | broadcast[1] | broadcast[2] | broadcast[3]) != 0) {
> +		memcpy(buf, broadcast, 4);
> +	} else {
> +		/* v4mapped? */
> +		if ((addr->s6_addr32[0] | addr->s6_addr32[1] |
> +		     (addr->s6_addr32[2] ^ htonl(0x0000ffff))) != 0)
> +			return -EINVAL;

		if (ipv6_addr_v4mapped(addr))


> +		memcpy(buf, &addr->s6_addr32[3], 4);
> +	}
> +	return 0;
> +}


--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Timo Teras March 29, 2011, 10 a.m. UTC | #2
On 03/29/2011 12:11 PM, Eric Dumazet wrote:
> Le mardi 29 mars 2011 à 11:40 +0300, Timo Teräs a écrit :
>> My commit 6d55cb91a0020ac0 (gre: fix hard header destination
>> address checking) broke multicast.
>>
>> The reason is that ip_gre used to get ipgre_header() calls with
>> zero destination if we have NOARP or multicast destination. Instead
>> the actual target was decided at ipgre_tunnel_xmit() time based on
>> per-protocol dissection.
>>
>> Instead of allowing the "abuse" of ->header() calls with invalid
>> destination, this creates multicast mappings for ip_gre. This also
>> fixes "ip neigh show nud noarp" to display the proper multicast
>> mappings used by the gre device.
>>
>> Reported-by: Doug Kehn <rdkehn@yahoo.com>
>> Signed-off-by: Timo Teräs <timo.teras@iki.fi>
>> ---
>> Compile tested only. Doug tested IPv4 side with the earlier patch.
> 
> 
>> +static inline int ipv6_ipgre_mc_map(const struct in6_addr *addr,
>> +				    const unsigned char *broadcast, char *buf)
>> +{
>> +	if ((broadcast[0] | broadcast[1] | broadcast[2] | broadcast[3]) != 0) {
>> +		memcpy(buf, broadcast, 4);
>> +	} else {
>> +		/* v4mapped? */
>> +		if ((addr->s6_addr32[0] | addr->s6_addr32[1] |
>> +		     (addr->s6_addr32[2] ^ htonl(0x0000ffff))) != 0)
>> +			return -EINVAL;
> 
> 		if (ipv6_addr_v4mapped(addr))
> 
> 
>> +		memcpy(buf, &addr->s6_addr32[3], 4);
>> +	}
>> +	return 0;
>> +}

I wanted to put the function same header as all other similar ones:
net/if_inet6.h. However, ipv6_addr_v4mapped() is defined in net/ipv6.h
which includes net/if_inet6.h. So I can't really use that function there.

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Doug Kehn March 29, 2011, 8:26 p.m. UTC | #3
--- On Tue, 3/29/11, Timo Teräs <timo.teras@iki.fi> wrote:

> From: Timo Teräs <timo.teras@iki.fi>
> Subject: [PATCH] net: gre: provide multicast mappings for ipv4 and ipv6
> To: netdev@vger.kernel.org
> Cc: "Doug Kehn" <rdkehn@yahoo.com>, "Timo Teräs" <timo.teras@iki.fi>
> Date: Tuesday, March 29, 2011, 4:40 AM
> My commit 6d55cb91a0020ac0 (gre: fix
> hard header destination
> address checking) broke multicast.
> 
> The reason is that ip_gre used to get ipgre_header() calls
> with
> zero destination if we have NOARP or multicast destination.
> Instead
> the actual target was decided at ipgre_tunnel_xmit() time
> based on
> per-protocol dissection.
> 
> Instead of allowing the "abuse" of ->header() calls with
> invalid
> destination, this creates multicast mappings for ip_gre.
> This also
> fixes "ip neigh show nud noarp" to display the proper
> multicast
> mappings used by the gre device.
> 
> Reported-by: Doug Kehn <rdkehn@yahoo.com>
> Signed-off-by: Timo Teräs <timo.teras@iki.fi>

Acked-by: Doug Kehn <rdkehn@yahoo.com>

> ---
> Compile tested only. Doug tested IPv4 side with the earlier
> patch.

This patch set [still] works with IPv4.

> 
> The IPv6 side needs a review. I'm not sure if mapped IPv4
> multicast
> addresses are intrepreted as multicast addresses by ndisc
> code or
> if we could map real IPv6 multicast addresses to IPv4
> multicast
> addresses.
> 
>  include/net/if_inet6.h |   16
> ++++++++++++++++
>  include/net/ip.h       | 
>   8 ++++++++
>  net/ipv4/arp.c     
>    |    3 +++
>  net/ipv6/ndisc.c       | 
>   2 ++
>  4 files changed, 29 insertions(+), 0 deletions(-)
> 
> diff --git a/include/net/if_inet6.h
> b/include/net/if_inet6.h
> index 04977ee..fccc218 100644
> --- a/include/net/if_inet6.h
> +++ b/include/net/if_inet6.h
> @@ -286,5 +286,21 @@ static inline void
> ipv6_ib_mc_map(const struct in6_addr *addr,
>      buf[9]  = broadcast[9];
>      memcpy(buf + 10, addr->s6_addr + 6,
> 10);
>  }
> +
> +static inline int ipv6_ipgre_mc_map(const struct in6_addr
> *addr,
> +           
>         const unsigned char
> *broadcast, char *buf)
> +{
> +    if ((broadcast[0] | broadcast[1] |
> broadcast[2] | broadcast[3]) != 0) {
> +        memcpy(buf,
> broadcast, 4);
> +    } else {
> +        /* v4mapped? */
> +        if
> ((addr->s6_addr32[0] | addr->s6_addr32[1] |
> +         
>    (addr->s6_addr32[2] ^
> htonl(0x0000ffff))) != 0)
> +           
> return -EINVAL;
> +        memcpy(buf,
> &addr->s6_addr32[3], 4);
> +    }
> +    return 0;
> +}
> +
>  #endif
>  #endif
> diff --git a/include/net/ip.h b/include/net/ip.h
> index a4f6311..7c41658 100644
> --- a/include/net/ip.h
> +++ b/include/net/ip.h
> @@ -339,6 +339,14 @@ static inline void ip_ib_mc_map(__be32
> naddr, const unsigned char *broadcast, ch
>      buf[16] = addr & 0x0f;
>  }
>  
> +static inline void ip_ipgre_mc_map(__be32 naddr, const
> unsigned char *broadcast, char *buf)
> +{
> +    if ((broadcast[0] | broadcast[1] |
> broadcast[2] | broadcast[3]) != 0)
> +        memcpy(buf,
> broadcast, 4);
> +    else
> +        memcpy(buf,
> &naddr, sizeof(naddr));
> +}
> +
>  #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
>  #include <linux/ipv6.h>
>  #endif
> diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c
> index 090d273..1b74d3b 100644
> --- a/net/ipv4/arp.c
> +++ b/net/ipv4/arp.c
> @@ -215,6 +215,9 @@ int arp_mc_map(__be32 addr, u8 *haddr,
> struct net_device *dev, int dir)
>      case ARPHRD_INFINIBAND:
>          ip_ib_mc_map(addr,
> dev->broadcast, haddr);
>          return 0;
> +    case ARPHRD_IPGRE:
> +       
> ip_ipgre_mc_map(addr, dev->broadcast, haddr);
> +        return 0;
>      default:
>          if (dir) {
>             
> memcpy(haddr, dev->broadcast, dev->addr_len);
> diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
> index 0e49c9d..92f952d 100644
> --- a/net/ipv6/ndisc.c
> +++ b/net/ipv6/ndisc.c
> @@ -341,6 +341,8 @@ int ndisc_mc_map(struct in6_addr *addr,
> char *buf, struct net_device *dev, int d
>      case ARPHRD_INFINIBAND:
>          ipv6_ib_mc_map(addr,
> dev->broadcast, buf);
>          return 0;
> +    case ARPHRD_IPGRE:
> +        return
> ipv6_ipgre_mc_map(addr, dev->broadcast, buf);
>      default:
>          if (dir) {
>             
> memcpy(buf, dev->broadcast, dev->addr_len);
> -- 
> 1.7.1
> 
> --
> To unsubscribe from this list: send the line "unsubscribe
> netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
David Miller March 30, 2011, 7:11 a.m. UTC | #4
From: Doug Kehn <rdkehn@yahoo.com>
Date: Tue, 29 Mar 2011 13:26:02 -0700 (PDT)

> 
> --- On Tue, 3/29/11, Timo Teräs <timo.teras@iki.fi> wrote:
> 
>> From: Timo Teräs <timo.teras@iki.fi>
>> Subject: [PATCH] net: gre: provide multicast mappings for ipv4 and ipv6
>> To: netdev@vger.kernel.org
>> Cc: "Doug Kehn" <rdkehn@yahoo.com>, "Timo Teräs" <timo.teras@iki.fi>
>> Date: Tuesday, March 29, 2011, 4:40 AM
>> My commit 6d55cb91a0020ac0 (gre: fix
>> hard header destination
>> address checking) broke multicast.
>> 
>> The reason is that ip_gre used to get ipgre_header() calls
>> with
>> zero destination if we have NOARP or multicast destination.
>> Instead
>> the actual target was decided at ipgre_tunnel_xmit() time
>> based on
>> per-protocol dissection.
>> 
>> Instead of allowing the "abuse" of ->header() calls with
>> invalid
>> destination, this creates multicast mappings for ip_gre.
>> This also
>> fixes "ip neigh show nud noarp" to display the proper
>> multicast
>> mappings used by the gre device.
>> 
>> Reported-by: Doug Kehn <rdkehn@yahoo.com>
>> Signed-off-by: Timo Teräs <timo.teras@iki.fi>
> 
> Acked-by: Doug Kehn <rdkehn@yahoo.com>

Applied.

Timo, if updates are necessary for the ipv4 mapped handling in ipv6,
please send that as a follow-up fix.

Thanks.
--
To unsubscribe from this list: send the line "unsubscribe netdev" 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/if_inet6.h b/include/net/if_inet6.h
index 04977ee..fccc218 100644
--- a/include/net/if_inet6.h
+++ b/include/net/if_inet6.h
@@ -286,5 +286,21 @@  static inline void ipv6_ib_mc_map(const struct in6_addr *addr,
 	buf[9]  = broadcast[9];
 	memcpy(buf + 10, addr->s6_addr + 6, 10);
 }
+
+static inline int ipv6_ipgre_mc_map(const struct in6_addr *addr,
+				    const unsigned char *broadcast, char *buf)
+{
+	if ((broadcast[0] | broadcast[1] | broadcast[2] | broadcast[3]) != 0) {
+		memcpy(buf, broadcast, 4);
+	} else {
+		/* v4mapped? */
+		if ((addr->s6_addr32[0] | addr->s6_addr32[1] |
+		     (addr->s6_addr32[2] ^ htonl(0x0000ffff))) != 0)
+			return -EINVAL;
+		memcpy(buf, &addr->s6_addr32[3], 4);
+	}
+	return 0;
+}
+
 #endif
 #endif
diff --git a/include/net/ip.h b/include/net/ip.h
index a4f6311..7c41658 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -339,6 +339,14 @@  static inline void ip_ib_mc_map(__be32 naddr, const unsigned char *broadcast, ch
 	buf[16] = addr & 0x0f;
 }
 
+static inline void ip_ipgre_mc_map(__be32 naddr, const unsigned char *broadcast, char *buf)
+{
+	if ((broadcast[0] | broadcast[1] | broadcast[2] | broadcast[3]) != 0)
+		memcpy(buf, broadcast, 4);
+	else
+		memcpy(buf, &naddr, sizeof(naddr));
+}
+
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 #include <linux/ipv6.h>
 #endif
diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c
index 090d273..1b74d3b 100644
--- a/net/ipv4/arp.c
+++ b/net/ipv4/arp.c
@@ -215,6 +215,9 @@  int arp_mc_map(__be32 addr, u8 *haddr, struct net_device *dev, int dir)
 	case ARPHRD_INFINIBAND:
 		ip_ib_mc_map(addr, dev->broadcast, haddr);
 		return 0;
+	case ARPHRD_IPGRE:
+		ip_ipgre_mc_map(addr, dev->broadcast, haddr);
+		return 0;
 	default:
 		if (dir) {
 			memcpy(haddr, dev->broadcast, dev->addr_len);
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 0e49c9d..92f952d 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -341,6 +341,8 @@  int ndisc_mc_map(struct in6_addr *addr, char *buf, struct net_device *dev, int d
 	case ARPHRD_INFINIBAND:
 		ipv6_ib_mc_map(addr, dev->broadcast, buf);
 		return 0;
+	case ARPHRD_IPGRE:
+		return ipv6_ipgre_mc_map(addr, dev->broadcast, buf);
 	default:
 		if (dir) {
 			memcpy(buf, dev->broadcast, dev->addr_len);