Skip to content

Commit 4b3087c

Browse files
T-Xdavem330
authored andcommitted
bridge: Snoop Multicast Router Advertisements
When multiple multicast routers are present in a broadcast domain then only one of them will be detectable via IGMP/MLD query snooping. The multicast router with the lowest IP address will become the selected and active querier while all other multicast routers will then refrain from sending queries. To detect such rather silent multicast routers, too, RFC4286 ("Multicast Router Discovery") provides a standardized protocol to detect multicast routers for multicast snooping switches. This patch implements the necessary MRD Advertisement message parsing and after successful processing adds such routers to the internal multicast router list. Signed-off-by: Linus Lüssing <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 4effd28 commit 4b3087c

File tree

6 files changed

+82
-1
lines changed

6 files changed

+82
-1
lines changed

include/linux/in.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ static inline bool ipv4_is_lbcast(__be32 addr)
6060
return addr == htonl(INADDR_BROADCAST);
6161
}
6262

63+
static inline bool ipv4_is_all_snoopers(__be32 addr)
64+
{
65+
return addr == htonl(INADDR_ALLSNOOPERS_GROUP);
66+
}
67+
6368
static inline bool ipv4_is_zeronet(__be32 addr)
6469
{
6570
return (addr & htonl(0xff000000)) == htonl(0x00000000);

include/net/addrconf.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ void ipv6_mc_unmap(struct inet6_dev *idev);
229229
void ipv6_mc_remap(struct inet6_dev *idev);
230230
void ipv6_mc_init_dev(struct inet6_dev *idev);
231231
void ipv6_mc_destroy_dev(struct inet6_dev *idev);
232+
int ipv6_mc_check_icmpv6(struct sk_buff *skb);
232233
int ipv6_mc_check_mld(struct sk_buff *skb);
233234
void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp);
234235

@@ -499,6 +500,20 @@ static inline bool ipv6_addr_is_solict_mult(const struct in6_addr *addr)
499500
#endif
500501
}
501502

503+
static inline bool ipv6_addr_is_all_snoopers(const struct in6_addr *addr)
504+
{
505+
#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64
506+
__be64 *p = (__be64 *)addr;
507+
508+
return ((p[0] ^ cpu_to_be64(0xff02000000000000UL)) |
509+
(p[1] ^ cpu_to_be64(0x6a))) == 0UL;
510+
#else
511+
return ((addr->s6_addr32[0] ^ htonl(0xff020000)) |
512+
addr->s6_addr32[1] | addr->s6_addr32[2] |
513+
(addr->s6_addr32[3] ^ htonl(0x0000006a))) == 0;
514+
#endif
515+
}
516+
502517
#ifdef CONFIG_PROC_FS
503518
int if6_proc_init(void);
504519
void if6_proc_exit(void);

