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

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

authored by

Linus Lüssing and committed by
David S. Miller
4b3087c7 4effd28c

+82 -1
+5
include/linux/in.h
··· 60 60 return addr == htonl(INADDR_BROADCAST); 61 61 } 62 62 63 + static inline bool ipv4_is_all_snoopers(__be32 addr) 64 + { 65 + return addr == htonl(INADDR_ALLSNOOPERS_GROUP); 66 + } 67 + 63 68 static inline bool ipv4_is_zeronet(__be32 addr) 64 69 { 65 70 return (addr & htonl(0xff000000)) == htonl(0x00000000);
+15
include/net/addrconf.h
··· 229 229 void ipv6_mc_remap(struct inet6_dev *idev); 230 230 void ipv6_mc_init_dev(struct inet6_dev *idev); 231 231 void ipv6_mc_destroy_dev(struct inet6_dev *idev); 232 + int ipv6_mc_check_icmpv6(struct sk_buff *skb); 232 233 int ipv6_mc_check_mld(struct sk_buff *skb); 233 234 void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp); 234 235 ··· 497 496 addr->s6_addr32[1] | 498 497 (addr->s6_addr32[2] ^ htonl(0x00000001)) | 499 498 (addr->s6_addr[12] ^ 0xff)) == 0; 499 + #endif 500 + } 501 + 502 + static inline bool ipv6_addr_is_all_snoopers(const struct in6_addr *addr) 503 + { 504 + #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64 505 + __be64 *p = (__be64 *)addr; 506 + 507 + return ((p[0] ^ cpu_to_be64(0xff02000000000000UL)) | 508 + (p[1] ^ cpu_to_be64(0x6a))) == 0UL; 509 + #else 510 + return ((addr->s6_addr32[0] ^ htonl(0xff020000)) | 511 + addr->s6_addr32[1] | addr->s6_addr32[2] | 512 + (addr->s6_addr32[3] ^ htonl(0x0000006a))) == 0; 500 513 #endif 501 514 } 502 515
+2
include/uapi/linux/icmpv6.h
··· 108 108 #define ICMPV6_MOBILE_PREFIX_SOL 146 109 109 #define ICMPV6_MOBILE_PREFIX_ADV 147 110 110 111 + #define ICMPV6_MRDISC_ADV 151 112 + 111 113 /* 112 114 * Codes for Destination Unreachable 113 115 */
+1
include/uapi/linux/igmp.h
··· 93 93 #define IGMP_MTRACE_RESP 0x1e 94 94 #define IGMP_MTRACE 0x1f 95 95 96 + #define IGMP_MRDISC_ADV 0x30 /* From RFC4286 */ 96 97 97 98 /* 98 99 * Use the BSD names for these for compatibility
+55
net/bridge/br_multicast.c
··· 14 14 #include <linux/export.h> 15 15 #include <linux/if_ether.h> 16 16 #include <linux/igmp.h> 17 + #include <linux/in.h> 17 18 #include <linux/jhash.h> 18 19 #include <linux/kernel.h> 19 20 #include <linux/log2.h> ··· 30 29 #include <net/ip.h> 31 30 #include <net/switchdev.h> 32 31 #if IS_ENABLED(CONFIG_IPV6) 32 + #include <linux/icmpv6.h> 33 33 #include <net/ipv6.h> 34 34 #include <net/mld.h> 35 35 #include <net/ip6_checksum.h> 36 36 #include <net/addrconf.h> 37 + #include <net/ipv6.h> 37 38 #endif 38 39 39 40 #include "br_private.h" ··· 1586 1583 br_multicast_mark_router(br, port); 1587 1584 } 1588 1585 1586 + static int br_ip4_multicast_mrd_rcv(struct net_bridge *br, 1587 + struct net_bridge_port *port, 1588 + struct sk_buff *skb) 1589 + { 1590 + if (ip_hdr(skb)->protocol != IPPROTO_IGMP || 1591 + igmp_hdr(skb)->type != IGMP_MRDISC_ADV) 1592 + return -ENOMSG; 1593 + 1594 + br_multicast_mark_router(br, port); 1595 + 1596 + return 0; 1597 + } 1598 + 1589 1599 static int br_multicast_ipv4_rcv(struct net_bridge *br, 1590 1600 struct net_bridge_port *port, 1591 1601 struct sk_buff *skb, ··· 1616 1600 } else if (pim_ipv4_all_pim_routers(ip_hdr(skb)->daddr)) { 1617 1601 if (ip_hdr(skb)->protocol == IPPROTO_PIM) 1618 1602 br_multicast_pim(br, port, skb); 1603 + } else if (ipv4_is_all_snoopers(ip_hdr(skb)->daddr)) { 1604 + err = br_ip4_multicast_mrd_rcv(br, port, skb); 1605 + 1606 + if (err < 0 && err != -ENOMSG) { 1607 + br_multicast_err_count(br, port, skb->protocol); 1608 + return err; 1609 + } 1619 1610 } 1611 + 1620 1612 return 0; 1621 1613 } else if (err < 0) { 1622 1614 br_multicast_err_count(br, port, skb->protocol); ··· 1659 1635 } 1660 1636 1661 1637 #if IS_ENABLED(CONFIG_IPV6) 1638 + static int br_ip6_multicast_mrd_rcv(struct net_bridge *br, 1639 + struct net_bridge_port *port, 1640 + struct sk_buff *skb) 1641 + { 1642 + int ret; 1643 + 1644 + if (ipv6_hdr(skb)->nexthdr != IPPROTO_ICMPV6) 1645 + return -ENOMSG; 1646 + 1647 + ret = ipv6_mc_check_icmpv6(skb); 1648 + if (ret < 0) 1649 + return ret; 1650 + 1651 + if (icmp6_hdr(skb)->icmp6_type != ICMPV6_MRDISC_ADV) 1652 + return -ENOMSG; 1653 + 1654 + br_multicast_mark_router(br, port); 1655 + 1656 + return 0; 1657 + } 1658 + 1662 1659 static int br_multicast_ipv6_rcv(struct net_bridge *br, 1663 1660 struct net_bridge_port *port, 1664 1661 struct sk_buff *skb, ··· 1694 1649 if (err == -ENOMSG) { 1695 1650 if (!ipv6_addr_is_ll_all_nodes(&ipv6_hdr(skb)->daddr)) 1696 1651 BR_INPUT_SKB_CB(skb)->mrouters_only = 1; 1652 + 1653 + if (ipv6_addr_is_all_snoopers(&ipv6_hdr(skb)->daddr)) { 1654 + err = br_ip6_multicast_mrd_rcv(br, port, skb); 1655 + 1656 + if (err < 0 && err != -ENOMSG) { 1657 + br_multicast_err_count(br, port, skb->protocol); 1658 + return err; 1659 + } 1660 + } 1661 + 1697 1662 return 0; 1698 1663 } else if (err < 0) { 1699 1664 br_multicast_err_count(br, port, skb->protocol);
+4 -1
net/ipv6/mcast_snoop.c
··· 41 41 if (skb->len < len || len <= offset) 42 42 return -EINVAL; 43 43 44 + skb_set_transport_header(skb, offset); 45 + 44 46 return 0; 45 47 } 46 48 ··· 144 142 return skb_checksum_validate(skb, IPPROTO_ICMPV6, ip6_compute_pseudo); 145 143 } 146 144 147 - static int ipv6_mc_check_icmpv6(struct sk_buff *skb) 145 + int ipv6_mc_check_icmpv6(struct sk_buff *skb) 148 146 { 149 147 unsigned int len = skb_transport_offset(skb) + sizeof(struct icmp6hdr); 150 148 unsigned int transport_len = ipv6_transport_len(skb); ··· 163 161 164 162 return 0; 165 163 } 164 + EXPORT_SYMBOL(ipv6_mc_check_icmpv6); 166 165 167 166 /** 168 167 * ipv6_mc_check_mld - checks whether this is a sane MLD packet