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

net: bridge: mcast: add support for raw L2 multicast groups

Extend the bridge multicast control and data path to configure routes
for L2 (non-IP) multicast groups.

The uapi struct br_mdb_entry union u is extended with another variant,
mac_addr, which does not change the structure size, and which is valid
when the proto field is zero.

To be compatible with the forwarding code that is already in place,
which acts as an IGMP/MLD snooping bridge with querier capabilities, we
need to declare that for L2 MDB entries (for which there exists no such
thing as IGMP/MLD snooping/querying), that there is always a querier.
Otherwise, these entries would be flooded to all bridge ports and not
just to those that are members of the L2 multicast group.

Needless to say, only permanent L2 multicast groups can be installed on
a bridge port.

Signed-off-by: Nikolay Aleksandrov <nikolay@nvidia.com>
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Link: https://lore.kernel.org/r/20201028233831.610076-1-vladimir.oltean@nxp.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Nikolay Aleksandrov and committed by
Jakub Kicinski
955062b0 8ece853d

+43 -10
+1
include/linux/if_bridge.h
··· 25 25 #if IS_ENABLED(CONFIG_IPV6) 26 26 struct in6_addr ip6; 27 27 #endif 28 + unsigned char mac_addr[ETH_ALEN]; 28 29 } dst; 29 30 __be16 proto; 30 31 __u16 vid;
+1
include/uapi/linux/if_bridge.h
··· 651 651 union { 652 652 __be32 ip4; 653 653 struct in6_addr ip6; 654 + unsigned char mac_addr[ETH_ALEN]; 654 655 } u; 655 656 __be16 proto; 656 657 } addr;
+1 -1
net/bridge/br_device.c
··· 93 93 94 94 mdst = br_mdb_get(br, skb, vid); 95 95 if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) && 96 - br_multicast_querier_exists(br, eth_hdr(skb))) 96 + br_multicast_querier_exists(br, eth_hdr(skb), mdst)) 97 97 br_multicast_flood(mdst, skb, false, true); 98 98 else 99 99 br_flood(br, skb, BR_PKT_MULTICAST, false, true);
+1 -1
net/bridge/br_input.c
··· 134 134 case BR_PKT_MULTICAST: 135 135 mdst = br_mdb_get(br, skb, vid); 136 136 if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) && 137 - br_multicast_querier_exists(br, eth_hdr(skb))) { 137 + br_multicast_querier_exists(br, eth_hdr(skb), mdst)) { 138 138 if ((mdst && mdst->host_joined) || 139 139 br_multicast_is_router(br)) { 140 140 local_rcv = true;
+22 -2
net/bridge/br_mdb.c
··· 87 87 ip->src.ip6 = nla_get_in6_addr(mdb_attrs[MDBE_ATTR_SOURCE]); 88 88 break; 89 89 #endif 90 + default: 91 + ether_addr_copy(ip->dst.mac_addr, entry->addr.u.mac_addr); 90 92 } 91 93 92 94 } ··· 176 174 if (mp->addr.proto == htons(ETH_P_IP)) 177 175 e.addr.u.ip4 = mp->addr.dst.ip4; 178 176 #if IS_ENABLED(CONFIG_IPV6) 179 - if (mp->addr.proto == htons(ETH_P_IPV6)) 177 + else if (mp->addr.proto == htons(ETH_P_IPV6)) 180 178 e.addr.u.ip6 = mp->addr.dst.ip6; 181 179 #endif 180 + else 181 + ether_addr_copy(e.addr.u.mac_addr, mp->addr.dst.mac_addr); 182 182 e.addr.proto = mp->addr.proto; 183 183 nest_ent = nla_nest_start_noflag(skb, 184 184 MDBA_MDB_ENTRY_INFO); ··· 214 210 } 215 211 break; 216 212 #endif 213 + default: 214 + ether_addr_copy(e.addr.u.mac_addr, mp->addr.dst.mac_addr); 217 215 } 218 216 if (p) { 219 217 if (nla_put_u8(skb, MDBA_MDB_EATTR_RTPROT, p->rt_protocol)) ··· 568 562 if (mp->addr.proto == htons(ETH_P_IP)) 569 563 ip_eth_mc_map(mp->addr.dst.ip4, mdb.addr); 570 564 #if IS_ENABLED(CONFIG_IPV6) 571 - else 565 + else if (mp->addr.proto == htons(ETH_P_IPV6)) 572 566 ipv6_eth_mc_map(&mp->addr.dst.ip6, mdb.addr); 573 567 #endif 568 + else 569 + ether_addr_copy(mdb.addr, mp->addr.dst.mac_addr); 570 + 574 571 mdb.obj.orig_dev = pg->key.port->dev; 575 572 switch (type) { 576 573 case RTM_NEWMDB: ··· 702 693 return false; 703 694 } 704 695 #endif 696 + } else if (entry->addr.proto == 0) { 697 + /* L2 mdb */ 698 + if (!is_multicast_ether_addr(entry->addr.u.mac_addr)) { 699 + NL_SET_ERR_MSG_MOD(extack, "L2 entry group is not multicast"); 700 + return false; 701 + } 705 702 } else { 706 703 NL_SET_ERR_MSG_MOD(extack, "Unknown entry protocol"); 707 704 return false; ··· 862 847 NL_SET_ERR_MSG_MOD(extack, "Groups with sources cannot be manually host joined"); 863 848 return -EINVAL; 864 849 } 850 + } 851 + 852 + if (br_group_is_l2(&group) && entry->state != MDB_PERMANENT) { 853 + NL_SET_ERR_MSG_MOD(extack, "Only permanent L2 entries allowed"); 854 + return -EINVAL; 865 855 } 866 856 867 857 mp = br_mdb_ip_get(br, &group);
+9 -4
net/bridge/br_multicast.c
··· 179 179 break; 180 180 #endif 181 181 default: 182 - return NULL; 182 + ip.proto = 0; 183 + ether_addr_copy(ip.dst.mac_addr, eth_hdr(skb)->h_dest); 183 184 } 184 185 185 186 return br_mdb_ip_get_rcu(br, &ip); ··· 1204 1203 if (notify) 1205 1204 br_mdb_notify(mp->br->dev, mp, NULL, RTM_NEWMDB); 1206 1205 } 1206 + 1207 + if (br_group_is_l2(&mp->addr)) 1208 + return; 1209 + 1207 1210 mod_timer(&mp->timer, jiffies + mp->br->multicast_membership_interval); 1208 1211 } 1209 1212 ··· 1259 1254 break; 1260 1255 } 1261 1256 1262 - p = br_multicast_new_port_group(port, group, *pp, 0, src, filter_mode, 1263 - RTPROT_KERNEL); 1257 + p = br_multicast_new_port_group(port, group, *pp, 0, src, 1258 + filter_mode, RTPROT_KERNEL); 1264 1259 if (unlikely(!p)) { 1265 1260 p = ERR_PTR(-ENOMEM); 1266 1261 goto out; ··· 3695 3690 memset(&eth, 0, sizeof(eth)); 3696 3691 eth.h_proto = htons(proto); 3697 3692 3698 - ret = br_multicast_querier_exists(br, &eth); 3693 + ret = br_multicast_querier_exists(br, &eth, NULL); 3699 3694 3700 3695 unlock: 3701 3696 rcu_read_unlock();
+8 -2
net/bridge/br_private.h
··· 854 854 void br_multicast_sg_add_exclude_ports(struct net_bridge_mdb_entry *star_mp, 855 855 struct net_bridge_port_group *sg); 856 856 857 + static inline bool br_group_is_l2(const struct br_ip *group) 858 + { 859 + return group->proto == 0; 860 + } 861 + 857 862 #define mlock_dereference(X, br) \ 858 863 rcu_dereference_protected(X, lockdep_is_held(&br->multicast_lock)) 859 864 ··· 890 885 } 891 886 892 887 static inline bool br_multicast_querier_exists(struct net_bridge *br, 893 - struct ethhdr *eth) 888 + struct ethhdr *eth, 889 + const struct net_bridge_mdb_entry *mdb) 894 890 { 895 891 switch (eth->h_proto) { 896 892 case (htons(ETH_P_IP)): ··· 903 897 &br->ip6_other_query, true); 904 898 #endif 905 899 default: 906 - return false; 900 + return !!mdb && br_group_is_l2(&mdb->addr); 907 901 } 908 902 } 909 903