include/uapi/linux/icmpv6.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ struct icmp6hdr {
108108
#define ICMPV6_MOBILE_PREFIX_SOL 146
109109
#define ICMPV6_MOBILE_PREFIX_ADV 147
110110

111+
#define ICMPV6_MRDISC_ADV 151
112+
111113
/*
112114
* Codes for Destination Unreachable
113115
*/

include/uapi/linux/igmp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ struct igmpv3_query {
9393
#define IGMP_MTRACE_RESP 0x1e
9494
#define IGMP_MTRACE 0x1f
9595

96+
#define IGMP_MRDISC_ADV 0x30 /* From RFC4286 */
9697

9798
/*
9899
* Use the BSD names for these for compatibility

net/bridge/br_multicast.c

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <linux/export.h>
1515
#include <linux/if_ether.h>
1616
#include <linux/igmp.h>
17+
#include <linux/in.h>
1718
#include <linux/jhash.h>
1819
#include <linux/kernel.h>
1920
#include <linux/log2.h>
@@ -29,10 +30,12 @@
2930
#include <net/ip.h>
3031
#include <net/switchdev.h>
3132
#if IS_ENABLED(CONFIG_IPV6)
33+
#include <linux/icmpv6.h>
3234
#include <net/ipv6.h>
3335
#include <net/mld.h>
3436
#include <net/ip6_checksum.h>
3537
#include <net/addrconf.h>
38+
#include <net/ipv6.h>
3639
#endif
3740

3841
#include "br_private.h"
@@ -1583,6 +1586,19 @@ static void br_multicast_pim(struct net_bridge *br,
15831586
br_multicast_mark_router(br, port);
15841587
}
15851588

1589+
static int br_ip4_multicast_mrd_rcv(struct net_bridge *br,
1590+
struct net_bridge_port *port,
1591+
struct sk_buff *skb)
1592+
{
1593+
if (ip_hdr(skb)->protocol != IPPROTO_IGMP ||
1594+
igmp_hdr(skb)->type != IGMP_MRDISC_ADV)
1595+
return -ENOMSG;
1596+
1597+
br_multicast_mark_router(br, port);
1598+
1599+
return 0;
1600+
}
1601+
15861602
static int br_multicast_ipv4_rcv(struct net_bridge *br,
15871603
struct net_bridge_port *port,
15881604
struct sk_buff *skb,
@@ -1600,7 +1616,15 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
16001616
} else if (pim_ipv4_all_pim_routers(ip_hdr(skb)->daddr)) {
16011617
if (ip_hdr(skb)->protocol == IPPROTO_PIM)
16021618
br_multicast_pim(br, port, skb);
1619+
} else if (ipv4_is_all_snoopers(ip_hdr(skb)->daddr)) {
1620+
err = br_ip4_multicast_mrd_rcv(br, port, skb);
1621+
1622+
if (err < 0 && err != -ENOMSG) {
1623+
br_multicast_err_count(br, port, skb->protocol);
1624+
return err;
1625+
}
16031626
}
1627+
16041628
return 0;
16051629
} else if (err < 0) {
16061630
br_multicast_err_count(br, port, skb->protocol);
@@ -1635,6 +1659,27 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
16351659
}
16361660

16371661
#if IS_ENABLED(CONFIG_IPV6)
1662+
static int br_ip6_multicast_mrd_rcv(struct net_bridge *br,
1663+
struct net_bridge_port *port,
1664+
struct sk_buff *skb)
1665+
{
1666+
int ret;
1667+
1668+
if (ipv6_hdr(skb)->nexthdr != IPPROTO_ICMPV6)
1669+
return -ENOMSG;
1670+
1671+
ret = ipv6_mc_check_icmpv6(skb);
1672+
if (ret < 0)
1673+
return ret;
1674+
1675+
if (icmp6_hdr(skb)->icmp6_type != ICMPV6_MRDISC_ADV)
1676+
return -ENOMSG;
1677+
1678+
br_multicast_mark_router(br, port);
1679+
1680+
return 0;
1681+
}
1682+
16381683
static int br_multicast_ipv6_rcv(struct net_bridge *br,
16391684
struct net_bridge_port *port,
16401685
struct sk_buff *skb,
@@ -1649,6 +1694,16 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,
16491694
if (err == -ENOMSG) {
16501695
if (!ipv6_addr_is_ll_all_nodes(&ipv6_hdr(skb)->daddr))
16511696
BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
1697+
1698+
if (ipv6_addr_is_all_snoopers(&ipv6_hdr(skb)->daddr)) {
1699+
err = br_ip6_multicast_mrd_rcv(br, port, skb);
1700+
1701+
if (err < 0 && err != -ENOMSG) {
1702+
br_multicast_err_count(br, port, skb->protocol);
1703+
return err;
1704+
}
1705+
}
1706+
16521707
return 0;
16531708
} else if (err < 0) {
16541709
br_multicast_err_count(br, port, skb->protocol);

net/ipv6/mcast_snoop.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ static int ipv6_mc_check_ip6hdr(struct sk_buff *skb)
4141
if (skb->len < len || len <= offset)
4242
return -EINVAL;
4343

44+
skb_set_transport_header(skb, offset);
45+
4446
return 0;
4547
}
4648

@@ -142,7 +144,7 @@ static inline __sum16 ipv6_mc_validate_checksum(struct sk_buff *skb)
142144
return skb_checksum_validate(skb, IPPROTO_ICMPV6, ip6_compute_pseudo);
143145
}
144146

145-
static int ipv6_mc_check_icmpv6(struct sk_buff *skb)
147+
int ipv6_mc_check_icmpv6(struct sk_buff *skb)
146148
{
147149
unsigned int len = skb_transport_offset(skb) + sizeof(struct icmp6hdr);
148150
unsigned int transport_len = ipv6_transport_len(skb);
@@ -161,6 +163,7 @@ static int ipv6_mc_check_icmpv6(struct sk_buff *skb)
161163

162164
return 0;
163165
}
166+
EXPORT_SYMBOL(ipv6_mc_check_icmpv6);
164167

165168
/**
166169
* ipv6_mc_check_mld - checks whether this is a sane MLD packet

0 commit comments

Comments
 (0)