Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: gre: provide multicast mappings for ipv4 and ipv6

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>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Timo Teräs and committed by
David S. Miller
93ca3bb5 1459a3cc

+29
+16
include/net/if_inet6.h
··· 286 286 buf[9] = broadcast[9]; 287 287 memcpy(buf + 10, addr->s6_addr + 6, 10); 288 288 } 289 + 290 + static inline int ipv6_ipgre_mc_map(const struct in6_addr *addr, 291 + const unsigned char *broadcast, char *buf) 292 + { 293 + if ((broadcast[0] | broadcast[1] | broadcast[2] | broadcast[3]) != 0) { 294 + memcpy(buf, broadcast, 4); 295 + } else { 296 + /* v4mapped? */ 297 + if ((addr->s6_addr32[0] | addr->s6_addr32[1] | 298 + (addr->s6_addr32[2] ^ htonl(0x0000ffff))) != 0) 299 + return -EINVAL; 300 + memcpy(buf, &addr->s6_addr32[3], 4); 301 + } 302 + return 0; 303 + } 304 + 289 305 #endif 290 306 #endif
+8
include/net/ip.h
··· 339 339 buf[16] = addr & 0x0f; 340 340 } 341 341 342 + static inline void ip_ipgre_mc_map(__be32 naddr, const unsigned char *broadcast, char *buf) 343 + { 344 + if ((broadcast[0] | broadcast[1] | broadcast[2] | broadcast[3]) != 0) 345 + memcpy(buf, broadcast, 4); 346 + else 347 + memcpy(buf, &naddr, sizeof(naddr)); 348 + } 349 + 342 350 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 343 351 #include <linux/ipv6.h> 344 352 #endif
+3
net/ipv4/arp.c
··· 215 215 case ARPHRD_INFINIBAND: 216 216 ip_ib_mc_map(addr, dev->broadcast, haddr); 217 217 return 0; 218 + case ARPHRD_IPGRE: 219 + ip_ipgre_mc_map(addr, dev->broadcast, haddr); 220 + return 0; 218 221 default: 219 222 if (dir) { 220 223 memcpy(haddr, dev->broadcast, dev->addr_len);
+2
net/ipv6/ndisc.c
··· 341 341 case ARPHRD_INFINIBAND: 342 342 ipv6_ib_mc_map(addr, dev->broadcast, buf); 343 343 return 0; 344 + case ARPHRD_IPGRE: 345 + return ipv6_ipgre_mc_map(addr, dev->broadcast, buf); 344 346 default: 345 347 if (dir) { 346 348 memcpy(buf, dev->broadcast, dev->addr_len);