12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2eb1d1641SHerbert Xu /* 3eb1d1641SHerbert Xu * Bridge multicast support. 4eb1d1641SHerbert Xu * 5eb1d1641SHerbert Xu * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au> 6eb1d1641SHerbert Xu */ 7eb1d1641SHerbert Xu 8eb1d1641SHerbert Xu #include <linux/err.h> 907f8ac4aSLinus Lüssing #include <linux/export.h> 10eb1d1641SHerbert Xu #include <linux/if_ether.h> 11eb1d1641SHerbert Xu #include <linux/igmp.h> 124b3087c7SLinus Lüssing #include <linux/in.h> 13eb1d1641SHerbert Xu #include <linux/jhash.h> 14eb1d1641SHerbert Xu #include <linux/kernel.h> 15b195167fSHerbert Xu #include <linux/log2.h> 16eb1d1641SHerbert Xu #include <linux/netdevice.h> 17eb1d1641SHerbert Xu #include <linux/netfilter_bridge.h> 18eb1d1641SHerbert Xu #include <linux/random.h> 19eb1d1641SHerbert Xu #include <linux/rculist.h> 20eb1d1641SHerbert Xu #include <linux/skbuff.h> 21eb1d1641SHerbert Xu #include <linux/slab.h> 22eb1d1641SHerbert Xu #include <linux/timer.h> 231c8ad5bfSCong Wang #include <linux/inetdevice.h> 2491b02d3dSNikolay Aleksandrov #include <linux/mroute.h> 25eb1d1641SHerbert Xu #include <net/ip.h> 26147c1e9bSNogah Frankel #include <net/switchdev.h> 27dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 284b3087c7SLinus Lüssing #include <linux/icmpv6.h> 2908b202b6SYOSHIFUJI Hideaki #include <net/ipv6.h> 3008b202b6SYOSHIFUJI Hideaki #include <net/mld.h> 31d4c4f07dSDavid S. Miller #include <net/ip6_checksum.h> 323c3769e6SLinus Lüssing #include <net/addrconf.h> 3308b202b6SYOSHIFUJI Hideaki #endif 34eb1d1641SHerbert Xu 35eb1d1641SHerbert Xu #include "br_private.h" 36eb1d1641SHerbert Xu 3719e3a9c9SNikolay Aleksandrov static const struct rhashtable_params br_mdb_rht_params = { 3819e3a9c9SNikolay Aleksandrov .head_offset = offsetof(struct net_bridge_mdb_entry, rhnode), 3919e3a9c9SNikolay Aleksandrov .key_offset = offsetof(struct net_bridge_mdb_entry, addr), 4019e3a9c9SNikolay Aleksandrov .key_len = sizeof(struct br_ip), 4119e3a9c9SNikolay Aleksandrov .automatic_shrinking = true, 4219e3a9c9SNikolay Aleksandrov }; 4319e3a9c9SNikolay Aleksandrov 44085b53c8SNikolay Aleksandrov static const struct rhashtable_params br_sg_port_rht_params = { 45085b53c8SNikolay Aleksandrov .head_offset = offsetof(struct net_bridge_port_group, rhnode), 46085b53c8SNikolay Aleksandrov .key_offset = offsetof(struct net_bridge_port_group, key), 47085b53c8SNikolay Aleksandrov .key_len = sizeof(struct net_bridge_port_group_sg_key), 48085b53c8SNikolay Aleksandrov .automatic_shrinking = true, 49085b53c8SNikolay Aleksandrov }; 50085b53c8SNikolay Aleksandrov 51cc0fdd80SLinus Lüssing static void br_multicast_start_querier(struct net_bridge *br, 5290010b36SLinus Lüssing struct bridge_mcast_own_query *query); 53754bc547SSatish Ashok static void br_multicast_add_router(struct net_bridge *br, 54754bc547SSatish Ashok struct net_bridge_port *port); 55bc8c20acSSatish Ashok static void br_ip4_multicast_leave_group(struct net_bridge *br, 56bc8c20acSSatish Ashok struct net_bridge_port *port, 57bc8c20acSSatish Ashok __be32 group, 586db6f0eaSFelix Fietkau __u16 vid, 596db6f0eaSFelix Fietkau const unsigned char *src); 6042c11ccfSNikolay Aleksandrov static void br_multicast_port_group_rexmit(struct timer_list *t); 616db6f0eaSFelix Fietkau 62f12e7d95SNogah Frankel static void __del_port_router(struct net_bridge_port *p); 63bc8c20acSSatish Ashok #if IS_ENABLED(CONFIG_IPV6) 64bc8c20acSSatish Ashok static void br_ip6_multicast_leave_group(struct net_bridge *br, 65bc8c20acSSatish Ashok struct net_bridge_port *port, 66bc8c20acSSatish Ashok const struct in6_addr *group, 676db6f0eaSFelix Fietkau __u16 vid, const unsigned char *src); 68bc8c20acSSatish Ashok #endif 69b0812368SNikolay Aleksandrov static struct net_bridge_port_group * 70b0812368SNikolay Aleksandrov __br_multicast_add_group(struct net_bridge *br, 71b0812368SNikolay Aleksandrov struct net_bridge_port *port, 72b0812368SNikolay Aleksandrov struct br_ip *group, 73b0812368SNikolay Aleksandrov const unsigned char *src, 74b0812368SNikolay Aleksandrov u8 filter_mode, 75b0812368SNikolay Aleksandrov bool igmpv2_mldv1); 76*8266a049SNikolay Aleksandrov static void br_multicast_find_del_pg(struct net_bridge *br, 77*8266a049SNikolay Aleksandrov struct net_bridge_port_group *pg); 78c83b8fabSHerbert Xu 79085b53c8SNikolay Aleksandrov static struct net_bridge_port_group * 80085b53c8SNikolay Aleksandrov br_sg_port_find(struct net_bridge *br, 81085b53c8SNikolay Aleksandrov struct net_bridge_port_group_sg_key *sg_p) 82085b53c8SNikolay Aleksandrov { 83085b53c8SNikolay Aleksandrov lockdep_assert_held_once(&br->multicast_lock); 84085b53c8SNikolay Aleksandrov 85085b53c8SNikolay Aleksandrov return rhashtable_lookup_fast(&br->sg_port_tbl, sg_p, 86085b53c8SNikolay Aleksandrov br_sg_port_rht_params); 87085b53c8SNikolay Aleksandrov } 88085b53c8SNikolay Aleksandrov 8919e3a9c9SNikolay Aleksandrov static struct net_bridge_mdb_entry *br_mdb_ip_get_rcu(struct net_bridge *br, 90cfd56754SCong Wang struct br_ip *dst) 917f285fa7SHerbert Xu { 9219e3a9c9SNikolay Aleksandrov return rhashtable_lookup(&br->mdb_hash_tbl, dst, br_mdb_rht_params); 937f285fa7SHerbert Xu } 947f285fa7SHerbert Xu 9519e3a9c9SNikolay Aleksandrov struct net_bridge_mdb_entry *br_mdb_ip_get(struct net_bridge *br, 9619e3a9c9SNikolay Aleksandrov struct br_ip *dst) 9719e3a9c9SNikolay Aleksandrov { 9819e3a9c9SNikolay Aleksandrov struct net_bridge_mdb_entry *ent; 9919e3a9c9SNikolay Aleksandrov 10019e3a9c9SNikolay Aleksandrov lockdep_assert_held_once(&br->multicast_lock); 10119e3a9c9SNikolay Aleksandrov 10219e3a9c9SNikolay Aleksandrov rcu_read_lock(); 10319e3a9c9SNikolay Aleksandrov ent = rhashtable_lookup(&br->mdb_hash_tbl, dst, br_mdb_rht_params); 10419e3a9c9SNikolay Aleksandrov rcu_read_unlock(); 10519e3a9c9SNikolay Aleksandrov 10619e3a9c9SNikolay Aleksandrov return ent; 10719e3a9c9SNikolay Aleksandrov } 10819e3a9c9SNikolay Aleksandrov 10919e3a9c9SNikolay Aleksandrov static struct net_bridge_mdb_entry *br_mdb_ip4_get(struct net_bridge *br, 11019e3a9c9SNikolay Aleksandrov __be32 dst, __u16 vid) 111eb1d1641SHerbert Xu { 1128ef2a9a5SYOSHIFUJI Hideaki struct br_ip br_dst; 1130821ec55SHerbert Xu 11419e3a9c9SNikolay Aleksandrov memset(&br_dst, 0, sizeof(br_dst)); 115eab3227bSNikolay Aleksandrov br_dst.dst.ip4 = dst; 1168ef2a9a5SYOSHIFUJI Hideaki br_dst.proto = htons(ETH_P_IP); 117b0e9a30dSVlad Yasevich br_dst.vid = vid; 1188ef2a9a5SYOSHIFUJI Hideaki 11919e3a9c9SNikolay Aleksandrov return br_mdb_ip_get(br, &br_dst); 1208ef2a9a5SYOSHIFUJI Hideaki } 1218ef2a9a5SYOSHIFUJI Hideaki 122dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 12319e3a9c9SNikolay Aleksandrov static struct net_bridge_mdb_entry *br_mdb_ip6_get(struct net_bridge *br, 12419e3a9c9SNikolay Aleksandrov const struct in6_addr *dst, 125b0e9a30dSVlad Yasevich __u16 vid) 12608b202b6SYOSHIFUJI Hideaki { 12708b202b6SYOSHIFUJI Hideaki struct br_ip br_dst; 12808b202b6SYOSHIFUJI Hideaki 12919e3a9c9SNikolay Aleksandrov memset(&br_dst, 0, sizeof(br_dst)); 130eab3227bSNikolay Aleksandrov br_dst.dst.ip6 = *dst; 13108b202b6SYOSHIFUJI Hideaki br_dst.proto = htons(ETH_P_IPV6); 132b0e9a30dSVlad Yasevich br_dst.vid = vid; 13308b202b6SYOSHIFUJI Hideaki 13419e3a9c9SNikolay Aleksandrov return br_mdb_ip_get(br, &br_dst); 13508b202b6SYOSHIFUJI Hideaki } 13608b202b6SYOSHIFUJI Hideaki #endif 13708b202b6SYOSHIFUJI Hideaki 138eb1d1641SHerbert Xu struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br, 139fbca58a2SCong Wang struct sk_buff *skb, u16 vid) 140eb1d1641SHerbert Xu { 1418ef2a9a5SYOSHIFUJI Hideaki struct br_ip ip; 1428ef2a9a5SYOSHIFUJI Hideaki 14313cefad2SNikolay Aleksandrov if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) 144eb1d1641SHerbert Xu return NULL; 145eb1d1641SHerbert Xu 1468ef2a9a5SYOSHIFUJI Hideaki if (BR_INPUT_SKB_CB(skb)->igmp) 1478ef2a9a5SYOSHIFUJI Hideaki return NULL; 1488ef2a9a5SYOSHIFUJI Hideaki 14919e3a9c9SNikolay Aleksandrov memset(&ip, 0, sizeof(ip)); 1508ef2a9a5SYOSHIFUJI Hideaki ip.proto = skb->protocol; 151fbca58a2SCong Wang ip.vid = vid; 1528ef2a9a5SYOSHIFUJI Hideaki 153eb1d1641SHerbert Xu switch (skb->protocol) { 154eb1d1641SHerbert Xu case htons(ETH_P_IP): 155eab3227bSNikolay Aleksandrov ip.dst.ip4 = ip_hdr(skb)->daddr; 1567d07a68cSNikolay Aleksandrov if (br->multicast_igmp_version == 3) { 1577d07a68cSNikolay Aleksandrov struct net_bridge_mdb_entry *mdb; 1587d07a68cSNikolay Aleksandrov 1597d07a68cSNikolay Aleksandrov ip.src.ip4 = ip_hdr(skb)->saddr; 1607d07a68cSNikolay Aleksandrov mdb = br_mdb_ip_get_rcu(br, &ip); 1617d07a68cSNikolay Aleksandrov if (mdb) 1627d07a68cSNikolay Aleksandrov return mdb; 1637d07a68cSNikolay Aleksandrov ip.src.ip4 = 0; 1647d07a68cSNikolay Aleksandrov } 165eb1d1641SHerbert Xu break; 166dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 16708b202b6SYOSHIFUJI Hideaki case htons(ETH_P_IPV6): 168eab3227bSNikolay Aleksandrov ip.dst.ip6 = ipv6_hdr(skb)->daddr; 1697d07a68cSNikolay Aleksandrov if (br->multicast_mld_version == 2) { 1707d07a68cSNikolay Aleksandrov struct net_bridge_mdb_entry *mdb; 1717d07a68cSNikolay Aleksandrov 1727d07a68cSNikolay Aleksandrov ip.src.ip6 = ipv6_hdr(skb)->saddr; 1737d07a68cSNikolay Aleksandrov mdb = br_mdb_ip_get_rcu(br, &ip); 1747d07a68cSNikolay Aleksandrov if (mdb) 1757d07a68cSNikolay Aleksandrov return mdb; 1767d07a68cSNikolay Aleksandrov memset(&ip.src.ip6, 0, sizeof(ip.src.ip6)); 1777d07a68cSNikolay Aleksandrov } 17808b202b6SYOSHIFUJI Hideaki break; 17908b202b6SYOSHIFUJI Hideaki #endif 1808ef2a9a5SYOSHIFUJI Hideaki default: 1818ef2a9a5SYOSHIFUJI Hideaki return NULL; 182eb1d1641SHerbert Xu } 183eb1d1641SHerbert Xu 18419e3a9c9SNikolay Aleksandrov return br_mdb_ip_get_rcu(br, &ip); 185eb1d1641SHerbert Xu } 186eb1d1641SHerbert Xu 187b0812368SNikolay Aleksandrov static bool br_port_group_equal(struct net_bridge_port_group *p, 188b0812368SNikolay Aleksandrov struct net_bridge_port *port, 189b0812368SNikolay Aleksandrov const unsigned char *src) 190b0812368SNikolay Aleksandrov { 191b0812368SNikolay Aleksandrov if (p->key.port != port) 192b0812368SNikolay Aleksandrov return false; 193b0812368SNikolay Aleksandrov 194b0812368SNikolay Aleksandrov if (!(port->flags & BR_MULTICAST_TO_UNICAST)) 195b0812368SNikolay Aleksandrov return true; 196b0812368SNikolay Aleksandrov 197b0812368SNikolay Aleksandrov return ether_addr_equal(src, p->eth_addr); 198b0812368SNikolay Aleksandrov } 199b0812368SNikolay Aleksandrov 200*8266a049SNikolay Aleksandrov static void __fwd_add_star_excl(struct net_bridge_port_group *pg, 201*8266a049SNikolay Aleksandrov struct br_ip *sg_ip) 202*8266a049SNikolay Aleksandrov { 203*8266a049SNikolay Aleksandrov struct net_bridge_port_group_sg_key sg_key; 204*8266a049SNikolay Aleksandrov struct net_bridge *br = pg->key.port->br; 205*8266a049SNikolay Aleksandrov struct net_bridge_port_group *src_pg; 206*8266a049SNikolay Aleksandrov 207*8266a049SNikolay Aleksandrov memset(&sg_key, 0, sizeof(sg_key)); 208*8266a049SNikolay Aleksandrov sg_key.port = pg->key.port; 209*8266a049SNikolay Aleksandrov sg_key.addr = *sg_ip; 210*8266a049SNikolay Aleksandrov if (br_sg_port_find(br, &sg_key)) 211*8266a049SNikolay Aleksandrov return; 212*8266a049SNikolay Aleksandrov 213*8266a049SNikolay Aleksandrov src_pg = __br_multicast_add_group(br, pg->key.port, sg_ip, pg->eth_addr, 214*8266a049SNikolay Aleksandrov MCAST_INCLUDE, false); 215*8266a049SNikolay Aleksandrov if (IS_ERR_OR_NULL(src_pg) || 216*8266a049SNikolay Aleksandrov src_pg->rt_protocol != RTPROT_KERNEL) 217*8266a049SNikolay Aleksandrov return; 218*8266a049SNikolay Aleksandrov 219*8266a049SNikolay Aleksandrov src_pg->flags |= MDB_PG_FLAGS_STAR_EXCL; 220*8266a049SNikolay Aleksandrov } 221*8266a049SNikolay Aleksandrov 222*8266a049SNikolay Aleksandrov static void __fwd_del_star_excl(struct net_bridge_port_group *pg, 223*8266a049SNikolay Aleksandrov struct br_ip *sg_ip) 224*8266a049SNikolay Aleksandrov { 225*8266a049SNikolay Aleksandrov struct net_bridge_port_group_sg_key sg_key; 226*8266a049SNikolay Aleksandrov struct net_bridge *br = pg->key.port->br; 227*8266a049SNikolay Aleksandrov struct net_bridge_port_group *src_pg; 228*8266a049SNikolay Aleksandrov 229*8266a049SNikolay Aleksandrov memset(&sg_key, 0, sizeof(sg_key)); 230*8266a049SNikolay Aleksandrov sg_key.port = pg->key.port; 231*8266a049SNikolay Aleksandrov sg_key.addr = *sg_ip; 232*8266a049SNikolay Aleksandrov src_pg = br_sg_port_find(br, &sg_key); 233*8266a049SNikolay Aleksandrov if (!src_pg || !(src_pg->flags & MDB_PG_FLAGS_STAR_EXCL) || 234*8266a049SNikolay Aleksandrov src_pg->rt_protocol != RTPROT_KERNEL) 235*8266a049SNikolay Aleksandrov return; 236*8266a049SNikolay Aleksandrov 237*8266a049SNikolay Aleksandrov br_multicast_find_del_pg(br, src_pg); 238*8266a049SNikolay Aleksandrov } 239*8266a049SNikolay Aleksandrov 240*8266a049SNikolay Aleksandrov /* When a port group transitions to (or is added as) EXCLUDE we need to add it 241*8266a049SNikolay Aleksandrov * to all other ports' S,G entries which are not blocked by the current group 242*8266a049SNikolay Aleksandrov * for proper replication, the assumption is that any S,G blocked entries 243*8266a049SNikolay Aleksandrov * are already added so the S,G,port lookup should skip them. 244*8266a049SNikolay Aleksandrov * When a port group transitions from EXCLUDE -> INCLUDE mode or is being 245*8266a049SNikolay Aleksandrov * deleted we need to remove it from all ports' S,G entries where it was 246*8266a049SNikolay Aleksandrov * automatically installed before (i.e. where it's MDB_PG_FLAGS_STAR_EXCL). 247*8266a049SNikolay Aleksandrov */ 248*8266a049SNikolay Aleksandrov void br_multicast_star_g_handle_mode(struct net_bridge_port_group *pg, 249*8266a049SNikolay Aleksandrov u8 filter_mode) 250*8266a049SNikolay Aleksandrov { 251*8266a049SNikolay Aleksandrov struct net_bridge *br = pg->key.port->br; 252*8266a049SNikolay Aleksandrov struct net_bridge_port_group *pg_lst; 253*8266a049SNikolay Aleksandrov struct net_bridge_mdb_entry *mp; 254*8266a049SNikolay Aleksandrov struct br_ip sg_ip; 255*8266a049SNikolay Aleksandrov 256*8266a049SNikolay Aleksandrov if (WARN_ON(!br_multicast_is_star_g(&pg->key.addr))) 257*8266a049SNikolay Aleksandrov return; 258*8266a049SNikolay Aleksandrov 259*8266a049SNikolay Aleksandrov mp = br_mdb_ip_get(br, &pg->key.addr); 260*8266a049SNikolay Aleksandrov if (!mp) 261*8266a049SNikolay Aleksandrov return; 262*8266a049SNikolay Aleksandrov 263*8266a049SNikolay Aleksandrov memset(&sg_ip, 0, sizeof(sg_ip)); 264*8266a049SNikolay Aleksandrov sg_ip = pg->key.addr; 265*8266a049SNikolay Aleksandrov for (pg_lst = mlock_dereference(mp->ports, br); 266*8266a049SNikolay Aleksandrov pg_lst; 267*8266a049SNikolay Aleksandrov pg_lst = mlock_dereference(pg_lst->next, br)) { 268*8266a049SNikolay Aleksandrov struct net_bridge_group_src *src_ent; 269*8266a049SNikolay Aleksandrov 270*8266a049SNikolay Aleksandrov if (pg_lst == pg) 271*8266a049SNikolay Aleksandrov continue; 272*8266a049SNikolay Aleksandrov hlist_for_each_entry(src_ent, &pg_lst->src_list, node) { 273*8266a049SNikolay Aleksandrov if (!(src_ent->flags & BR_SGRP_F_INSTALLED)) 274*8266a049SNikolay Aleksandrov continue; 275*8266a049SNikolay Aleksandrov sg_ip.src = src_ent->addr.src; 276*8266a049SNikolay Aleksandrov switch (filter_mode) { 277*8266a049SNikolay Aleksandrov case MCAST_INCLUDE: 278*8266a049SNikolay Aleksandrov __fwd_del_star_excl(pg, &sg_ip); 279*8266a049SNikolay Aleksandrov break; 280*8266a049SNikolay Aleksandrov case MCAST_EXCLUDE: 281*8266a049SNikolay Aleksandrov __fwd_add_star_excl(pg, &sg_ip); 282*8266a049SNikolay Aleksandrov break; 283*8266a049SNikolay Aleksandrov } 284*8266a049SNikolay Aleksandrov } 285*8266a049SNikolay Aleksandrov } 286*8266a049SNikolay Aleksandrov } 287*8266a049SNikolay Aleksandrov 288*8266a049SNikolay Aleksandrov static void br_multicast_sg_del_exclude_ports(struct net_bridge_mdb_entry *sgmp) 289*8266a049SNikolay Aleksandrov { 290*8266a049SNikolay Aleksandrov struct net_bridge_port_group __rcu **pp; 291*8266a049SNikolay Aleksandrov struct net_bridge_port_group *p; 292*8266a049SNikolay Aleksandrov 293*8266a049SNikolay Aleksandrov /* *,G exclude ports are only added to S,G entries */ 294*8266a049SNikolay Aleksandrov if (WARN_ON(br_multicast_is_star_g(&sgmp->addr))) 295*8266a049SNikolay Aleksandrov return; 296*8266a049SNikolay Aleksandrov 297*8266a049SNikolay Aleksandrov /* we need the STAR_EXCLUDE ports if there are non-STAR_EXCLUDE ports 298*8266a049SNikolay Aleksandrov * we should ignore perm entries since they're managed by user-space 299*8266a049SNikolay Aleksandrov */ 300*8266a049SNikolay Aleksandrov for (pp = &sgmp->ports; 301*8266a049SNikolay Aleksandrov (p = mlock_dereference(*pp, sgmp->br)) != NULL; 302*8266a049SNikolay Aleksandrov pp = &p->next) 303*8266a049SNikolay Aleksandrov if (!(p->flags & (MDB_PG_FLAGS_STAR_EXCL | 304*8266a049SNikolay Aleksandrov MDB_PG_FLAGS_PERMANENT))) 305*8266a049SNikolay Aleksandrov return; 306*8266a049SNikolay Aleksandrov 307*8266a049SNikolay Aleksandrov for (pp = &sgmp->ports; 308*8266a049SNikolay Aleksandrov (p = mlock_dereference(*pp, sgmp->br)) != NULL;) { 309*8266a049SNikolay Aleksandrov if (!(p->flags & MDB_PG_FLAGS_PERMANENT)) 310*8266a049SNikolay Aleksandrov br_multicast_del_pg(sgmp, p, pp); 311*8266a049SNikolay Aleksandrov else 312*8266a049SNikolay Aleksandrov pp = &p->next; 313*8266a049SNikolay Aleksandrov } 314*8266a049SNikolay Aleksandrov } 315*8266a049SNikolay Aleksandrov 316*8266a049SNikolay Aleksandrov void br_multicast_sg_add_exclude_ports(struct net_bridge_mdb_entry *star_mp, 317*8266a049SNikolay Aleksandrov struct net_bridge_port_group *sg) 318*8266a049SNikolay Aleksandrov { 319*8266a049SNikolay Aleksandrov struct net_bridge_port_group_sg_key sg_key; 320*8266a049SNikolay Aleksandrov struct net_bridge *br = star_mp->br; 321*8266a049SNikolay Aleksandrov struct net_bridge_port_group *pg; 322*8266a049SNikolay Aleksandrov 323*8266a049SNikolay Aleksandrov if (WARN_ON(br_multicast_is_star_g(&sg->key.addr))) 324*8266a049SNikolay Aleksandrov return; 325*8266a049SNikolay Aleksandrov if (WARN_ON(!br_multicast_is_star_g(&star_mp->addr))) 326*8266a049SNikolay Aleksandrov return; 327*8266a049SNikolay Aleksandrov 328*8266a049SNikolay Aleksandrov memset(&sg_key, 0, sizeof(sg_key)); 329*8266a049SNikolay Aleksandrov sg_key.addr = sg->key.addr; 330*8266a049SNikolay Aleksandrov /* we need to add all exclude ports to the S,G */ 331*8266a049SNikolay Aleksandrov for (pg = mlock_dereference(star_mp->ports, br); 332*8266a049SNikolay Aleksandrov pg; 333*8266a049SNikolay Aleksandrov pg = mlock_dereference(pg->next, br)) { 334*8266a049SNikolay Aleksandrov struct net_bridge_port_group *src_pg; 335*8266a049SNikolay Aleksandrov 336*8266a049SNikolay Aleksandrov if (pg == sg || pg->filter_mode == MCAST_INCLUDE) 337*8266a049SNikolay Aleksandrov continue; 338*8266a049SNikolay Aleksandrov 339*8266a049SNikolay Aleksandrov sg_key.port = pg->key.port; 340*8266a049SNikolay Aleksandrov if (br_sg_port_find(br, &sg_key)) 341*8266a049SNikolay Aleksandrov continue; 342*8266a049SNikolay Aleksandrov 343*8266a049SNikolay Aleksandrov src_pg = __br_multicast_add_group(br, pg->key.port, 344*8266a049SNikolay Aleksandrov &sg->key.addr, 345*8266a049SNikolay Aleksandrov sg->eth_addr, 346*8266a049SNikolay Aleksandrov MCAST_INCLUDE, false); 347*8266a049SNikolay Aleksandrov if (IS_ERR_OR_NULL(src_pg) || 348*8266a049SNikolay Aleksandrov src_pg->rt_protocol != RTPROT_KERNEL) 349*8266a049SNikolay Aleksandrov continue; 350*8266a049SNikolay Aleksandrov src_pg->flags |= MDB_PG_FLAGS_STAR_EXCL; 351*8266a049SNikolay Aleksandrov } 352*8266a049SNikolay Aleksandrov } 353*8266a049SNikolay Aleksandrov 354b0812368SNikolay Aleksandrov static void br_multicast_fwd_src_add(struct net_bridge_group_src *src) 355b0812368SNikolay Aleksandrov { 356*8266a049SNikolay Aleksandrov struct net_bridge_mdb_entry *star_mp; 357b0812368SNikolay Aleksandrov struct net_bridge_port_group *sg; 358b0812368SNikolay Aleksandrov struct br_ip sg_ip; 359b0812368SNikolay Aleksandrov 360b0812368SNikolay Aleksandrov if (src->flags & BR_SGRP_F_INSTALLED) 361b0812368SNikolay Aleksandrov return; 362b0812368SNikolay Aleksandrov 363b0812368SNikolay Aleksandrov memset(&sg_ip, 0, sizeof(sg_ip)); 364b0812368SNikolay Aleksandrov sg_ip = src->pg->key.addr; 365b0812368SNikolay Aleksandrov sg_ip.src = src->addr.src; 366b0812368SNikolay Aleksandrov sg = __br_multicast_add_group(src->br, src->pg->key.port, &sg_ip, 367b0812368SNikolay Aleksandrov src->pg->eth_addr, MCAST_INCLUDE, false); 368b0812368SNikolay Aleksandrov if (IS_ERR_OR_NULL(sg)) 369b0812368SNikolay Aleksandrov return; 370b0812368SNikolay Aleksandrov src->flags |= BR_SGRP_F_INSTALLED; 371*8266a049SNikolay Aleksandrov sg->flags &= ~MDB_PG_FLAGS_STAR_EXCL; 372b0812368SNikolay Aleksandrov 373b0812368SNikolay Aleksandrov /* if it was added by user-space as perm we can skip next steps */ 374b0812368SNikolay Aleksandrov if (sg->rt_protocol != RTPROT_KERNEL && 375b0812368SNikolay Aleksandrov (sg->flags & MDB_PG_FLAGS_PERMANENT)) 376b0812368SNikolay Aleksandrov return; 377b0812368SNikolay Aleksandrov 378b0812368SNikolay Aleksandrov /* the kernel is now responsible for removing this S,G */ 379b0812368SNikolay Aleksandrov del_timer(&sg->timer); 380*8266a049SNikolay Aleksandrov star_mp = br_mdb_ip_get(src->br, &src->pg->key.addr); 381*8266a049SNikolay Aleksandrov if (!star_mp) 382*8266a049SNikolay Aleksandrov return; 383*8266a049SNikolay Aleksandrov 384*8266a049SNikolay Aleksandrov br_multicast_sg_add_exclude_ports(star_mp, sg); 385b0812368SNikolay Aleksandrov } 386b0812368SNikolay Aleksandrov 387b0812368SNikolay Aleksandrov static void br_multicast_fwd_src_remove(struct net_bridge_group_src *src) 388b0812368SNikolay Aleksandrov { 389b0812368SNikolay Aleksandrov struct net_bridge_port_group *p, *pg = src->pg; 390b0812368SNikolay Aleksandrov struct net_bridge_port_group __rcu **pp; 391b0812368SNikolay Aleksandrov struct net_bridge_mdb_entry *mp; 392b0812368SNikolay Aleksandrov struct br_ip sg_ip; 393b0812368SNikolay Aleksandrov 394b0812368SNikolay Aleksandrov memset(&sg_ip, 0, sizeof(sg_ip)); 395b0812368SNikolay Aleksandrov sg_ip = pg->key.addr; 396b0812368SNikolay Aleksandrov sg_ip.src = src->addr.src; 397b0812368SNikolay Aleksandrov 398b0812368SNikolay Aleksandrov mp = br_mdb_ip_get(src->br, &sg_ip); 399b0812368SNikolay Aleksandrov if (!mp) 400b0812368SNikolay Aleksandrov return; 401b0812368SNikolay Aleksandrov 402b0812368SNikolay Aleksandrov for (pp = &mp->ports; 403b0812368SNikolay Aleksandrov (p = mlock_dereference(*pp, src->br)) != NULL; 404b0812368SNikolay Aleksandrov pp = &p->next) { 405b0812368SNikolay Aleksandrov if (!br_port_group_equal(p, pg->key.port, pg->eth_addr)) 406b0812368SNikolay Aleksandrov continue; 407b0812368SNikolay Aleksandrov 408b0812368SNikolay Aleksandrov if (p->rt_protocol != RTPROT_KERNEL && 409b0812368SNikolay Aleksandrov (p->flags & MDB_PG_FLAGS_PERMANENT)) 410b0812368SNikolay Aleksandrov break; 411b0812368SNikolay Aleksandrov 412b0812368SNikolay Aleksandrov br_multicast_del_pg(mp, p, pp); 413b0812368SNikolay Aleksandrov break; 414b0812368SNikolay Aleksandrov } 415b0812368SNikolay Aleksandrov src->flags &= ~BR_SGRP_F_INSTALLED; 416b0812368SNikolay Aleksandrov } 417b0812368SNikolay Aleksandrov 418b0812368SNikolay Aleksandrov static void br_multicast_fwd_src_handle(struct net_bridge_group_src *src) 419b0812368SNikolay Aleksandrov { 420b0812368SNikolay Aleksandrov br_multicast_fwd_src_add(src); 421b0812368SNikolay Aleksandrov } 422b0812368SNikolay Aleksandrov 423e12cec65SNikolay Aleksandrov static void br_multicast_destroy_mdb_entry(struct net_bridge_mcast_gc *gc) 424e12cec65SNikolay Aleksandrov { 425e12cec65SNikolay Aleksandrov struct net_bridge_mdb_entry *mp; 426e12cec65SNikolay Aleksandrov 427e12cec65SNikolay Aleksandrov mp = container_of(gc, struct net_bridge_mdb_entry, mcast_gc); 428e12cec65SNikolay Aleksandrov WARN_ON(!hlist_unhashed(&mp->mdb_node)); 429e12cec65SNikolay Aleksandrov WARN_ON(mp->ports); 430e12cec65SNikolay Aleksandrov 431e12cec65SNikolay Aleksandrov del_timer_sync(&mp->timer); 432e12cec65SNikolay Aleksandrov kfree_rcu(mp, rcu); 433e12cec65SNikolay Aleksandrov } 434e12cec65SNikolay Aleksandrov 435e12cec65SNikolay Aleksandrov static void br_multicast_del_mdb_entry(struct net_bridge_mdb_entry *mp) 436e12cec65SNikolay Aleksandrov { 437e12cec65SNikolay Aleksandrov struct net_bridge *br = mp->br; 438e12cec65SNikolay Aleksandrov 439e12cec65SNikolay Aleksandrov rhashtable_remove_fast(&br->mdb_hash_tbl, &mp->rhnode, 440e12cec65SNikolay Aleksandrov br_mdb_rht_params); 441e12cec65SNikolay Aleksandrov hlist_del_init_rcu(&mp->mdb_node); 442e12cec65SNikolay Aleksandrov hlist_add_head(&mp->mcast_gc.gc_node, &br->mcast_gc_list); 443e12cec65SNikolay Aleksandrov queue_work(system_long_wq, &br->mcast_gc_work); 444e12cec65SNikolay Aleksandrov } 445e12cec65SNikolay Aleksandrov 44688c1f37fSAllen Pais static void br_multicast_group_expired(struct timer_list *t) 447eb1d1641SHerbert Xu { 44888c1f37fSAllen Pais struct net_bridge_mdb_entry *mp = from_timer(mp, t, timer); 449eb1d1641SHerbert Xu struct net_bridge *br = mp->br; 450eb1d1641SHerbert Xu 451eb1d1641SHerbert Xu spin_lock(&br->multicast_lock); 452b0812368SNikolay Aleksandrov if (hlist_unhashed(&mp->mdb_node) || !netif_running(br->dev) || 453b0812368SNikolay Aleksandrov timer_pending(&mp->timer)) 454eb1d1641SHerbert Xu goto out; 455eb1d1641SHerbert Xu 4561bc844eeSNikolay Aleksandrov br_multicast_host_leave(mp, true); 457eb1d1641SHerbert Xu 458eb1d1641SHerbert Xu if (mp->ports) 459eb1d1641SHerbert Xu goto out; 460e12cec65SNikolay Aleksandrov br_multicast_del_mdb_entry(mp); 461eb1d1641SHerbert Xu out: 462eb1d1641SHerbert Xu spin_unlock(&br->multicast_lock); 463eb1d1641SHerbert Xu } 464eb1d1641SHerbert Xu 465e12cec65SNikolay Aleksandrov static void br_multicast_destroy_group_src(struct net_bridge_mcast_gc *gc) 466e12cec65SNikolay Aleksandrov { 467e12cec65SNikolay Aleksandrov struct net_bridge_group_src *src; 468e12cec65SNikolay Aleksandrov 469e12cec65SNikolay Aleksandrov src = container_of(gc, struct net_bridge_group_src, mcast_gc); 470e12cec65SNikolay Aleksandrov WARN_ON(!hlist_unhashed(&src->node)); 471e12cec65SNikolay Aleksandrov 472e12cec65SNikolay Aleksandrov del_timer_sync(&src->timer); 473e12cec65SNikolay Aleksandrov kfree_rcu(src, rcu); 474e12cec65SNikolay Aleksandrov } 475e12cec65SNikolay Aleksandrov 4768b671779SNikolay Aleksandrov static void br_multicast_del_group_src(struct net_bridge_group_src *src) 4778b671779SNikolay Aleksandrov { 478085b53c8SNikolay Aleksandrov struct net_bridge *br = src->pg->key.port->br; 4798b671779SNikolay Aleksandrov 480b0812368SNikolay Aleksandrov br_multicast_fwd_src_remove(src); 4818b671779SNikolay Aleksandrov hlist_del_init_rcu(&src->node); 4828b671779SNikolay Aleksandrov src->pg->src_ents--; 483e12cec65SNikolay Aleksandrov hlist_add_head(&src->mcast_gc.gc_node, &br->mcast_gc_list); 484e12cec65SNikolay Aleksandrov queue_work(system_long_wq, &br->mcast_gc_work); 485e12cec65SNikolay Aleksandrov } 486e12cec65SNikolay Aleksandrov 487e12cec65SNikolay Aleksandrov static void br_multicast_destroy_port_group(struct net_bridge_mcast_gc *gc) 488e12cec65SNikolay Aleksandrov { 489e12cec65SNikolay Aleksandrov struct net_bridge_port_group *pg; 490e12cec65SNikolay Aleksandrov 491e12cec65SNikolay Aleksandrov pg = container_of(gc, struct net_bridge_port_group, mcast_gc); 492e12cec65SNikolay Aleksandrov WARN_ON(!hlist_unhashed(&pg->mglist)); 493e12cec65SNikolay Aleksandrov WARN_ON(!hlist_empty(&pg->src_list)); 494e12cec65SNikolay Aleksandrov 495e12cec65SNikolay Aleksandrov del_timer_sync(&pg->rexmit_timer); 496e12cec65SNikolay Aleksandrov del_timer_sync(&pg->timer); 497e12cec65SNikolay Aleksandrov kfree_rcu(pg, rcu); 4988b671779SNikolay Aleksandrov } 4998b671779SNikolay Aleksandrov 500681590bdSNikolay Aleksandrov void br_multicast_del_pg(struct net_bridge_mdb_entry *mp, 501681590bdSNikolay Aleksandrov struct net_bridge_port_group *pg, 502681590bdSNikolay Aleksandrov struct net_bridge_port_group __rcu **pp) 503681590bdSNikolay Aleksandrov { 504085b53c8SNikolay Aleksandrov struct net_bridge *br = pg->key.port->br; 5058b671779SNikolay Aleksandrov struct net_bridge_group_src *ent; 5068b671779SNikolay Aleksandrov struct hlist_node *tmp; 507681590bdSNikolay Aleksandrov 508085b53c8SNikolay Aleksandrov rhashtable_remove_fast(&br->sg_port_tbl, &pg->rhnode, 509085b53c8SNikolay Aleksandrov br_sg_port_rht_params); 510681590bdSNikolay Aleksandrov rcu_assign_pointer(*pp, pg->next); 511681590bdSNikolay Aleksandrov hlist_del_init(&pg->mglist); 5128b671779SNikolay Aleksandrov hlist_for_each_entry_safe(ent, tmp, &pg->src_list, node) 5138b671779SNikolay Aleksandrov br_multicast_del_group_src(ent); 51481f19838SNikolay Aleksandrov br_mdb_notify(br->dev, mp, pg, RTM_DELMDB); 515*8266a049SNikolay Aleksandrov if (!br_multicast_is_star_g(&mp->addr)) 516*8266a049SNikolay Aleksandrov br_multicast_sg_del_exclude_ports(mp); 517*8266a049SNikolay Aleksandrov else 518*8266a049SNikolay Aleksandrov br_multicast_star_g_handle_mode(pg, MCAST_INCLUDE); 519e12cec65SNikolay Aleksandrov hlist_add_head(&pg->mcast_gc.gc_node, &br->mcast_gc_list); 520e12cec65SNikolay Aleksandrov queue_work(system_long_wq, &br->mcast_gc_work); 521681590bdSNikolay Aleksandrov 522681590bdSNikolay Aleksandrov if (!mp->ports && !mp->host_joined && netif_running(br->dev)) 523681590bdSNikolay Aleksandrov mod_timer(&mp->timer, jiffies); 524681590bdSNikolay Aleksandrov } 525681590bdSNikolay Aleksandrov 526681590bdSNikolay Aleksandrov static void br_multicast_find_del_pg(struct net_bridge *br, 527eb1d1641SHerbert Xu struct net_bridge_port_group *pg) 528eb1d1641SHerbert Xu { 5298b671779SNikolay Aleksandrov struct net_bridge_port_group __rcu **pp; 530eb1d1641SHerbert Xu struct net_bridge_mdb_entry *mp; 531eb1d1641SHerbert Xu struct net_bridge_port_group *p; 532e8051688SEric Dumazet 533085b53c8SNikolay Aleksandrov mp = br_mdb_ip_get(br, &pg->key.addr); 534eb1d1641SHerbert Xu if (WARN_ON(!mp)) 535eb1d1641SHerbert Xu return; 536eb1d1641SHerbert Xu 537e8051688SEric Dumazet for (pp = &mp->ports; 538e8051688SEric Dumazet (p = mlock_dereference(*pp, br)) != NULL; 539e8051688SEric Dumazet pp = &p->next) { 540eb1d1641SHerbert Xu if (p != pg) 541eb1d1641SHerbert Xu continue; 542eb1d1641SHerbert Xu 543681590bdSNikolay Aleksandrov br_multicast_del_pg(mp, pg, pp); 544eb1d1641SHerbert Xu return; 545eb1d1641SHerbert Xu } 546eb1d1641SHerbert Xu 547eb1d1641SHerbert Xu WARN_ON(1); 548eb1d1641SHerbert Xu } 549eb1d1641SHerbert Xu 55088c1f37fSAllen Pais static void br_multicast_port_group_expired(struct timer_list *t) 551eb1d1641SHerbert Xu { 55288c1f37fSAllen Pais struct net_bridge_port_group *pg = from_timer(pg, t, timer); 553d6c33d67SNikolay Aleksandrov struct net_bridge_group_src *src_ent; 554085b53c8SNikolay Aleksandrov struct net_bridge *br = pg->key.port->br; 555d6c33d67SNikolay Aleksandrov struct hlist_node *tmp; 556d6c33d67SNikolay Aleksandrov bool changed; 557eb1d1641SHerbert Xu 558eb1d1641SHerbert Xu spin_lock(&br->multicast_lock); 559eb1d1641SHerbert Xu if (!netif_running(br->dev) || timer_pending(&pg->timer) || 5609d06b6d8SElad Raz hlist_unhashed(&pg->mglist) || pg->flags & MDB_PG_FLAGS_PERMANENT) 561eb1d1641SHerbert Xu goto out; 562eb1d1641SHerbert Xu 563d6c33d67SNikolay Aleksandrov changed = !!(pg->filter_mode == MCAST_EXCLUDE); 564d6c33d67SNikolay Aleksandrov pg->filter_mode = MCAST_INCLUDE; 565d6c33d67SNikolay Aleksandrov hlist_for_each_entry_safe(src_ent, tmp, &pg->src_list, node) { 566d6c33d67SNikolay Aleksandrov if (!timer_pending(&src_ent->timer)) { 567d6c33d67SNikolay Aleksandrov br_multicast_del_group_src(src_ent); 568d6c33d67SNikolay Aleksandrov changed = true; 569d6c33d67SNikolay Aleksandrov } 570d6c33d67SNikolay Aleksandrov } 571eb1d1641SHerbert Xu 572d6c33d67SNikolay Aleksandrov if (hlist_empty(&pg->src_list)) { 573d6c33d67SNikolay Aleksandrov br_multicast_find_del_pg(br, pg); 574d6c33d67SNikolay Aleksandrov } else if (changed) { 575085b53c8SNikolay Aleksandrov struct net_bridge_mdb_entry *mp = br_mdb_ip_get(br, &pg->key.addr); 576d6c33d67SNikolay Aleksandrov 577*8266a049SNikolay Aleksandrov if (changed && br_multicast_is_star_g(&pg->key.addr)) 578*8266a049SNikolay Aleksandrov br_multicast_star_g_handle_mode(pg, MCAST_INCLUDE); 579*8266a049SNikolay Aleksandrov 580d6c33d67SNikolay Aleksandrov if (WARN_ON(!mp)) 581d6c33d67SNikolay Aleksandrov goto out; 582d6c33d67SNikolay Aleksandrov br_mdb_notify(br->dev, mp, pg, RTM_NEWMDB); 583d6c33d67SNikolay Aleksandrov } 584eb1d1641SHerbert Xu out: 585eb1d1641SHerbert Xu spin_unlock(&br->multicast_lock); 586eb1d1641SHerbert Xu } 587eb1d1641SHerbert Xu 588e12cec65SNikolay Aleksandrov static void br_multicast_gc(struct hlist_head *head) 589e12cec65SNikolay Aleksandrov { 590e12cec65SNikolay Aleksandrov struct net_bridge_mcast_gc *gcent; 591e12cec65SNikolay Aleksandrov struct hlist_node *tmp; 592e12cec65SNikolay Aleksandrov 593e12cec65SNikolay Aleksandrov hlist_for_each_entry_safe(gcent, tmp, head, gc_node) { 594e12cec65SNikolay Aleksandrov hlist_del_init(&gcent->gc_node); 595e12cec65SNikolay Aleksandrov gcent->destroy(gcent); 596e12cec65SNikolay Aleksandrov } 597e12cec65SNikolay Aleksandrov } 598e12cec65SNikolay Aleksandrov 5998ef2a9a5SYOSHIFUJI Hideaki static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge *br, 600438ef2d0SNikolay Aleksandrov struct net_bridge_port_group *pg, 601438ef2d0SNikolay Aleksandrov __be32 ip_dst, __be32 group, 602438ef2d0SNikolay Aleksandrov bool with_srcs, bool over_lmqt, 60342c11ccfSNikolay Aleksandrov u8 sflag, u8 *igmp_type, 60442c11ccfSNikolay Aleksandrov bool *need_rexmit) 605eb1d1641SHerbert Xu { 606085b53c8SNikolay Aleksandrov struct net_bridge_port *p = pg ? pg->key.port : NULL; 607438ef2d0SNikolay Aleksandrov struct net_bridge_group_src *ent; 608438ef2d0SNikolay Aleksandrov size_t pkt_size, igmp_hdr_size; 609438ef2d0SNikolay Aleksandrov unsigned long now = jiffies; 6105e923585SNikolay Aleksandrov struct igmpv3_query *ihv3; 611438ef2d0SNikolay Aleksandrov void *csum_start = NULL; 612438ef2d0SNikolay Aleksandrov __sum16 *csum = NULL; 613eb1d1641SHerbert Xu struct sk_buff *skb; 614eb1d1641SHerbert Xu struct igmphdr *ih; 615eb1d1641SHerbert Xu struct ethhdr *eth; 616438ef2d0SNikolay Aleksandrov unsigned long lmqt; 617eb1d1641SHerbert Xu struct iphdr *iph; 618438ef2d0SNikolay Aleksandrov u16 lmqt_srcs = 0; 619eb1d1641SHerbert Xu 6205e923585SNikolay Aleksandrov igmp_hdr_size = sizeof(*ih); 621438ef2d0SNikolay Aleksandrov if (br->multicast_igmp_version == 3) { 6225e923585SNikolay Aleksandrov igmp_hdr_size = sizeof(*ihv3); 623438ef2d0SNikolay Aleksandrov if (pg && with_srcs) { 624438ef2d0SNikolay Aleksandrov lmqt = now + (br->multicast_last_member_interval * 625438ef2d0SNikolay Aleksandrov br->multicast_last_member_count); 626438ef2d0SNikolay Aleksandrov hlist_for_each_entry(ent, &pg->src_list, node) { 627438ef2d0SNikolay Aleksandrov if (over_lmqt == time_after(ent->timer.expires, 628438ef2d0SNikolay Aleksandrov lmqt) && 629438ef2d0SNikolay Aleksandrov ent->src_query_rexmit_cnt > 0) 630438ef2d0SNikolay Aleksandrov lmqt_srcs++; 631438ef2d0SNikolay Aleksandrov } 632438ef2d0SNikolay Aleksandrov 633438ef2d0SNikolay Aleksandrov if (!lmqt_srcs) 634438ef2d0SNikolay Aleksandrov return NULL; 635438ef2d0SNikolay Aleksandrov igmp_hdr_size += lmqt_srcs * sizeof(__be32); 636438ef2d0SNikolay Aleksandrov } 637438ef2d0SNikolay Aleksandrov } 638438ef2d0SNikolay Aleksandrov 639438ef2d0SNikolay Aleksandrov pkt_size = sizeof(*eth) + sizeof(*iph) + 4 + igmp_hdr_size; 640438ef2d0SNikolay Aleksandrov if ((p && pkt_size > p->dev->mtu) || 641438ef2d0SNikolay Aleksandrov pkt_size > br->dev->mtu) 642438ef2d0SNikolay Aleksandrov return NULL; 643438ef2d0SNikolay Aleksandrov 644438ef2d0SNikolay Aleksandrov skb = netdev_alloc_skb_ip_align(br->dev, pkt_size); 645eb1d1641SHerbert Xu if (!skb) 646eb1d1641SHerbert Xu goto out; 647eb1d1641SHerbert Xu 648eb1d1641SHerbert Xu skb->protocol = htons(ETH_P_IP); 649eb1d1641SHerbert Xu 650eb1d1641SHerbert Xu skb_reset_mac_header(skb); 651eb1d1641SHerbert Xu eth = eth_hdr(skb); 652eb1d1641SHerbert Xu 653e5a727f6SJoe Perches ether_addr_copy(eth->h_source, br->dev->dev_addr); 654438ef2d0SNikolay Aleksandrov ip_eth_mc_map(ip_dst, eth->h_dest); 655eb1d1641SHerbert Xu eth->h_proto = htons(ETH_P_IP); 656eb1d1641SHerbert Xu skb_put(skb, sizeof(*eth)); 657eb1d1641SHerbert Xu 658eb1d1641SHerbert Xu skb_set_network_header(skb, skb->len); 659eb1d1641SHerbert Xu iph = ip_hdr(skb); 660438ef2d0SNikolay Aleksandrov iph->tot_len = htons(pkt_size - sizeof(*eth)); 661eb1d1641SHerbert Xu 662eb1d1641SHerbert Xu iph->version = 4; 663eb1d1641SHerbert Xu iph->ihl = 6; 664eb1d1641SHerbert Xu iph->tos = 0xc0; 665eb1d1641SHerbert Xu iph->id = 0; 666eb1d1641SHerbert Xu iph->frag_off = htons(IP_DF); 667eb1d1641SHerbert Xu iph->ttl = 1; 668eb1d1641SHerbert Xu iph->protocol = IPPROTO_IGMP; 669675779adSNikolay Aleksandrov iph->saddr = br_opt_get(br, BROPT_MULTICAST_QUERY_USE_IFADDR) ? 6701c8ad5bfSCong Wang inet_select_addr(br->dev, 0, RT_SCOPE_LINK) : 0; 671438ef2d0SNikolay Aleksandrov iph->daddr = ip_dst; 672eb1d1641SHerbert Xu ((u8 *)&iph[1])[0] = IPOPT_RA; 673eb1d1641SHerbert Xu ((u8 *)&iph[1])[1] = 4; 674eb1d1641SHerbert Xu ((u8 *)&iph[1])[2] = 0; 675eb1d1641SHerbert Xu ((u8 *)&iph[1])[3] = 0; 676eb1d1641SHerbert Xu ip_send_check(iph); 677eb1d1641SHerbert Xu skb_put(skb, 24); 678eb1d1641SHerbert Xu 679eb1d1641SHerbert Xu skb_set_transport_header(skb, skb->len); 6801080ab95SNikolay Aleksandrov *igmp_type = IGMP_HOST_MEMBERSHIP_QUERY; 6815e923585SNikolay Aleksandrov 6825e923585SNikolay Aleksandrov switch (br->multicast_igmp_version) { 6835e923585SNikolay Aleksandrov case 2: 6845e923585SNikolay Aleksandrov ih = igmp_hdr(skb); 685eb1d1641SHerbert Xu ih->type = IGMP_HOST_MEMBERSHIP_QUERY; 686eb1d1641SHerbert Xu ih->code = (group ? br->multicast_last_member_interval : 687eb1d1641SHerbert Xu br->multicast_query_response_interval) / 688eb1d1641SHerbert Xu (HZ / IGMP_TIMER_SCALE); 689eb1d1641SHerbert Xu ih->group = group; 690eb1d1641SHerbert Xu ih->csum = 0; 691438ef2d0SNikolay Aleksandrov csum = &ih->csum; 692438ef2d0SNikolay Aleksandrov csum_start = (void *)ih; 6935e923585SNikolay Aleksandrov break; 6945e923585SNikolay Aleksandrov case 3: 6955e923585SNikolay Aleksandrov ihv3 = igmpv3_query_hdr(skb); 6965e923585SNikolay Aleksandrov ihv3->type = IGMP_HOST_MEMBERSHIP_QUERY; 6975e923585SNikolay Aleksandrov ihv3->code = (group ? br->multicast_last_member_interval : 6985e923585SNikolay Aleksandrov br->multicast_query_response_interval) / 6995e923585SNikolay Aleksandrov (HZ / IGMP_TIMER_SCALE); 7005e923585SNikolay Aleksandrov ihv3->group = group; 7015e923585SNikolay Aleksandrov ihv3->qqic = br->multicast_query_interval / HZ; 702438ef2d0SNikolay Aleksandrov ihv3->nsrcs = htons(lmqt_srcs); 7035e923585SNikolay Aleksandrov ihv3->resv = 0; 704438ef2d0SNikolay Aleksandrov ihv3->suppress = sflag; 7055e923585SNikolay Aleksandrov ihv3->qrv = 2; 7065e923585SNikolay Aleksandrov ihv3->csum = 0; 707438ef2d0SNikolay Aleksandrov csum = &ihv3->csum; 708438ef2d0SNikolay Aleksandrov csum_start = (void *)ihv3; 709438ef2d0SNikolay Aleksandrov if (!pg || !with_srcs) 710438ef2d0SNikolay Aleksandrov break; 711438ef2d0SNikolay Aleksandrov 712438ef2d0SNikolay Aleksandrov lmqt_srcs = 0; 713438ef2d0SNikolay Aleksandrov hlist_for_each_entry(ent, &pg->src_list, node) { 714438ef2d0SNikolay Aleksandrov if (over_lmqt == time_after(ent->timer.expires, 715438ef2d0SNikolay Aleksandrov lmqt) && 716438ef2d0SNikolay Aleksandrov ent->src_query_rexmit_cnt > 0) { 717deb96566SNikolay Aleksandrov ihv3->srcs[lmqt_srcs++] = ent->addr.src.ip4; 718438ef2d0SNikolay Aleksandrov ent->src_query_rexmit_cnt--; 71942c11ccfSNikolay Aleksandrov if (need_rexmit && ent->src_query_rexmit_cnt) 72042c11ccfSNikolay Aleksandrov *need_rexmit = true; 721438ef2d0SNikolay Aleksandrov } 722438ef2d0SNikolay Aleksandrov } 723438ef2d0SNikolay Aleksandrov if (WARN_ON(lmqt_srcs != ntohs(ihv3->nsrcs))) { 724438ef2d0SNikolay Aleksandrov kfree_skb(skb); 725438ef2d0SNikolay Aleksandrov return NULL; 726438ef2d0SNikolay Aleksandrov } 7275e923585SNikolay Aleksandrov break; 7285e923585SNikolay Aleksandrov } 729eb1d1641SHerbert Xu 730438ef2d0SNikolay Aleksandrov if (WARN_ON(!csum || !csum_start)) { 731438ef2d0SNikolay Aleksandrov kfree_skb(skb); 732438ef2d0SNikolay Aleksandrov return NULL; 733438ef2d0SNikolay Aleksandrov } 734438ef2d0SNikolay Aleksandrov 735438ef2d0SNikolay Aleksandrov *csum = ip_compute_csum(csum_start, igmp_hdr_size); 7365e923585SNikolay Aleksandrov skb_put(skb, igmp_hdr_size); 737eb1d1641SHerbert Xu __skb_pull(skb, sizeof(*eth)); 738eb1d1641SHerbert Xu 739eb1d1641SHerbert Xu out: 740eb1d1641SHerbert Xu return skb; 741eb1d1641SHerbert Xu } 742eb1d1641SHerbert Xu 743dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 74408b202b6SYOSHIFUJI Hideaki static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br, 745438ef2d0SNikolay Aleksandrov struct net_bridge_port_group *pg, 746438ef2d0SNikolay Aleksandrov const struct in6_addr *ip6_dst, 747438ef2d0SNikolay Aleksandrov const struct in6_addr *group, 748438ef2d0SNikolay Aleksandrov bool with_srcs, bool over_llqt, 74942c11ccfSNikolay Aleksandrov u8 sflag, u8 *igmp_type, 75042c11ccfSNikolay Aleksandrov bool *need_rexmit) 75108b202b6SYOSHIFUJI Hideaki { 752085b53c8SNikolay Aleksandrov struct net_bridge_port *p = pg ? pg->key.port : NULL; 753438ef2d0SNikolay Aleksandrov struct net_bridge_group_src *ent; 754438ef2d0SNikolay Aleksandrov size_t pkt_size, mld_hdr_size; 755438ef2d0SNikolay Aleksandrov unsigned long now = jiffies; 756aa2ae3e7SNikolay Aleksandrov struct mld2_query *mld2q; 757438ef2d0SNikolay Aleksandrov void *csum_start = NULL; 758aa2ae3e7SNikolay Aleksandrov unsigned long interval; 759438ef2d0SNikolay Aleksandrov __sum16 *csum = NULL; 76008b202b6SYOSHIFUJI Hideaki struct ipv6hdr *ip6h; 76108b202b6SYOSHIFUJI Hideaki struct mld_msg *mldq; 762aa2ae3e7SNikolay Aleksandrov struct sk_buff *skb; 763438ef2d0SNikolay Aleksandrov unsigned long llqt; 76408b202b6SYOSHIFUJI Hideaki struct ethhdr *eth; 765438ef2d0SNikolay Aleksandrov u16 llqt_srcs = 0; 76608b202b6SYOSHIFUJI Hideaki u8 *hopopt; 76708b202b6SYOSHIFUJI Hideaki 768aa2ae3e7SNikolay Aleksandrov mld_hdr_size = sizeof(*mldq); 769438ef2d0SNikolay Aleksandrov if (br->multicast_mld_version == 2) { 770aa2ae3e7SNikolay Aleksandrov mld_hdr_size = sizeof(*mld2q); 771438ef2d0SNikolay Aleksandrov if (pg && with_srcs) { 772438ef2d0SNikolay Aleksandrov llqt = now + (br->multicast_last_member_interval * 773438ef2d0SNikolay Aleksandrov br->multicast_last_member_count); 774438ef2d0SNikolay Aleksandrov hlist_for_each_entry(ent, &pg->src_list, node) { 775438ef2d0SNikolay Aleksandrov if (over_llqt == time_after(ent->timer.expires, 776438ef2d0SNikolay Aleksandrov llqt) && 777438ef2d0SNikolay Aleksandrov ent->src_query_rexmit_cnt > 0) 778438ef2d0SNikolay Aleksandrov llqt_srcs++; 779438ef2d0SNikolay Aleksandrov } 780438ef2d0SNikolay Aleksandrov 781438ef2d0SNikolay Aleksandrov if (!llqt_srcs) 782438ef2d0SNikolay Aleksandrov return NULL; 783438ef2d0SNikolay Aleksandrov mld_hdr_size += llqt_srcs * sizeof(struct in6_addr); 784438ef2d0SNikolay Aleksandrov } 785438ef2d0SNikolay Aleksandrov } 786438ef2d0SNikolay Aleksandrov 787438ef2d0SNikolay Aleksandrov pkt_size = sizeof(*eth) + sizeof(*ip6h) + 8 + mld_hdr_size; 788438ef2d0SNikolay Aleksandrov if ((p && pkt_size > p->dev->mtu) || 789438ef2d0SNikolay Aleksandrov pkt_size > br->dev->mtu) 790438ef2d0SNikolay Aleksandrov return NULL; 791438ef2d0SNikolay Aleksandrov 792438ef2d0SNikolay Aleksandrov skb = netdev_alloc_skb_ip_align(br->dev, pkt_size); 79308b202b6SYOSHIFUJI Hideaki if (!skb) 79408b202b6SYOSHIFUJI Hideaki goto out; 79508b202b6SYOSHIFUJI Hideaki 79608b202b6SYOSHIFUJI Hideaki skb->protocol = htons(ETH_P_IPV6); 79708b202b6SYOSHIFUJI Hideaki 79808b202b6SYOSHIFUJI Hideaki /* Ethernet header */ 79908b202b6SYOSHIFUJI Hideaki skb_reset_mac_header(skb); 80008b202b6SYOSHIFUJI Hideaki eth = eth_hdr(skb); 80108b202b6SYOSHIFUJI Hideaki 802e5a727f6SJoe Perches ether_addr_copy(eth->h_source, br->dev->dev_addr); 80308b202b6SYOSHIFUJI Hideaki eth->h_proto = htons(ETH_P_IPV6); 80408b202b6SYOSHIFUJI Hideaki skb_put(skb, sizeof(*eth)); 80508b202b6SYOSHIFUJI Hideaki 80608b202b6SYOSHIFUJI Hideaki /* IPv6 header + HbH option */ 80708b202b6SYOSHIFUJI Hideaki skb_set_network_header(skb, skb->len); 80808b202b6SYOSHIFUJI Hideaki ip6h = ipv6_hdr(skb); 80908b202b6SYOSHIFUJI Hideaki 81008b202b6SYOSHIFUJI Hideaki *(__force __be32 *)ip6h = htonl(0x60000000); 811aa2ae3e7SNikolay Aleksandrov ip6h->payload_len = htons(8 + mld_hdr_size); 81208b202b6SYOSHIFUJI Hideaki ip6h->nexthdr = IPPROTO_HOPOPTS; 81308b202b6SYOSHIFUJI Hideaki ip6h->hop_limit = 1; 814438ef2d0SNikolay Aleksandrov ip6h->daddr = *ip6_dst; 815d1d81d4cSUlrich Weber if (ipv6_dev_get_saddr(dev_net(br->dev), br->dev, &ip6h->daddr, 0, 816d1d81d4cSUlrich Weber &ip6h->saddr)) { 817d1d81d4cSUlrich Weber kfree_skb(skb); 818675779adSNikolay Aleksandrov br_opt_toggle(br, BROPT_HAS_IPV6_ADDR, false); 819d1d81d4cSUlrich Weber return NULL; 820d1d81d4cSUlrich Weber } 8210888d5f3Sdaniel 822675779adSNikolay Aleksandrov br_opt_toggle(br, BROPT_HAS_IPV6_ADDR, true); 82336cff5a1SLinus Lüssing ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest); 82408b202b6SYOSHIFUJI Hideaki 82508b202b6SYOSHIFUJI Hideaki hopopt = (u8 *)(ip6h + 1); 82608b202b6SYOSHIFUJI Hideaki hopopt[0] = IPPROTO_ICMPV6; /* next hdr */ 82708b202b6SYOSHIFUJI Hideaki hopopt[1] = 0; /* length of HbH */ 82808b202b6SYOSHIFUJI Hideaki hopopt[2] = IPV6_TLV_ROUTERALERT; /* Router Alert */ 82908b202b6SYOSHIFUJI Hideaki hopopt[3] = 2; /* Length of RA Option */ 83008b202b6SYOSHIFUJI Hideaki hopopt[4] = 0; /* Type = 0x0000 (MLD) */ 83108b202b6SYOSHIFUJI Hideaki hopopt[5] = 0; 8321de5a71cSEldad Zack hopopt[6] = IPV6_TLV_PAD1; /* Pad1 */ 8331de5a71cSEldad Zack hopopt[7] = IPV6_TLV_PAD1; /* Pad1 */ 83408b202b6SYOSHIFUJI Hideaki 83508b202b6SYOSHIFUJI Hideaki skb_put(skb, sizeof(*ip6h) + 8); 83608b202b6SYOSHIFUJI Hideaki 83708b202b6SYOSHIFUJI Hideaki /* ICMPv6 */ 83808b202b6SYOSHIFUJI Hideaki skb_set_transport_header(skb, skb->len); 839438ef2d0SNikolay Aleksandrov interval = ipv6_addr_any(group) ? 84032de868cSLinus Lüssing br->multicast_query_response_interval : 84132de868cSLinus Lüssing br->multicast_last_member_interval; 8421080ab95SNikolay Aleksandrov *igmp_type = ICMPV6_MGM_QUERY; 843aa2ae3e7SNikolay Aleksandrov switch (br->multicast_mld_version) { 844aa2ae3e7SNikolay Aleksandrov case 1: 845aa2ae3e7SNikolay Aleksandrov mldq = (struct mld_msg *)icmp6_hdr(skb); 84608b202b6SYOSHIFUJI Hideaki mldq->mld_type = ICMPV6_MGM_QUERY; 84708b202b6SYOSHIFUJI Hideaki mldq->mld_code = 0; 84808b202b6SYOSHIFUJI Hideaki mldq->mld_cksum = 0; 84908b202b6SYOSHIFUJI Hideaki mldq->mld_maxdelay = htons((u16)jiffies_to_msecs(interval)); 85008b202b6SYOSHIFUJI Hideaki mldq->mld_reserved = 0; 851438ef2d0SNikolay Aleksandrov mldq->mld_mca = *group; 852438ef2d0SNikolay Aleksandrov csum = &mldq->mld_cksum; 853438ef2d0SNikolay Aleksandrov csum_start = (void *)mldq; 854aa2ae3e7SNikolay Aleksandrov break; 855aa2ae3e7SNikolay Aleksandrov case 2: 856aa2ae3e7SNikolay Aleksandrov mld2q = (struct mld2_query *)icmp6_hdr(skb); 85753631a5fSLance Richardson mld2q->mld2q_mrc = htons((u16)jiffies_to_msecs(interval)); 858aa2ae3e7SNikolay Aleksandrov mld2q->mld2q_type = ICMPV6_MGM_QUERY; 859aa2ae3e7SNikolay Aleksandrov mld2q->mld2q_code = 0; 860aa2ae3e7SNikolay Aleksandrov mld2q->mld2q_cksum = 0; 861aa2ae3e7SNikolay Aleksandrov mld2q->mld2q_resv1 = 0; 862aa2ae3e7SNikolay Aleksandrov mld2q->mld2q_resv2 = 0; 863438ef2d0SNikolay Aleksandrov mld2q->mld2q_suppress = sflag; 864aa2ae3e7SNikolay Aleksandrov mld2q->mld2q_qrv = 2; 865438ef2d0SNikolay Aleksandrov mld2q->mld2q_nsrcs = htons(llqt_srcs); 866aa2ae3e7SNikolay Aleksandrov mld2q->mld2q_qqic = br->multicast_query_interval / HZ; 867438ef2d0SNikolay Aleksandrov mld2q->mld2q_mca = *group; 868438ef2d0SNikolay Aleksandrov csum = &mld2q->mld2q_cksum; 869438ef2d0SNikolay Aleksandrov csum_start = (void *)mld2q; 870438ef2d0SNikolay Aleksandrov if (!pg || !with_srcs) 871438ef2d0SNikolay Aleksandrov break; 872438ef2d0SNikolay Aleksandrov 873438ef2d0SNikolay Aleksandrov llqt_srcs = 0; 874438ef2d0SNikolay Aleksandrov hlist_for_each_entry(ent, &pg->src_list, node) { 875438ef2d0SNikolay Aleksandrov if (over_llqt == time_after(ent->timer.expires, 876438ef2d0SNikolay Aleksandrov llqt) && 877438ef2d0SNikolay Aleksandrov ent->src_query_rexmit_cnt > 0) { 878deb96566SNikolay Aleksandrov mld2q->mld2q_srcs[llqt_srcs++] = ent->addr.src.ip6; 879438ef2d0SNikolay Aleksandrov ent->src_query_rexmit_cnt--; 88042c11ccfSNikolay Aleksandrov if (need_rexmit && ent->src_query_rexmit_cnt) 88142c11ccfSNikolay Aleksandrov *need_rexmit = true; 882438ef2d0SNikolay Aleksandrov } 883438ef2d0SNikolay Aleksandrov } 884438ef2d0SNikolay Aleksandrov if (WARN_ON(llqt_srcs != ntohs(mld2q->mld2q_nsrcs))) { 885438ef2d0SNikolay Aleksandrov kfree_skb(skb); 886438ef2d0SNikolay Aleksandrov return NULL; 887438ef2d0SNikolay Aleksandrov } 888aa2ae3e7SNikolay Aleksandrov break; 889aa2ae3e7SNikolay Aleksandrov } 89008b202b6SYOSHIFUJI Hideaki 891438ef2d0SNikolay Aleksandrov if (WARN_ON(!csum || !csum_start)) { 892438ef2d0SNikolay Aleksandrov kfree_skb(skb); 893438ef2d0SNikolay Aleksandrov return NULL; 894438ef2d0SNikolay Aleksandrov } 895438ef2d0SNikolay Aleksandrov 896438ef2d0SNikolay Aleksandrov *csum = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, mld_hdr_size, 897438ef2d0SNikolay Aleksandrov IPPROTO_ICMPV6, 898438ef2d0SNikolay Aleksandrov csum_partial(csum_start, mld_hdr_size, 0)); 899438ef2d0SNikolay Aleksandrov skb_put(skb, mld_hdr_size); 90008b202b6SYOSHIFUJI Hideaki __skb_pull(skb, sizeof(*eth)); 90108b202b6SYOSHIFUJI Hideaki 90208b202b6SYOSHIFUJI Hideaki out: 90308b202b6SYOSHIFUJI Hideaki return skb; 90408b202b6SYOSHIFUJI Hideaki } 90508b202b6SYOSHIFUJI Hideaki #endif 90608b202b6SYOSHIFUJI Hideaki 9078ef2a9a5SYOSHIFUJI Hideaki static struct sk_buff *br_multicast_alloc_query(struct net_bridge *br, 908438ef2d0SNikolay Aleksandrov struct net_bridge_port_group *pg, 909438ef2d0SNikolay Aleksandrov struct br_ip *ip_dst, 910438ef2d0SNikolay Aleksandrov struct br_ip *group, 911438ef2d0SNikolay Aleksandrov bool with_srcs, bool over_lmqt, 91242c11ccfSNikolay Aleksandrov u8 sflag, u8 *igmp_type, 91342c11ccfSNikolay Aleksandrov bool *need_rexmit) 9148ef2a9a5SYOSHIFUJI Hideaki { 915438ef2d0SNikolay Aleksandrov __be32 ip4_dst; 916438ef2d0SNikolay Aleksandrov 917438ef2d0SNikolay Aleksandrov switch (group->proto) { 9188ef2a9a5SYOSHIFUJI Hideaki case htons(ETH_P_IP): 919eab3227bSNikolay Aleksandrov ip4_dst = ip_dst ? ip_dst->dst.ip4 : htonl(INADDR_ALLHOSTS_GROUP); 920438ef2d0SNikolay Aleksandrov return br_ip4_multicast_alloc_query(br, pg, 921eab3227bSNikolay Aleksandrov ip4_dst, group->dst.ip4, 922438ef2d0SNikolay Aleksandrov with_srcs, over_lmqt, 92342c11ccfSNikolay Aleksandrov sflag, igmp_type, 92442c11ccfSNikolay Aleksandrov need_rexmit); 925dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 926438ef2d0SNikolay Aleksandrov case htons(ETH_P_IPV6): { 927438ef2d0SNikolay Aleksandrov struct in6_addr ip6_dst; 928438ef2d0SNikolay Aleksandrov 929438ef2d0SNikolay Aleksandrov if (ip_dst) 930eab3227bSNikolay Aleksandrov ip6_dst = ip_dst->dst.ip6; 931438ef2d0SNikolay Aleksandrov else 932438ef2d0SNikolay Aleksandrov ipv6_addr_set(&ip6_dst, htonl(0xff020000), 0, 0, 933438ef2d0SNikolay Aleksandrov htonl(1)); 934438ef2d0SNikolay Aleksandrov 935438ef2d0SNikolay Aleksandrov return br_ip6_multicast_alloc_query(br, pg, 936eab3227bSNikolay Aleksandrov &ip6_dst, &group->dst.ip6, 937438ef2d0SNikolay Aleksandrov with_srcs, over_lmqt, 93842c11ccfSNikolay Aleksandrov sflag, igmp_type, 93942c11ccfSNikolay Aleksandrov need_rexmit); 940438ef2d0SNikolay Aleksandrov } 94108b202b6SYOSHIFUJI Hideaki #endif 9428ef2a9a5SYOSHIFUJI Hideaki } 9438ef2a9a5SYOSHIFUJI Hideaki return NULL; 9448ef2a9a5SYOSHIFUJI Hideaki } 9458ef2a9a5SYOSHIFUJI Hideaki 946cfd56754SCong Wang struct net_bridge_mdb_entry *br_multicast_new_group(struct net_bridge *br, 9475e923585SNikolay Aleksandrov struct br_ip *group) 948eb1d1641SHerbert Xu { 949eb1d1641SHerbert Xu struct net_bridge_mdb_entry *mp; 9504c0833bcSTobias Klauser int err; 951eb1d1641SHerbert Xu 95219e3a9c9SNikolay Aleksandrov mp = br_mdb_ip_get(br, group); 95319e3a9c9SNikolay Aleksandrov if (mp) 95419e3a9c9SNikolay Aleksandrov return mp; 955eb1d1641SHerbert Xu 95619e3a9c9SNikolay Aleksandrov if (atomic_read(&br->mdb_hash_tbl.nelems) >= br->hash_max) { 95719e3a9c9SNikolay Aleksandrov br_opt_toggle(br, BROPT_MULTICAST_ENABLED, false); 95819e3a9c9SNikolay Aleksandrov return ERR_PTR(-E2BIG); 959eb1d1641SHerbert Xu } 960eb1d1641SHerbert Xu 961eb1d1641SHerbert Xu mp = kzalloc(sizeof(*mp), GFP_ATOMIC); 962eb1d1641SHerbert Xu if (unlikely(!mp)) 9634c0833bcSTobias Klauser return ERR_PTR(-ENOMEM); 964eb1d1641SHerbert Xu 965eb1d1641SHerbert Xu mp->br = br; 9668ef2a9a5SYOSHIFUJI Hideaki mp->addr = *group; 967e12cec65SNikolay Aleksandrov mp->mcast_gc.destroy = br_multicast_destroy_mdb_entry; 96888c1f37fSAllen Pais timer_setup(&mp->timer, br_multicast_group_expired, 0); 96919e3a9c9SNikolay Aleksandrov err = rhashtable_lookup_insert_fast(&br->mdb_hash_tbl, &mp->rhnode, 97019e3a9c9SNikolay Aleksandrov br_mdb_rht_params); 97119e3a9c9SNikolay Aleksandrov if (err) { 97219e3a9c9SNikolay Aleksandrov kfree(mp); 97319e3a9c9SNikolay Aleksandrov mp = ERR_PTR(err); 97419e3a9c9SNikolay Aleksandrov } else { 97519e3a9c9SNikolay Aleksandrov hlist_add_head_rcu(&mp->mdb_node, &br->mdb_list); 97619e3a9c9SNikolay Aleksandrov } 9771faabf2aSEric Dumazet 978eb1d1641SHerbert Xu return mp; 979eb1d1641SHerbert Xu } 980eb1d1641SHerbert Xu 9818b671779SNikolay Aleksandrov static void br_multicast_group_src_expired(struct timer_list *t) 9828b671779SNikolay Aleksandrov { 9838b671779SNikolay Aleksandrov struct net_bridge_group_src *src = from_timer(src, t, timer); 9848b671779SNikolay Aleksandrov struct net_bridge_port_group *pg; 9858b671779SNikolay Aleksandrov struct net_bridge *br = src->br; 9868b671779SNikolay Aleksandrov 9878b671779SNikolay Aleksandrov spin_lock(&br->multicast_lock); 9888b671779SNikolay Aleksandrov if (hlist_unhashed(&src->node) || !netif_running(br->dev) || 9898b671779SNikolay Aleksandrov timer_pending(&src->timer)) 9908b671779SNikolay Aleksandrov goto out; 9918b671779SNikolay Aleksandrov 9928b671779SNikolay Aleksandrov pg = src->pg; 9938b671779SNikolay Aleksandrov if (pg->filter_mode == MCAST_INCLUDE) { 9948b671779SNikolay Aleksandrov br_multicast_del_group_src(src); 9958b671779SNikolay Aleksandrov if (!hlist_empty(&pg->src_list)) 9968b671779SNikolay Aleksandrov goto out; 9978b671779SNikolay Aleksandrov br_multicast_find_del_pg(br, pg); 9988b671779SNikolay Aleksandrov } 9998b671779SNikolay Aleksandrov out: 10008b671779SNikolay Aleksandrov spin_unlock(&br->multicast_lock); 10018b671779SNikolay Aleksandrov } 10028b671779SNikolay Aleksandrov 10038b671779SNikolay Aleksandrov static struct net_bridge_group_src * 10048b671779SNikolay Aleksandrov br_multicast_find_group_src(struct net_bridge_port_group *pg, struct br_ip *ip) 10058b671779SNikolay Aleksandrov { 10068b671779SNikolay Aleksandrov struct net_bridge_group_src *ent; 10078b671779SNikolay Aleksandrov 10088b671779SNikolay Aleksandrov switch (ip->proto) { 10098b671779SNikolay Aleksandrov case htons(ETH_P_IP): 10108b671779SNikolay Aleksandrov hlist_for_each_entry(ent, &pg->src_list, node) 1011deb96566SNikolay Aleksandrov if (ip->src.ip4 == ent->addr.src.ip4) 10128b671779SNikolay Aleksandrov return ent; 10138b671779SNikolay Aleksandrov break; 10148b671779SNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6) 10158b671779SNikolay Aleksandrov case htons(ETH_P_IPV6): 10168b671779SNikolay Aleksandrov hlist_for_each_entry(ent, &pg->src_list, node) 1017deb96566SNikolay Aleksandrov if (!ipv6_addr_cmp(&ent->addr.src.ip6, &ip->src.ip6)) 10188b671779SNikolay Aleksandrov return ent; 10198b671779SNikolay Aleksandrov break; 10208b671779SNikolay Aleksandrov #endif 10218b671779SNikolay Aleksandrov } 10228b671779SNikolay Aleksandrov 10238b671779SNikolay Aleksandrov return NULL; 10248b671779SNikolay Aleksandrov } 10258b671779SNikolay Aleksandrov 10268b671779SNikolay Aleksandrov static struct net_bridge_group_src * 10278b671779SNikolay Aleksandrov br_multicast_new_group_src(struct net_bridge_port_group *pg, struct br_ip *src_ip) 10288b671779SNikolay Aleksandrov { 10298b671779SNikolay Aleksandrov struct net_bridge_group_src *grp_src; 10308b671779SNikolay Aleksandrov 10318b671779SNikolay Aleksandrov if (unlikely(pg->src_ents >= PG_SRC_ENT_LIMIT)) 10328b671779SNikolay Aleksandrov return NULL; 10338b671779SNikolay Aleksandrov 10348b671779SNikolay Aleksandrov switch (src_ip->proto) { 10358b671779SNikolay Aleksandrov case htons(ETH_P_IP): 1036deb96566SNikolay Aleksandrov if (ipv4_is_zeronet(src_ip->src.ip4) || 1037deb96566SNikolay Aleksandrov ipv4_is_multicast(src_ip->src.ip4)) 10388b671779SNikolay Aleksandrov return NULL; 10398b671779SNikolay Aleksandrov break; 10408b671779SNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6) 10418b671779SNikolay Aleksandrov case htons(ETH_P_IPV6): 1042deb96566SNikolay Aleksandrov if (ipv6_addr_any(&src_ip->src.ip6) || 1043deb96566SNikolay Aleksandrov ipv6_addr_is_multicast(&src_ip->src.ip6)) 10448b671779SNikolay Aleksandrov return NULL; 10458b671779SNikolay Aleksandrov break; 10468b671779SNikolay Aleksandrov #endif 10478b671779SNikolay Aleksandrov } 10488b671779SNikolay Aleksandrov 10498b671779SNikolay Aleksandrov grp_src = kzalloc(sizeof(*grp_src), GFP_ATOMIC); 10508b671779SNikolay Aleksandrov if (unlikely(!grp_src)) 10518b671779SNikolay Aleksandrov return NULL; 10528b671779SNikolay Aleksandrov 10538b671779SNikolay Aleksandrov grp_src->pg = pg; 1054085b53c8SNikolay Aleksandrov grp_src->br = pg->key.port->br; 10558b671779SNikolay Aleksandrov grp_src->addr = *src_ip; 1056e12cec65SNikolay Aleksandrov grp_src->mcast_gc.destroy = br_multicast_destroy_group_src; 10578b671779SNikolay Aleksandrov timer_setup(&grp_src->timer, br_multicast_group_src_expired, 0); 10588b671779SNikolay Aleksandrov 10598b671779SNikolay Aleksandrov hlist_add_head_rcu(&grp_src->node, &pg->src_list); 10608b671779SNikolay Aleksandrov pg->src_ents++; 10618b671779SNikolay Aleksandrov 10628b671779SNikolay Aleksandrov return grp_src; 10638b671779SNikolay Aleksandrov } 10648b671779SNikolay Aleksandrov 1065cfd56754SCong Wang struct net_bridge_port_group *br_multicast_new_port_group( 1066cfd56754SCong Wang struct net_bridge_port *port, 1067cfd56754SCong Wang struct br_ip *group, 1068ccb1c31aSAmerigo Wang struct net_bridge_port_group __rcu *next, 10696db6f0eaSFelix Fietkau unsigned char flags, 10708b671779SNikolay Aleksandrov const unsigned char *src, 10718f8cb77eSNikolay Aleksandrov u8 filter_mode, 10728f8cb77eSNikolay Aleksandrov u8 rt_protocol) 1073cfd56754SCong Wang { 1074cfd56754SCong Wang struct net_bridge_port_group *p; 1075cfd56754SCong Wang 1076cfd56754SCong Wang p = kzalloc(sizeof(*p), GFP_ATOMIC); 1077cfd56754SCong Wang if (unlikely(!p)) 1078cfd56754SCong Wang return NULL; 1079cfd56754SCong Wang 1080085b53c8SNikolay Aleksandrov p->key.addr = *group; 1081085b53c8SNikolay Aleksandrov p->key.port = port; 10829d06b6d8SElad Raz p->flags = flags; 10838b671779SNikolay Aleksandrov p->filter_mode = filter_mode; 10848f8cb77eSNikolay Aleksandrov p->rt_protocol = rt_protocol; 1085e12cec65SNikolay Aleksandrov p->mcast_gc.destroy = br_multicast_destroy_port_group; 10868b671779SNikolay Aleksandrov INIT_HLIST_HEAD(&p->src_list); 1087085b53c8SNikolay Aleksandrov 1088085b53c8SNikolay Aleksandrov if (!br_multicast_is_star_g(group) && 1089085b53c8SNikolay Aleksandrov rhashtable_lookup_insert_fast(&port->br->sg_port_tbl, &p->rhnode, 1090085b53c8SNikolay Aleksandrov br_sg_port_rht_params)) { 1091085b53c8SNikolay Aleksandrov kfree(p); 1092085b53c8SNikolay Aleksandrov return NULL; 1093085b53c8SNikolay Aleksandrov } 1094085b53c8SNikolay Aleksandrov 1095eca2a43bSstephen hemminger rcu_assign_pointer(p->next, next); 109688c1f37fSAllen Pais timer_setup(&p->timer, br_multicast_port_group_expired, 0); 109742c11ccfSNikolay Aleksandrov timer_setup(&p->rexmit_timer, br_multicast_port_group_rexmit, 0); 109842c11ccfSNikolay Aleksandrov hlist_add_head(&p->mglist, &port->mglist); 10996db6f0eaSFelix Fietkau 11006db6f0eaSFelix Fietkau if (src) 11016db6f0eaSFelix Fietkau memcpy(p->eth_addr, src, ETH_ALEN); 11026db6f0eaSFelix Fietkau else 11031bfe45f4SMao Wenan eth_broadcast_addr(p->eth_addr); 11046db6f0eaSFelix Fietkau 1105cfd56754SCong Wang return p; 1106cfd56754SCong Wang } 1107cfd56754SCong Wang 11081bc844eeSNikolay Aleksandrov void br_multicast_host_join(struct net_bridge_mdb_entry *mp, bool notify) 11091bc844eeSNikolay Aleksandrov { 11101bc844eeSNikolay Aleksandrov if (!mp->host_joined) { 11111bc844eeSNikolay Aleksandrov mp->host_joined = true; 11121bc844eeSNikolay Aleksandrov if (notify) 111381f19838SNikolay Aleksandrov br_mdb_notify(mp->br->dev, mp, NULL, RTM_NEWMDB); 11141bc844eeSNikolay Aleksandrov } 11151bc844eeSNikolay Aleksandrov mod_timer(&mp->timer, jiffies + mp->br->multicast_membership_interval); 11161bc844eeSNikolay Aleksandrov } 11171bc844eeSNikolay Aleksandrov 11181bc844eeSNikolay Aleksandrov void br_multicast_host_leave(struct net_bridge_mdb_entry *mp, bool notify) 11191bc844eeSNikolay Aleksandrov { 11201bc844eeSNikolay Aleksandrov if (!mp->host_joined) 11211bc844eeSNikolay Aleksandrov return; 11221bc844eeSNikolay Aleksandrov 11231bc844eeSNikolay Aleksandrov mp->host_joined = false; 11241bc844eeSNikolay Aleksandrov if (notify) 112581f19838SNikolay Aleksandrov br_mdb_notify(mp->br->dev, mp, NULL, RTM_DELMDB); 11261bc844eeSNikolay Aleksandrov } 11271bc844eeSNikolay Aleksandrov 1128b0812368SNikolay Aleksandrov static struct net_bridge_port_group * 1129b0812368SNikolay Aleksandrov __br_multicast_add_group(struct net_bridge *br, 11308ef2a9a5SYOSHIFUJI Hideaki struct net_bridge_port *port, 11316db6f0eaSFelix Fietkau struct br_ip *group, 11328b671779SNikolay Aleksandrov const unsigned char *src, 11330436862eSNikolay Aleksandrov u8 filter_mode, 11340436862eSNikolay Aleksandrov bool igmpv2_mldv1) 1135eb1d1641SHerbert Xu { 1136e8051688SEric Dumazet struct net_bridge_port_group __rcu **pp; 1137b0812368SNikolay Aleksandrov struct net_bridge_port_group *p = NULL; 11385e923585SNikolay Aleksandrov struct net_bridge_mdb_entry *mp; 1139454594f3SLinus Lüssing unsigned long now = jiffies; 1140eb1d1641SHerbert Xu 1141eb1d1641SHerbert Xu if (!netif_running(br->dev) || 1142eb1d1641SHerbert Xu (port && port->state == BR_STATE_DISABLED)) 1143eb1d1641SHerbert Xu goto out; 1144eb1d1641SHerbert Xu 114519e3a9c9SNikolay Aleksandrov mp = br_multicast_new_group(br, group); 11464c0833bcSTobias Klauser if (IS_ERR(mp)) 1147b0812368SNikolay Aleksandrov return ERR_PTR(PTR_ERR(mp)); 1148eb1d1641SHerbert Xu 1149eb1d1641SHerbert Xu if (!port) { 11501bc844eeSNikolay Aleksandrov br_multicast_host_join(mp, true); 1151eb1d1641SHerbert Xu goto out; 1152eb1d1641SHerbert Xu } 1153eb1d1641SHerbert Xu 1154e8051688SEric Dumazet for (pp = &mp->ports; 1155e8051688SEric Dumazet (p = mlock_dereference(*pp, br)) != NULL; 1156e8051688SEric Dumazet pp = &p->next) { 11576db6f0eaSFelix Fietkau if (br_port_group_equal(p, port, src)) 1158454594f3SLinus Lüssing goto found; 1159085b53c8SNikolay Aleksandrov if ((unsigned long)p->key.port < (unsigned long)port) 1160eb1d1641SHerbert Xu break; 1161eb1d1641SHerbert Xu } 1162eb1d1641SHerbert Xu 11638f8cb77eSNikolay Aleksandrov p = br_multicast_new_port_group(port, group, *pp, 0, src, filter_mode, 11648f8cb77eSNikolay Aleksandrov RTPROT_KERNEL); 1165b0812368SNikolay Aleksandrov if (unlikely(!p)) { 1166b0812368SNikolay Aleksandrov p = ERR_PTR(-ENOMEM); 1167b0812368SNikolay Aleksandrov goto out; 1168b0812368SNikolay Aleksandrov } 1169eb1d1641SHerbert Xu rcu_assign_pointer(*pp, p); 117081f19838SNikolay Aleksandrov br_mdb_notify(br->dev, mp, p, RTM_NEWMDB); 1171eb1d1641SHerbert Xu 1172454594f3SLinus Lüssing found: 11730436862eSNikolay Aleksandrov if (igmpv2_mldv1) 1174454594f3SLinus Lüssing mod_timer(&p->timer, now + br->multicast_membership_interval); 117581f19838SNikolay Aleksandrov 1176eb1d1641SHerbert Xu out: 1177b0812368SNikolay Aleksandrov return p; 1178b0812368SNikolay Aleksandrov } 1179eb1d1641SHerbert Xu 1180b0812368SNikolay Aleksandrov static int br_multicast_add_group(struct net_bridge *br, 1181b0812368SNikolay Aleksandrov struct net_bridge_port *port, 1182b0812368SNikolay Aleksandrov struct br_ip *group, 1183b0812368SNikolay Aleksandrov const unsigned char *src, 1184b0812368SNikolay Aleksandrov u8 filter_mode, 1185b0812368SNikolay Aleksandrov bool igmpv2_mldv1) 1186b0812368SNikolay Aleksandrov { 1187b0812368SNikolay Aleksandrov struct net_bridge_port_group *pg; 1188b0812368SNikolay Aleksandrov int err; 1189b0812368SNikolay Aleksandrov 1190b0812368SNikolay Aleksandrov spin_lock(&br->multicast_lock); 1191b0812368SNikolay Aleksandrov pg = __br_multicast_add_group(br, port, group, src, filter_mode, 1192b0812368SNikolay Aleksandrov igmpv2_mldv1); 1193b0812368SNikolay Aleksandrov /* NULL is considered valid for host joined groups */ 1194b0812368SNikolay Aleksandrov err = IS_ERR(pg) ? PTR_ERR(pg) : 0; 1195eb1d1641SHerbert Xu spin_unlock(&br->multicast_lock); 1196b0812368SNikolay Aleksandrov 1197eb1d1641SHerbert Xu return err; 1198eb1d1641SHerbert Xu } 1199eb1d1641SHerbert Xu 12008ef2a9a5SYOSHIFUJI Hideaki static int br_ip4_multicast_add_group(struct net_bridge *br, 12018ef2a9a5SYOSHIFUJI Hideaki struct net_bridge_port *port, 1202b0e9a30dSVlad Yasevich __be32 group, 12036db6f0eaSFelix Fietkau __u16 vid, 12048b671779SNikolay Aleksandrov const unsigned char *src, 12058b671779SNikolay Aleksandrov bool igmpv2) 12068ef2a9a5SYOSHIFUJI Hideaki { 12078ef2a9a5SYOSHIFUJI Hideaki struct br_ip br_group; 12088b671779SNikolay Aleksandrov u8 filter_mode; 12098ef2a9a5SYOSHIFUJI Hideaki 12108ef2a9a5SYOSHIFUJI Hideaki if (ipv4_is_local_multicast(group)) 12118ef2a9a5SYOSHIFUJI Hideaki return 0; 12128ef2a9a5SYOSHIFUJI Hideaki 12131515a63fSNikolay Aleksandrov memset(&br_group, 0, sizeof(br_group)); 1214eab3227bSNikolay Aleksandrov br_group.dst.ip4 = group; 12158ef2a9a5SYOSHIFUJI Hideaki br_group.proto = htons(ETH_P_IP); 1216b0e9a30dSVlad Yasevich br_group.vid = vid; 12178b671779SNikolay Aleksandrov filter_mode = igmpv2 ? MCAST_EXCLUDE : MCAST_INCLUDE; 12188ef2a9a5SYOSHIFUJI Hideaki 12190436862eSNikolay Aleksandrov return br_multicast_add_group(br, port, &br_group, src, filter_mode, 12200436862eSNikolay Aleksandrov igmpv2); 12218ef2a9a5SYOSHIFUJI Hideaki } 12228ef2a9a5SYOSHIFUJI Hideaki 1223dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 122408b202b6SYOSHIFUJI Hideaki static int br_ip6_multicast_add_group(struct net_bridge *br, 122508b202b6SYOSHIFUJI Hideaki struct net_bridge_port *port, 1226b0e9a30dSVlad Yasevich const struct in6_addr *group, 12276db6f0eaSFelix Fietkau __u16 vid, 12288b671779SNikolay Aleksandrov const unsigned char *src, 12298b671779SNikolay Aleksandrov bool mldv1) 123008b202b6SYOSHIFUJI Hideaki { 123108b202b6SYOSHIFUJI Hideaki struct br_ip br_group; 12328b671779SNikolay Aleksandrov u8 filter_mode; 123308b202b6SYOSHIFUJI Hideaki 12343c3769e6SLinus Lüssing if (ipv6_addr_is_ll_all_nodes(group)) 123508b202b6SYOSHIFUJI Hideaki return 0; 123608b202b6SYOSHIFUJI Hideaki 123719e3a9c9SNikolay Aleksandrov memset(&br_group, 0, sizeof(br_group)); 1238eab3227bSNikolay Aleksandrov br_group.dst.ip6 = *group; 12399cc6e0c4SLinus Lüssing br_group.proto = htons(ETH_P_IPV6); 1240b0e9a30dSVlad Yasevich br_group.vid = vid; 12418b671779SNikolay Aleksandrov filter_mode = mldv1 ? MCAST_EXCLUDE : MCAST_INCLUDE; 124208b202b6SYOSHIFUJI Hideaki 12430436862eSNikolay Aleksandrov return br_multicast_add_group(br, port, &br_group, src, filter_mode, 12440436862eSNikolay Aleksandrov mldv1); 124508b202b6SYOSHIFUJI Hideaki } 124608b202b6SYOSHIFUJI Hideaki #endif 124708b202b6SYOSHIFUJI Hideaki 124888c1f37fSAllen Pais static void br_multicast_router_expired(struct timer_list *t) 1249eb1d1641SHerbert Xu { 125088c1f37fSAllen Pais struct net_bridge_port *port = 125188c1f37fSAllen Pais from_timer(port, t, multicast_router_timer); 1252eb1d1641SHerbert Xu struct net_bridge *br = port->br; 1253eb1d1641SHerbert Xu 1254eb1d1641SHerbert Xu spin_lock(&br->multicast_lock); 1255a55d8246SNikolay Aleksandrov if (port->multicast_router == MDB_RTR_TYPE_DISABLED || 1256a55d8246SNikolay Aleksandrov port->multicast_router == MDB_RTR_TYPE_PERM || 1257f12e7d95SNogah Frankel timer_pending(&port->multicast_router_timer)) 1258eb1d1641SHerbert Xu goto out; 1259eb1d1641SHerbert Xu 1260f12e7d95SNogah Frankel __del_port_router(port); 1261eb1d1641SHerbert Xu out: 1262eb1d1641SHerbert Xu spin_unlock(&br->multicast_lock); 1263eb1d1641SHerbert Xu } 1264eb1d1641SHerbert Xu 126577041420SYotam Gigi static void br_mc_router_state_change(struct net_bridge *p, 126677041420SYotam Gigi bool is_mc_router) 126777041420SYotam Gigi { 126877041420SYotam Gigi struct switchdev_attr attr = { 126977041420SYotam Gigi .orig_dev = p->dev, 127077041420SYotam Gigi .id = SWITCHDEV_ATTR_ID_BRIDGE_MROUTER, 127177041420SYotam Gigi .flags = SWITCHDEV_F_DEFER, 127277041420SYotam Gigi .u.mrouter = is_mc_router, 127377041420SYotam Gigi }; 127477041420SYotam Gigi 127577041420SYotam Gigi switchdev_port_attr_set(p->dev, &attr); 127677041420SYotam Gigi } 127777041420SYotam Gigi 127888c1f37fSAllen Pais static void br_multicast_local_router_expired(struct timer_list *t) 1279eb1d1641SHerbert Xu { 128088c1f37fSAllen Pais struct net_bridge *br = from_timer(br, t, multicast_router_timer); 128177041420SYotam Gigi 128277041420SYotam Gigi spin_lock(&br->multicast_lock); 128377041420SYotam Gigi if (br->multicast_router == MDB_RTR_TYPE_DISABLED || 128477041420SYotam Gigi br->multicast_router == MDB_RTR_TYPE_PERM || 128577041420SYotam Gigi timer_pending(&br->multicast_router_timer)) 128677041420SYotam Gigi goto out; 128777041420SYotam Gigi 128877041420SYotam Gigi br_mc_router_state_change(br, false); 128977041420SYotam Gigi out: 129077041420SYotam Gigi spin_unlock(&br->multicast_lock); 1291eb1d1641SHerbert Xu } 1292eb1d1641SHerbert Xu 1293cc0fdd80SLinus Lüssing static void br_multicast_querier_expired(struct net_bridge *br, 129490010b36SLinus Lüssing struct bridge_mcast_own_query *query) 1295c83b8fabSHerbert Xu { 1296c83b8fabSHerbert Xu spin_lock(&br->multicast_lock); 129713cefad2SNikolay Aleksandrov if (!netif_running(br->dev) || !br_opt_get(br, BROPT_MULTICAST_ENABLED)) 1298c83b8fabSHerbert Xu goto out; 1299c83b8fabSHerbert Xu 1300cc0fdd80SLinus Lüssing br_multicast_start_querier(br, query); 1301c83b8fabSHerbert Xu 1302c83b8fabSHerbert Xu out: 1303c83b8fabSHerbert Xu spin_unlock(&br->multicast_lock); 1304c83b8fabSHerbert Xu } 1305c83b8fabSHerbert Xu 130688c1f37fSAllen Pais static void br_ip4_multicast_querier_expired(struct timer_list *t) 1307cc0fdd80SLinus Lüssing { 130888c1f37fSAllen Pais struct net_bridge *br = from_timer(br, t, ip4_other_query.timer); 1309cc0fdd80SLinus Lüssing 131090010b36SLinus Lüssing br_multicast_querier_expired(br, &br->ip4_own_query); 1311cc0fdd80SLinus Lüssing } 1312cc0fdd80SLinus Lüssing 1313cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 131488c1f37fSAllen Pais static void br_ip6_multicast_querier_expired(struct timer_list *t) 1315cc0fdd80SLinus Lüssing { 131688c1f37fSAllen Pais struct net_bridge *br = from_timer(br, t, ip6_other_query.timer); 1317cc0fdd80SLinus Lüssing 131890010b36SLinus Lüssing br_multicast_querier_expired(br, &br->ip6_own_query); 1319cc0fdd80SLinus Lüssing } 1320cc0fdd80SLinus Lüssing #endif 1321cc0fdd80SLinus Lüssing 1322dc4eb53aSLinus Lüssing static void br_multicast_select_own_querier(struct net_bridge *br, 1323dc4eb53aSLinus Lüssing struct br_ip *ip, 1324dc4eb53aSLinus Lüssing struct sk_buff *skb) 1325dc4eb53aSLinus Lüssing { 1326dc4eb53aSLinus Lüssing if (ip->proto == htons(ETH_P_IP)) 1327deb96566SNikolay Aleksandrov br->ip4_querier.addr.src.ip4 = ip_hdr(skb)->saddr; 1328dc4eb53aSLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 1329dc4eb53aSLinus Lüssing else 1330deb96566SNikolay Aleksandrov br->ip6_querier.addr.src.ip6 = ipv6_hdr(skb)->saddr; 1331dc4eb53aSLinus Lüssing #endif 1332dc4eb53aSLinus Lüssing } 1333dc4eb53aSLinus Lüssing 13348ef2a9a5SYOSHIFUJI Hideaki static void __br_multicast_send_query(struct net_bridge *br, 13358ef2a9a5SYOSHIFUJI Hideaki struct net_bridge_port *port, 1336438ef2d0SNikolay Aleksandrov struct net_bridge_port_group *pg, 1337438ef2d0SNikolay Aleksandrov struct br_ip *ip_dst, 1338438ef2d0SNikolay Aleksandrov struct br_ip *group, 1339438ef2d0SNikolay Aleksandrov bool with_srcs, 134042c11ccfSNikolay Aleksandrov u8 sflag, 134142c11ccfSNikolay Aleksandrov bool *need_rexmit) 1342eb1d1641SHerbert Xu { 1343438ef2d0SNikolay Aleksandrov bool over_lmqt = !!sflag; 1344eb1d1641SHerbert Xu struct sk_buff *skb; 13451080ab95SNikolay Aleksandrov u8 igmp_type; 1346eb1d1641SHerbert Xu 1347438ef2d0SNikolay Aleksandrov again_under_lmqt: 1348438ef2d0SNikolay Aleksandrov skb = br_multicast_alloc_query(br, pg, ip_dst, group, with_srcs, 134942c11ccfSNikolay Aleksandrov over_lmqt, sflag, &igmp_type, 135042c11ccfSNikolay Aleksandrov need_rexmit); 1351eb1d1641SHerbert Xu if (!skb) 13528ef2a9a5SYOSHIFUJI Hideaki return; 1353eb1d1641SHerbert Xu 1354eb1d1641SHerbert Xu if (port) { 1355eb1d1641SHerbert Xu skb->dev = port->dev; 1356a65056ecSNikolay Aleksandrov br_multicast_count(br, port, skb, igmp_type, 13571080ab95SNikolay Aleksandrov BR_MCAST_DIR_TX); 135829a26a56SEric W. Biederman NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, 135929a26a56SEric W. Biederman dev_net(port->dev), NULL, skb, NULL, skb->dev, 1360f0b4eeceSLinus Lüssing br_dev_queue_push_xmit); 1361438ef2d0SNikolay Aleksandrov 1362438ef2d0SNikolay Aleksandrov if (over_lmqt && with_srcs && sflag) { 1363438ef2d0SNikolay Aleksandrov over_lmqt = false; 1364438ef2d0SNikolay Aleksandrov goto again_under_lmqt; 1365438ef2d0SNikolay Aleksandrov } 1366dc4eb53aSLinus Lüssing } else { 1367438ef2d0SNikolay Aleksandrov br_multicast_select_own_querier(br, group, skb); 1368a65056ecSNikolay Aleksandrov br_multicast_count(br, port, skb, igmp_type, 13691080ab95SNikolay Aleksandrov BR_MCAST_DIR_RX); 1370eb1d1641SHerbert Xu netif_rx(skb); 13718ef2a9a5SYOSHIFUJI Hideaki } 1372dc4eb53aSLinus Lüssing } 1373eb1d1641SHerbert Xu 13748ef2a9a5SYOSHIFUJI Hideaki static void br_multicast_send_query(struct net_bridge *br, 1375cc0fdd80SLinus Lüssing struct net_bridge_port *port, 137690010b36SLinus Lüssing struct bridge_mcast_own_query *own_query) 13778ef2a9a5SYOSHIFUJI Hideaki { 137890010b36SLinus Lüssing struct bridge_mcast_other_query *other_query = NULL; 13795e923585SNikolay Aleksandrov struct br_ip br_group; 13805e923585SNikolay Aleksandrov unsigned long time; 13818ef2a9a5SYOSHIFUJI Hideaki 138213cefad2SNikolay Aleksandrov if (!netif_running(br->dev) || 138313cefad2SNikolay Aleksandrov !br_opt_get(br, BROPT_MULTICAST_ENABLED) || 1384675779adSNikolay Aleksandrov !br_opt_get(br, BROPT_MULTICAST_QUERIER)) 13858ef2a9a5SYOSHIFUJI Hideaki return; 13868ef2a9a5SYOSHIFUJI Hideaki 1387eab3227bSNikolay Aleksandrov memset(&br_group.dst, 0, sizeof(br_group.dst)); 13888ef2a9a5SYOSHIFUJI Hideaki 138990010b36SLinus Lüssing if (port ? (own_query == &port->ip4_own_query) : 139090010b36SLinus Lüssing (own_query == &br->ip4_own_query)) { 139190010b36SLinus Lüssing other_query = &br->ip4_other_query; 139208b202b6SYOSHIFUJI Hideaki br_group.proto = htons(ETH_P_IP); 1393dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 1394cc0fdd80SLinus Lüssing } else { 139590010b36SLinus Lüssing other_query = &br->ip6_other_query; 139608b202b6SYOSHIFUJI Hideaki br_group.proto = htons(ETH_P_IPV6); 139708b202b6SYOSHIFUJI Hideaki #endif 1398eb1d1641SHerbert Xu } 1399eb1d1641SHerbert Xu 140090010b36SLinus Lüssing if (!other_query || timer_pending(&other_query->timer)) 1401cc0fdd80SLinus Lüssing return; 1402cc0fdd80SLinus Lüssing 140342c11ccfSNikolay Aleksandrov __br_multicast_send_query(br, port, NULL, NULL, &br_group, false, 0, 140442c11ccfSNikolay Aleksandrov NULL); 1405cc0fdd80SLinus Lüssing 1406cc0fdd80SLinus Lüssing time = jiffies; 140790010b36SLinus Lüssing time += own_query->startup_sent < br->multicast_startup_query_count ? 1408cc0fdd80SLinus Lüssing br->multicast_startup_query_interval : 1409cc0fdd80SLinus Lüssing br->multicast_query_interval; 141090010b36SLinus Lüssing mod_timer(&own_query->timer, time); 1411cc0fdd80SLinus Lüssing } 1412cc0fdd80SLinus Lüssing 141390010b36SLinus Lüssing static void 141490010b36SLinus Lüssing br_multicast_port_query_expired(struct net_bridge_port *port, 141590010b36SLinus Lüssing struct bridge_mcast_own_query *query) 1416eb1d1641SHerbert Xu { 1417eb1d1641SHerbert Xu struct net_bridge *br = port->br; 1418eb1d1641SHerbert Xu 1419eb1d1641SHerbert Xu spin_lock(&br->multicast_lock); 142002a780c0SDan Carpenter if (port->state == BR_STATE_DISABLED || 142102a780c0SDan Carpenter port->state == BR_STATE_BLOCKING) 1422eb1d1641SHerbert Xu goto out; 1423eb1d1641SHerbert Xu 1424cc0fdd80SLinus Lüssing if (query->startup_sent < br->multicast_startup_query_count) 1425cc0fdd80SLinus Lüssing query->startup_sent++; 1426eb1d1641SHerbert Xu 1427cc0fdd80SLinus Lüssing br_multicast_send_query(port->br, port, query); 1428eb1d1641SHerbert Xu 1429eb1d1641SHerbert Xu out: 1430eb1d1641SHerbert Xu spin_unlock(&br->multicast_lock); 1431eb1d1641SHerbert Xu } 1432eb1d1641SHerbert Xu 143388c1f37fSAllen Pais static void br_ip4_multicast_port_query_expired(struct timer_list *t) 1434cc0fdd80SLinus Lüssing { 143588c1f37fSAllen Pais struct net_bridge_port *port = from_timer(port, t, ip4_own_query.timer); 1436cc0fdd80SLinus Lüssing 143790010b36SLinus Lüssing br_multicast_port_query_expired(port, &port->ip4_own_query); 1438cc0fdd80SLinus Lüssing } 1439cc0fdd80SLinus Lüssing 1440cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 144188c1f37fSAllen Pais static void br_ip6_multicast_port_query_expired(struct timer_list *t) 1442cc0fdd80SLinus Lüssing { 144388c1f37fSAllen Pais struct net_bridge_port *port = from_timer(port, t, ip6_own_query.timer); 1444cc0fdd80SLinus Lüssing 144590010b36SLinus Lüssing br_multicast_port_query_expired(port, &port->ip6_own_query); 1446cc0fdd80SLinus Lüssing } 1447cc0fdd80SLinus Lüssing #endif 1448cc0fdd80SLinus Lüssing 144942c11ccfSNikolay Aleksandrov static void br_multicast_port_group_rexmit(struct timer_list *t) 145042c11ccfSNikolay Aleksandrov { 145142c11ccfSNikolay Aleksandrov struct net_bridge_port_group *pg = from_timer(pg, t, rexmit_timer); 145242c11ccfSNikolay Aleksandrov struct bridge_mcast_other_query *other_query = NULL; 1453085b53c8SNikolay Aleksandrov struct net_bridge *br = pg->key.port->br; 145442c11ccfSNikolay Aleksandrov bool need_rexmit = false; 145542c11ccfSNikolay Aleksandrov 145642c11ccfSNikolay Aleksandrov spin_lock(&br->multicast_lock); 145742c11ccfSNikolay Aleksandrov if (!netif_running(br->dev) || hlist_unhashed(&pg->mglist) || 145842c11ccfSNikolay Aleksandrov !br_opt_get(br, BROPT_MULTICAST_ENABLED) || 145942c11ccfSNikolay Aleksandrov !br_opt_get(br, BROPT_MULTICAST_QUERIER)) 146042c11ccfSNikolay Aleksandrov goto out; 146142c11ccfSNikolay Aleksandrov 1462085b53c8SNikolay Aleksandrov if (pg->key.addr.proto == htons(ETH_P_IP)) 146342c11ccfSNikolay Aleksandrov other_query = &br->ip4_other_query; 146442c11ccfSNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6) 146542c11ccfSNikolay Aleksandrov else 146642c11ccfSNikolay Aleksandrov other_query = &br->ip6_other_query; 146742c11ccfSNikolay Aleksandrov #endif 146842c11ccfSNikolay Aleksandrov 146942c11ccfSNikolay Aleksandrov if (!other_query || timer_pending(&other_query->timer)) 147042c11ccfSNikolay Aleksandrov goto out; 147142c11ccfSNikolay Aleksandrov 147242c11ccfSNikolay Aleksandrov if (pg->grp_query_rexmit_cnt) { 147342c11ccfSNikolay Aleksandrov pg->grp_query_rexmit_cnt--; 1474085b53c8SNikolay Aleksandrov __br_multicast_send_query(br, pg->key.port, pg, &pg->key.addr, 1475085b53c8SNikolay Aleksandrov &pg->key.addr, false, 1, NULL); 147642c11ccfSNikolay Aleksandrov } 1477085b53c8SNikolay Aleksandrov __br_multicast_send_query(br, pg->key.port, pg, &pg->key.addr, 1478085b53c8SNikolay Aleksandrov &pg->key.addr, true, 0, &need_rexmit); 147942c11ccfSNikolay Aleksandrov 148042c11ccfSNikolay Aleksandrov if (pg->grp_query_rexmit_cnt || need_rexmit) 148142c11ccfSNikolay Aleksandrov mod_timer(&pg->rexmit_timer, jiffies + 148242c11ccfSNikolay Aleksandrov br->multicast_last_member_interval); 148342c11ccfSNikolay Aleksandrov out: 148442c11ccfSNikolay Aleksandrov spin_unlock(&br->multicast_lock); 148542c11ccfSNikolay Aleksandrov } 148642c11ccfSNikolay Aleksandrov 1487147c1e9bSNogah Frankel static void br_mc_disabled_update(struct net_device *dev, bool value) 1488147c1e9bSNogah Frankel { 1489147c1e9bSNogah Frankel struct switchdev_attr attr = { 1490147c1e9bSNogah Frankel .orig_dev = dev, 1491147c1e9bSNogah Frankel .id = SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED, 1492147c1e9bSNogah Frankel .flags = SWITCHDEV_F_DEFER, 149313cefad2SNikolay Aleksandrov .u.mc_disabled = !value, 1494147c1e9bSNogah Frankel }; 1495147c1e9bSNogah Frankel 1496147c1e9bSNogah Frankel switchdev_port_attr_set(dev, &attr); 1497147c1e9bSNogah Frankel } 1498147c1e9bSNogah Frankel 14991080ab95SNikolay Aleksandrov int br_multicast_add_port(struct net_bridge_port *port) 1500eb1d1641SHerbert Xu { 15017f0aec7aSNikolay Aleksandrov port->multicast_router = MDB_RTR_TYPE_TEMP_QUERY; 1502eb1d1641SHerbert Xu 150388c1f37fSAllen Pais timer_setup(&port->multicast_router_timer, 150488c1f37fSAllen Pais br_multicast_router_expired, 0); 150588c1f37fSAllen Pais timer_setup(&port->ip4_own_query.timer, 150688c1f37fSAllen Pais br_ip4_multicast_port_query_expired, 0); 1507cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 150888c1f37fSAllen Pais timer_setup(&port->ip6_own_query.timer, 150988c1f37fSAllen Pais br_ip6_multicast_port_query_expired, 0); 1510cc0fdd80SLinus Lüssing #endif 151113cefad2SNikolay Aleksandrov br_mc_disabled_update(port->dev, 151213cefad2SNikolay Aleksandrov br_opt_get(port->br, BROPT_MULTICAST_ENABLED)); 1513147c1e9bSNogah Frankel 15141080ab95SNikolay Aleksandrov port->mcast_stats = netdev_alloc_pcpu_stats(struct bridge_mcast_stats); 15151080ab95SNikolay Aleksandrov if (!port->mcast_stats) 15161080ab95SNikolay Aleksandrov return -ENOMEM; 15171080ab95SNikolay Aleksandrov 15181080ab95SNikolay Aleksandrov return 0; 1519eb1d1641SHerbert Xu } 1520eb1d1641SHerbert Xu 1521eb1d1641SHerbert Xu void br_multicast_del_port(struct net_bridge_port *port) 1522eb1d1641SHerbert Xu { 1523e10177abSSatish Ashok struct net_bridge *br = port->br; 1524e10177abSSatish Ashok struct net_bridge_port_group *pg; 1525e12cec65SNikolay Aleksandrov HLIST_HEAD(deleted_head); 1526e10177abSSatish Ashok struct hlist_node *n; 1527e10177abSSatish Ashok 1528e10177abSSatish Ashok /* Take care of the remaining groups, only perm ones should be left */ 1529e10177abSSatish Ashok spin_lock_bh(&br->multicast_lock); 1530e10177abSSatish Ashok hlist_for_each_entry_safe(pg, n, &port->mglist, mglist) 1531681590bdSNikolay Aleksandrov br_multicast_find_del_pg(br, pg); 1532e12cec65SNikolay Aleksandrov hlist_move_list(&br->mcast_gc_list, &deleted_head); 1533e10177abSSatish Ashok spin_unlock_bh(&br->multicast_lock); 1534e12cec65SNikolay Aleksandrov br_multicast_gc(&deleted_head); 1535eb1d1641SHerbert Xu del_timer_sync(&port->multicast_router_timer); 15361080ab95SNikolay Aleksandrov free_percpu(port->mcast_stats); 1537eb1d1641SHerbert Xu } 1538eb1d1641SHerbert Xu 153990010b36SLinus Lüssing static void br_multicast_enable(struct bridge_mcast_own_query *query) 1540561f1103SHerbert Xu { 1541cc0fdd80SLinus Lüssing query->startup_sent = 0; 1542561f1103SHerbert Xu 1543cc0fdd80SLinus Lüssing if (try_to_del_timer_sync(&query->timer) >= 0 || 1544cc0fdd80SLinus Lüssing del_timer(&query->timer)) 1545cc0fdd80SLinus Lüssing mod_timer(&query->timer, jiffies); 1546561f1103SHerbert Xu } 1547561f1103SHerbert Xu 15487cb3f921SNikolay Aleksandrov static void __br_multicast_enable_port(struct net_bridge_port *port) 1549eb1d1641SHerbert Xu { 1550eb1d1641SHerbert Xu struct net_bridge *br = port->br; 1551eb1d1641SHerbert Xu 155213cefad2SNikolay Aleksandrov if (!br_opt_get(br, BROPT_MULTICAST_ENABLED) || !netif_running(br->dev)) 15537cb3f921SNikolay Aleksandrov return; 1554eb1d1641SHerbert Xu 155590010b36SLinus Lüssing br_multicast_enable(&port->ip4_own_query); 1556cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 155790010b36SLinus Lüssing br_multicast_enable(&port->ip6_own_query); 1558cc0fdd80SLinus Lüssing #endif 15597f0aec7aSNikolay Aleksandrov if (port->multicast_router == MDB_RTR_TYPE_PERM && 15607f0aec7aSNikolay Aleksandrov hlist_unhashed(&port->rlist)) 1561754bc547SSatish Ashok br_multicast_add_router(br, port); 15627cb3f921SNikolay Aleksandrov } 1563eb1d1641SHerbert Xu 15647cb3f921SNikolay Aleksandrov void br_multicast_enable_port(struct net_bridge_port *port) 15657cb3f921SNikolay Aleksandrov { 15667cb3f921SNikolay Aleksandrov struct net_bridge *br = port->br; 15677cb3f921SNikolay Aleksandrov 15687cb3f921SNikolay Aleksandrov spin_lock(&br->multicast_lock); 15697cb3f921SNikolay Aleksandrov __br_multicast_enable_port(port); 1570eb1d1641SHerbert Xu spin_unlock(&br->multicast_lock); 1571eb1d1641SHerbert Xu } 1572eb1d1641SHerbert Xu 1573eb1d1641SHerbert Xu void br_multicast_disable_port(struct net_bridge_port *port) 1574eb1d1641SHerbert Xu { 1575eb1d1641SHerbert Xu struct net_bridge *br = port->br; 1576eb1d1641SHerbert Xu struct net_bridge_port_group *pg; 1577b67bfe0dSSasha Levin struct hlist_node *n; 1578eb1d1641SHerbert Xu 1579eb1d1641SHerbert Xu spin_lock(&br->multicast_lock); 1580b67bfe0dSSasha Levin hlist_for_each_entry_safe(pg, n, &port->mglist, mglist) 15819d06b6d8SElad Raz if (!(pg->flags & MDB_PG_FLAGS_PERMANENT)) 1582681590bdSNikolay Aleksandrov br_multicast_find_del_pg(br, pg); 1583eb1d1641SHerbert Xu 1584f12e7d95SNogah Frankel __del_port_router(port); 1585f12e7d95SNogah Frankel 1586eb1d1641SHerbert Xu del_timer(&port->multicast_router_timer); 158790010b36SLinus Lüssing del_timer(&port->ip4_own_query.timer); 1588cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 158990010b36SLinus Lüssing del_timer(&port->ip6_own_query.timer); 1590cc0fdd80SLinus Lüssing #endif 1591eb1d1641SHerbert Xu spin_unlock(&br->multicast_lock); 1592eb1d1641SHerbert Xu } 1593eb1d1641SHerbert Xu 1594e6231bcaSNikolay Aleksandrov static int __grp_src_delete_marked(struct net_bridge_port_group *pg) 1595e6231bcaSNikolay Aleksandrov { 1596e6231bcaSNikolay Aleksandrov struct net_bridge_group_src *ent; 1597e6231bcaSNikolay Aleksandrov struct hlist_node *tmp; 1598e6231bcaSNikolay Aleksandrov int deleted = 0; 1599e6231bcaSNikolay Aleksandrov 1600e6231bcaSNikolay Aleksandrov hlist_for_each_entry_safe(ent, tmp, &pg->src_list, node) 1601e6231bcaSNikolay Aleksandrov if (ent->flags & BR_SGRP_F_DELETE) { 1602e6231bcaSNikolay Aleksandrov br_multicast_del_group_src(ent); 1603e6231bcaSNikolay Aleksandrov deleted++; 1604e6231bcaSNikolay Aleksandrov } 1605e6231bcaSNikolay Aleksandrov 1606e6231bcaSNikolay Aleksandrov return deleted; 1607e6231bcaSNikolay Aleksandrov } 1608e6231bcaSNikolay Aleksandrov 1609b0812368SNikolay Aleksandrov static void __grp_src_mod_timer(struct net_bridge_group_src *src, 1610b0812368SNikolay Aleksandrov unsigned long expires) 1611b0812368SNikolay Aleksandrov { 1612b0812368SNikolay Aleksandrov mod_timer(&src->timer, expires); 1613b0812368SNikolay Aleksandrov br_multicast_fwd_src_handle(src); 1614b0812368SNikolay Aleksandrov } 1615b0812368SNikolay Aleksandrov 16165bf1e00bSNikolay Aleksandrov static void __grp_src_query_marked_and_rexmit(struct net_bridge_port_group *pg) 16175bf1e00bSNikolay Aleksandrov { 16185bf1e00bSNikolay Aleksandrov struct bridge_mcast_other_query *other_query = NULL; 1619085b53c8SNikolay Aleksandrov struct net_bridge *br = pg->key.port->br; 16205bf1e00bSNikolay Aleksandrov u32 lmqc = br->multicast_last_member_count; 16215bf1e00bSNikolay Aleksandrov unsigned long lmqt, lmi, now = jiffies; 16225bf1e00bSNikolay Aleksandrov struct net_bridge_group_src *ent; 16235bf1e00bSNikolay Aleksandrov 16245bf1e00bSNikolay Aleksandrov if (!netif_running(br->dev) || 16255bf1e00bSNikolay Aleksandrov !br_opt_get(br, BROPT_MULTICAST_ENABLED)) 16265bf1e00bSNikolay Aleksandrov return; 16275bf1e00bSNikolay Aleksandrov 1628085b53c8SNikolay Aleksandrov if (pg->key.addr.proto == htons(ETH_P_IP)) 16295bf1e00bSNikolay Aleksandrov other_query = &br->ip4_other_query; 16305bf1e00bSNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6) 16315bf1e00bSNikolay Aleksandrov else 16325bf1e00bSNikolay Aleksandrov other_query = &br->ip6_other_query; 16335bf1e00bSNikolay Aleksandrov #endif 16345bf1e00bSNikolay Aleksandrov 16355bf1e00bSNikolay Aleksandrov lmqt = now + br_multicast_lmqt(br); 16365bf1e00bSNikolay Aleksandrov hlist_for_each_entry(ent, &pg->src_list, node) { 16375bf1e00bSNikolay Aleksandrov if (ent->flags & BR_SGRP_F_SEND) { 16385bf1e00bSNikolay Aleksandrov ent->flags &= ~BR_SGRP_F_SEND; 16395bf1e00bSNikolay Aleksandrov if (ent->timer.expires > lmqt) { 16405bf1e00bSNikolay Aleksandrov if (br_opt_get(br, BROPT_MULTICAST_QUERIER) && 16415bf1e00bSNikolay Aleksandrov other_query && 16425bf1e00bSNikolay Aleksandrov !timer_pending(&other_query->timer)) 16435bf1e00bSNikolay Aleksandrov ent->src_query_rexmit_cnt = lmqc; 1644b0812368SNikolay Aleksandrov __grp_src_mod_timer(ent, lmqt); 16455bf1e00bSNikolay Aleksandrov } 16465bf1e00bSNikolay Aleksandrov } 16475bf1e00bSNikolay Aleksandrov } 16485bf1e00bSNikolay Aleksandrov 16495bf1e00bSNikolay Aleksandrov if (!br_opt_get(br, BROPT_MULTICAST_QUERIER) || 16505bf1e00bSNikolay Aleksandrov !other_query || timer_pending(&other_query->timer)) 16515bf1e00bSNikolay Aleksandrov return; 16525bf1e00bSNikolay Aleksandrov 1653085b53c8SNikolay Aleksandrov __br_multicast_send_query(br, pg->key.port, pg, &pg->key.addr, 1654085b53c8SNikolay Aleksandrov &pg->key.addr, true, 1, NULL); 16555bf1e00bSNikolay Aleksandrov 16565bf1e00bSNikolay Aleksandrov lmi = now + br->multicast_last_member_interval; 16575bf1e00bSNikolay Aleksandrov if (!timer_pending(&pg->rexmit_timer) || 16585bf1e00bSNikolay Aleksandrov time_after(pg->rexmit_timer.expires, lmi)) 16595bf1e00bSNikolay Aleksandrov mod_timer(&pg->rexmit_timer, lmi); 16605bf1e00bSNikolay Aleksandrov } 16615bf1e00bSNikolay Aleksandrov 16625bf1e00bSNikolay Aleksandrov static void __grp_send_query_and_rexmit(struct net_bridge_port_group *pg) 16635bf1e00bSNikolay Aleksandrov { 16645bf1e00bSNikolay Aleksandrov struct bridge_mcast_other_query *other_query = NULL; 1665085b53c8SNikolay Aleksandrov struct net_bridge *br = pg->key.port->br; 16665bf1e00bSNikolay Aleksandrov unsigned long now = jiffies, lmi; 16675bf1e00bSNikolay Aleksandrov 16685bf1e00bSNikolay Aleksandrov if (!netif_running(br->dev) || 16695bf1e00bSNikolay Aleksandrov !br_opt_get(br, BROPT_MULTICAST_ENABLED)) 16705bf1e00bSNikolay Aleksandrov return; 16715bf1e00bSNikolay Aleksandrov 1672085b53c8SNikolay Aleksandrov if (pg->key.addr.proto == htons(ETH_P_IP)) 16735bf1e00bSNikolay Aleksandrov other_query = &br->ip4_other_query; 16745bf1e00bSNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6) 16755bf1e00bSNikolay Aleksandrov else 16765bf1e00bSNikolay Aleksandrov other_query = &br->ip6_other_query; 16775bf1e00bSNikolay Aleksandrov #endif 16785bf1e00bSNikolay Aleksandrov 16795bf1e00bSNikolay Aleksandrov if (br_opt_get(br, BROPT_MULTICAST_QUERIER) && 16805bf1e00bSNikolay Aleksandrov other_query && !timer_pending(&other_query->timer)) { 16815bf1e00bSNikolay Aleksandrov lmi = now + br->multicast_last_member_interval; 16825bf1e00bSNikolay Aleksandrov pg->grp_query_rexmit_cnt = br->multicast_last_member_count - 1; 1683085b53c8SNikolay Aleksandrov __br_multicast_send_query(br, pg->key.port, pg, &pg->key.addr, 1684085b53c8SNikolay Aleksandrov &pg->key.addr, false, 0, NULL); 16855bf1e00bSNikolay Aleksandrov if (!timer_pending(&pg->rexmit_timer) || 16865bf1e00bSNikolay Aleksandrov time_after(pg->rexmit_timer.expires, lmi)) 16875bf1e00bSNikolay Aleksandrov mod_timer(&pg->rexmit_timer, lmi); 16885bf1e00bSNikolay Aleksandrov } 16895bf1e00bSNikolay Aleksandrov 16905bf1e00bSNikolay Aleksandrov if (pg->filter_mode == MCAST_EXCLUDE && 16915bf1e00bSNikolay Aleksandrov (!timer_pending(&pg->timer) || 16925bf1e00bSNikolay Aleksandrov time_after(pg->timer.expires, now + br_multicast_lmqt(br)))) 16935bf1e00bSNikolay Aleksandrov mod_timer(&pg->timer, now + br_multicast_lmqt(br)); 16945bf1e00bSNikolay Aleksandrov } 16955bf1e00bSNikolay Aleksandrov 16960436862eSNikolay Aleksandrov /* State Msg type New state Actions 16970436862eSNikolay Aleksandrov * INCLUDE (A) IS_IN (B) INCLUDE (A+B) (B)=GMI 16980436862eSNikolay Aleksandrov * INCLUDE (A) ALLOW (B) INCLUDE (A+B) (B)=GMI 16990436862eSNikolay Aleksandrov * EXCLUDE (X,Y) ALLOW (A) EXCLUDE (X+A,Y-A) (A)=GMI 17000436862eSNikolay Aleksandrov */ 17010436862eSNikolay Aleksandrov static bool br_multicast_isinc_allow(struct net_bridge_port_group *pg, 17020436862eSNikolay Aleksandrov void *srcs, u32 nsrcs, size_t src_size) 17030436862eSNikolay Aleksandrov { 1704085b53c8SNikolay Aleksandrov struct net_bridge *br = pg->key.port->br; 17050436862eSNikolay Aleksandrov struct net_bridge_group_src *ent; 17060436862eSNikolay Aleksandrov unsigned long now = jiffies; 17070436862eSNikolay Aleksandrov bool changed = false; 17080436862eSNikolay Aleksandrov struct br_ip src_ip; 17090436862eSNikolay Aleksandrov u32 src_idx; 17100436862eSNikolay Aleksandrov 17110436862eSNikolay Aleksandrov memset(&src_ip, 0, sizeof(src_ip)); 1712085b53c8SNikolay Aleksandrov src_ip.proto = pg->key.addr.proto; 17130436862eSNikolay Aleksandrov for (src_idx = 0; src_idx < nsrcs; src_idx++) { 1714deb96566SNikolay Aleksandrov memcpy(&src_ip.src, srcs, src_size); 17150436862eSNikolay Aleksandrov ent = br_multicast_find_group_src(pg, &src_ip); 17160436862eSNikolay Aleksandrov if (!ent) { 17170436862eSNikolay Aleksandrov ent = br_multicast_new_group_src(pg, &src_ip); 17180436862eSNikolay Aleksandrov if (ent) 17190436862eSNikolay Aleksandrov changed = true; 17200436862eSNikolay Aleksandrov } 17210436862eSNikolay Aleksandrov 17220436862eSNikolay Aleksandrov if (ent) 1723b0812368SNikolay Aleksandrov __grp_src_mod_timer(ent, now + br_multicast_gmi(br)); 17240436862eSNikolay Aleksandrov srcs += src_size; 17250436862eSNikolay Aleksandrov } 17260436862eSNikolay Aleksandrov 17270436862eSNikolay Aleksandrov return changed; 17280436862eSNikolay Aleksandrov } 17290436862eSNikolay Aleksandrov 1730e6231bcaSNikolay Aleksandrov /* State Msg type New state Actions 1731e6231bcaSNikolay Aleksandrov * INCLUDE (A) IS_EX (B) EXCLUDE (A*B,B-A) (B-A)=0 1732e6231bcaSNikolay Aleksandrov * Delete (A-B) 1733e6231bcaSNikolay Aleksandrov * Group Timer=GMI 1734e6231bcaSNikolay Aleksandrov */ 1735e6231bcaSNikolay Aleksandrov static void __grp_src_isexc_incl(struct net_bridge_port_group *pg, 1736e6231bcaSNikolay Aleksandrov void *srcs, u32 nsrcs, size_t src_size) 1737e6231bcaSNikolay Aleksandrov { 1738e6231bcaSNikolay Aleksandrov struct net_bridge_group_src *ent; 1739e6231bcaSNikolay Aleksandrov struct br_ip src_ip; 1740e6231bcaSNikolay Aleksandrov u32 src_idx; 1741e6231bcaSNikolay Aleksandrov 1742e6231bcaSNikolay Aleksandrov hlist_for_each_entry(ent, &pg->src_list, node) 1743e6231bcaSNikolay Aleksandrov ent->flags |= BR_SGRP_F_DELETE; 1744e6231bcaSNikolay Aleksandrov 1745e6231bcaSNikolay Aleksandrov memset(&src_ip, 0, sizeof(src_ip)); 1746085b53c8SNikolay Aleksandrov src_ip.proto = pg->key.addr.proto; 1747e6231bcaSNikolay Aleksandrov for (src_idx = 0; src_idx < nsrcs; src_idx++) { 1748deb96566SNikolay Aleksandrov memcpy(&src_ip.src, srcs, src_size); 1749e6231bcaSNikolay Aleksandrov ent = br_multicast_find_group_src(pg, &src_ip); 1750e6231bcaSNikolay Aleksandrov if (ent) 1751e6231bcaSNikolay Aleksandrov ent->flags &= ~BR_SGRP_F_DELETE; 1752e6231bcaSNikolay Aleksandrov else 1753b0812368SNikolay Aleksandrov ent = br_multicast_new_group_src(pg, &src_ip); 1754b0812368SNikolay Aleksandrov if (ent) 1755b0812368SNikolay Aleksandrov br_multicast_fwd_src_handle(ent); 1756e6231bcaSNikolay Aleksandrov srcs += src_size; 1757e6231bcaSNikolay Aleksandrov } 1758e6231bcaSNikolay Aleksandrov 1759e6231bcaSNikolay Aleksandrov __grp_src_delete_marked(pg); 1760e6231bcaSNikolay Aleksandrov } 1761e6231bcaSNikolay Aleksandrov 1762e6231bcaSNikolay Aleksandrov /* State Msg type New state Actions 1763e6231bcaSNikolay Aleksandrov * EXCLUDE (X,Y) IS_EX (A) EXCLUDE (A-Y,Y*A) (A-X-Y)=GMI 1764e6231bcaSNikolay Aleksandrov * Delete (X-A) 1765e6231bcaSNikolay Aleksandrov * Delete (Y-A) 1766e6231bcaSNikolay Aleksandrov * Group Timer=GMI 1767e6231bcaSNikolay Aleksandrov */ 1768e6231bcaSNikolay Aleksandrov static bool __grp_src_isexc_excl(struct net_bridge_port_group *pg, 1769e6231bcaSNikolay Aleksandrov void *srcs, u32 nsrcs, size_t src_size) 1770e6231bcaSNikolay Aleksandrov { 1771085b53c8SNikolay Aleksandrov struct net_bridge *br = pg->key.port->br; 1772e6231bcaSNikolay Aleksandrov struct net_bridge_group_src *ent; 1773e6231bcaSNikolay Aleksandrov unsigned long now = jiffies; 1774e6231bcaSNikolay Aleksandrov bool changed = false; 1775e6231bcaSNikolay Aleksandrov struct br_ip src_ip; 1776e6231bcaSNikolay Aleksandrov u32 src_idx; 1777e6231bcaSNikolay Aleksandrov 1778e6231bcaSNikolay Aleksandrov hlist_for_each_entry(ent, &pg->src_list, node) 1779e6231bcaSNikolay Aleksandrov ent->flags |= BR_SGRP_F_DELETE; 1780e6231bcaSNikolay Aleksandrov 1781e6231bcaSNikolay Aleksandrov memset(&src_ip, 0, sizeof(src_ip)); 1782085b53c8SNikolay Aleksandrov src_ip.proto = pg->key.addr.proto; 1783e6231bcaSNikolay Aleksandrov for (src_idx = 0; src_idx < nsrcs; src_idx++) { 1784deb96566SNikolay Aleksandrov memcpy(&src_ip.src, srcs, src_size); 1785e6231bcaSNikolay Aleksandrov ent = br_multicast_find_group_src(pg, &src_ip); 1786e6231bcaSNikolay Aleksandrov if (ent) { 1787e6231bcaSNikolay Aleksandrov ent->flags &= ~BR_SGRP_F_DELETE; 1788e6231bcaSNikolay Aleksandrov } else { 1789e6231bcaSNikolay Aleksandrov ent = br_multicast_new_group_src(pg, &src_ip); 1790e6231bcaSNikolay Aleksandrov if (ent) { 1791b0812368SNikolay Aleksandrov __grp_src_mod_timer(ent, 1792e6231bcaSNikolay Aleksandrov now + br_multicast_gmi(br)); 1793e6231bcaSNikolay Aleksandrov changed = true; 1794e6231bcaSNikolay Aleksandrov } 1795e6231bcaSNikolay Aleksandrov } 1796e6231bcaSNikolay Aleksandrov srcs += src_size; 1797e6231bcaSNikolay Aleksandrov } 1798e6231bcaSNikolay Aleksandrov 1799e6231bcaSNikolay Aleksandrov if (__grp_src_delete_marked(pg)) 1800e6231bcaSNikolay Aleksandrov changed = true; 1801e6231bcaSNikolay Aleksandrov 1802e6231bcaSNikolay Aleksandrov return changed; 1803e6231bcaSNikolay Aleksandrov } 1804e6231bcaSNikolay Aleksandrov 1805e6231bcaSNikolay Aleksandrov static bool br_multicast_isexc(struct net_bridge_port_group *pg, 1806e6231bcaSNikolay Aleksandrov void *srcs, u32 nsrcs, size_t src_size) 1807e6231bcaSNikolay Aleksandrov { 1808085b53c8SNikolay Aleksandrov struct net_bridge *br = pg->key.port->br; 1809e6231bcaSNikolay Aleksandrov bool changed = false; 1810e6231bcaSNikolay Aleksandrov 1811e6231bcaSNikolay Aleksandrov switch (pg->filter_mode) { 1812e6231bcaSNikolay Aleksandrov case MCAST_INCLUDE: 1813e6231bcaSNikolay Aleksandrov __grp_src_isexc_incl(pg, srcs, nsrcs, src_size); 1814*8266a049SNikolay Aleksandrov br_multicast_star_g_handle_mode(pg, MCAST_EXCLUDE); 1815e6231bcaSNikolay Aleksandrov changed = true; 1816e6231bcaSNikolay Aleksandrov break; 1817e6231bcaSNikolay Aleksandrov case MCAST_EXCLUDE: 1818e6231bcaSNikolay Aleksandrov changed = __grp_src_isexc_excl(pg, srcs, nsrcs, src_size); 1819e6231bcaSNikolay Aleksandrov break; 1820e6231bcaSNikolay Aleksandrov } 1821e6231bcaSNikolay Aleksandrov 1822e6231bcaSNikolay Aleksandrov pg->filter_mode = MCAST_EXCLUDE; 1823e6231bcaSNikolay Aleksandrov mod_timer(&pg->timer, jiffies + br_multicast_gmi(br)); 1824e6231bcaSNikolay Aleksandrov 1825e6231bcaSNikolay Aleksandrov return changed; 1826e6231bcaSNikolay Aleksandrov } 1827e6231bcaSNikolay Aleksandrov 18285bf1e00bSNikolay Aleksandrov /* State Msg type New state Actions 18295bf1e00bSNikolay Aleksandrov * INCLUDE (A) TO_IN (B) INCLUDE (A+B) (B)=GMI 18305bf1e00bSNikolay Aleksandrov * Send Q(G,A-B) 18315bf1e00bSNikolay Aleksandrov */ 18325bf1e00bSNikolay Aleksandrov static bool __grp_src_toin_incl(struct net_bridge_port_group *pg, 18335bf1e00bSNikolay Aleksandrov void *srcs, u32 nsrcs, size_t src_size) 18345bf1e00bSNikolay Aleksandrov { 1835085b53c8SNikolay Aleksandrov struct net_bridge *br = pg->key.port->br; 18365bf1e00bSNikolay Aleksandrov u32 src_idx, to_send = pg->src_ents; 18375bf1e00bSNikolay Aleksandrov struct net_bridge_group_src *ent; 18385bf1e00bSNikolay Aleksandrov unsigned long now = jiffies; 18395bf1e00bSNikolay Aleksandrov bool changed = false; 18405bf1e00bSNikolay Aleksandrov struct br_ip src_ip; 18415bf1e00bSNikolay Aleksandrov 18425bf1e00bSNikolay Aleksandrov hlist_for_each_entry(ent, &pg->src_list, node) 18435bf1e00bSNikolay Aleksandrov ent->flags |= BR_SGRP_F_SEND; 18445bf1e00bSNikolay Aleksandrov 18455bf1e00bSNikolay Aleksandrov memset(&src_ip, 0, sizeof(src_ip)); 1846085b53c8SNikolay Aleksandrov src_ip.proto = pg->key.addr.proto; 18475bf1e00bSNikolay Aleksandrov for (src_idx = 0; src_idx < nsrcs; src_idx++) { 1848deb96566SNikolay Aleksandrov memcpy(&src_ip.src, srcs, src_size); 18495bf1e00bSNikolay Aleksandrov ent = br_multicast_find_group_src(pg, &src_ip); 18505bf1e00bSNikolay Aleksandrov if (ent) { 18515bf1e00bSNikolay Aleksandrov ent->flags &= ~BR_SGRP_F_SEND; 18525bf1e00bSNikolay Aleksandrov to_send--; 18535bf1e00bSNikolay Aleksandrov } else { 18545bf1e00bSNikolay Aleksandrov ent = br_multicast_new_group_src(pg, &src_ip); 18555bf1e00bSNikolay Aleksandrov if (ent) 18565bf1e00bSNikolay Aleksandrov changed = true; 18575bf1e00bSNikolay Aleksandrov } 18585bf1e00bSNikolay Aleksandrov if (ent) 1859b0812368SNikolay Aleksandrov __grp_src_mod_timer(ent, now + br_multicast_gmi(br)); 18605bf1e00bSNikolay Aleksandrov srcs += src_size; 18615bf1e00bSNikolay Aleksandrov } 18625bf1e00bSNikolay Aleksandrov 18635bf1e00bSNikolay Aleksandrov if (to_send) 18645bf1e00bSNikolay Aleksandrov __grp_src_query_marked_and_rexmit(pg); 18655bf1e00bSNikolay Aleksandrov 18665bf1e00bSNikolay Aleksandrov return changed; 18675bf1e00bSNikolay Aleksandrov } 18685bf1e00bSNikolay Aleksandrov 18695bf1e00bSNikolay Aleksandrov /* State Msg type New state Actions 18705bf1e00bSNikolay Aleksandrov * EXCLUDE (X,Y) TO_IN (A) EXCLUDE (X+A,Y-A) (A)=GMI 18715bf1e00bSNikolay Aleksandrov * Send Q(G,X-A) 18725bf1e00bSNikolay Aleksandrov * Send Q(G) 18735bf1e00bSNikolay Aleksandrov */ 18745bf1e00bSNikolay Aleksandrov static bool __grp_src_toin_excl(struct net_bridge_port_group *pg, 18755bf1e00bSNikolay Aleksandrov void *srcs, u32 nsrcs, size_t src_size) 18765bf1e00bSNikolay Aleksandrov { 1877085b53c8SNikolay Aleksandrov struct net_bridge *br = pg->key.port->br; 18785bf1e00bSNikolay Aleksandrov u32 src_idx, to_send = pg->src_ents; 18795bf1e00bSNikolay Aleksandrov struct net_bridge_group_src *ent; 18805bf1e00bSNikolay Aleksandrov unsigned long now = jiffies; 18815bf1e00bSNikolay Aleksandrov bool changed = false; 18825bf1e00bSNikolay Aleksandrov struct br_ip src_ip; 18835bf1e00bSNikolay Aleksandrov 18845bf1e00bSNikolay Aleksandrov hlist_for_each_entry(ent, &pg->src_list, node) 18855bf1e00bSNikolay Aleksandrov if (timer_pending(&ent->timer)) 18865bf1e00bSNikolay Aleksandrov ent->flags |= BR_SGRP_F_SEND; 18875bf1e00bSNikolay Aleksandrov 18885bf1e00bSNikolay Aleksandrov memset(&src_ip, 0, sizeof(src_ip)); 1889085b53c8SNikolay Aleksandrov src_ip.proto = pg->key.addr.proto; 18905bf1e00bSNikolay Aleksandrov for (src_idx = 0; src_idx < nsrcs; src_idx++) { 1891deb96566SNikolay Aleksandrov memcpy(&src_ip.src, srcs, src_size); 18925bf1e00bSNikolay Aleksandrov ent = br_multicast_find_group_src(pg, &src_ip); 18935bf1e00bSNikolay Aleksandrov if (ent) { 18945bf1e00bSNikolay Aleksandrov if (timer_pending(&ent->timer)) { 18955bf1e00bSNikolay Aleksandrov ent->flags &= ~BR_SGRP_F_SEND; 18965bf1e00bSNikolay Aleksandrov to_send--; 18975bf1e00bSNikolay Aleksandrov } 18985bf1e00bSNikolay Aleksandrov } else { 18995bf1e00bSNikolay Aleksandrov ent = br_multicast_new_group_src(pg, &src_ip); 19005bf1e00bSNikolay Aleksandrov if (ent) 19015bf1e00bSNikolay Aleksandrov changed = true; 19025bf1e00bSNikolay Aleksandrov } 19035bf1e00bSNikolay Aleksandrov if (ent) 1904b0812368SNikolay Aleksandrov __grp_src_mod_timer(ent, now + br_multicast_gmi(br)); 19055bf1e00bSNikolay Aleksandrov srcs += src_size; 19065bf1e00bSNikolay Aleksandrov } 19075bf1e00bSNikolay Aleksandrov 19085bf1e00bSNikolay Aleksandrov if (to_send) 19095bf1e00bSNikolay Aleksandrov __grp_src_query_marked_and_rexmit(pg); 19105bf1e00bSNikolay Aleksandrov 19115bf1e00bSNikolay Aleksandrov __grp_send_query_and_rexmit(pg); 19125bf1e00bSNikolay Aleksandrov 19135bf1e00bSNikolay Aleksandrov return changed; 19145bf1e00bSNikolay Aleksandrov } 19155bf1e00bSNikolay Aleksandrov 19165bf1e00bSNikolay Aleksandrov static bool br_multicast_toin(struct net_bridge_port_group *pg, 19175bf1e00bSNikolay Aleksandrov void *srcs, u32 nsrcs, size_t src_size) 19185bf1e00bSNikolay Aleksandrov { 19195bf1e00bSNikolay Aleksandrov bool changed = false; 19205bf1e00bSNikolay Aleksandrov 19215bf1e00bSNikolay Aleksandrov switch (pg->filter_mode) { 19225bf1e00bSNikolay Aleksandrov case MCAST_INCLUDE: 19235bf1e00bSNikolay Aleksandrov changed = __grp_src_toin_incl(pg, srcs, nsrcs, src_size); 19245bf1e00bSNikolay Aleksandrov break; 19255bf1e00bSNikolay Aleksandrov case MCAST_EXCLUDE: 19265bf1e00bSNikolay Aleksandrov changed = __grp_src_toin_excl(pg, srcs, nsrcs, src_size); 19275bf1e00bSNikolay Aleksandrov break; 19285bf1e00bSNikolay Aleksandrov } 19295bf1e00bSNikolay Aleksandrov 19305bf1e00bSNikolay Aleksandrov return changed; 19315bf1e00bSNikolay Aleksandrov } 19325bf1e00bSNikolay Aleksandrov 19335bf1e00bSNikolay Aleksandrov /* State Msg type New state Actions 19345bf1e00bSNikolay Aleksandrov * INCLUDE (A) TO_EX (B) EXCLUDE (A*B,B-A) (B-A)=0 19355bf1e00bSNikolay Aleksandrov * Delete (A-B) 19365bf1e00bSNikolay Aleksandrov * Send Q(G,A*B) 19375bf1e00bSNikolay Aleksandrov * Group Timer=GMI 19385bf1e00bSNikolay Aleksandrov */ 19395bf1e00bSNikolay Aleksandrov static void __grp_src_toex_incl(struct net_bridge_port_group *pg, 19405bf1e00bSNikolay Aleksandrov void *srcs, u32 nsrcs, size_t src_size) 19415bf1e00bSNikolay Aleksandrov { 19425bf1e00bSNikolay Aleksandrov struct net_bridge_group_src *ent; 19435bf1e00bSNikolay Aleksandrov u32 src_idx, to_send = 0; 19445bf1e00bSNikolay Aleksandrov struct br_ip src_ip; 19455bf1e00bSNikolay Aleksandrov 19465bf1e00bSNikolay Aleksandrov hlist_for_each_entry(ent, &pg->src_list, node) 19475bf1e00bSNikolay Aleksandrov ent->flags = (ent->flags & ~BR_SGRP_F_SEND) | BR_SGRP_F_DELETE; 19485bf1e00bSNikolay Aleksandrov 19495bf1e00bSNikolay Aleksandrov memset(&src_ip, 0, sizeof(src_ip)); 1950085b53c8SNikolay Aleksandrov src_ip.proto = pg->key.addr.proto; 19515bf1e00bSNikolay Aleksandrov for (src_idx = 0; src_idx < nsrcs; src_idx++) { 1952deb96566SNikolay Aleksandrov memcpy(&src_ip.src, srcs, src_size); 19535bf1e00bSNikolay Aleksandrov ent = br_multicast_find_group_src(pg, &src_ip); 19545bf1e00bSNikolay Aleksandrov if (ent) { 19555bf1e00bSNikolay Aleksandrov ent->flags = (ent->flags & ~BR_SGRP_F_DELETE) | 19565bf1e00bSNikolay Aleksandrov BR_SGRP_F_SEND; 19575bf1e00bSNikolay Aleksandrov to_send++; 19585bf1e00bSNikolay Aleksandrov } else { 1959b0812368SNikolay Aleksandrov ent = br_multicast_new_group_src(pg, &src_ip); 19605bf1e00bSNikolay Aleksandrov } 1961b0812368SNikolay Aleksandrov if (ent) 1962b0812368SNikolay Aleksandrov br_multicast_fwd_src_handle(ent); 19635bf1e00bSNikolay Aleksandrov srcs += src_size; 19645bf1e00bSNikolay Aleksandrov } 19655bf1e00bSNikolay Aleksandrov 19665bf1e00bSNikolay Aleksandrov __grp_src_delete_marked(pg); 19675bf1e00bSNikolay Aleksandrov if (to_send) 19685bf1e00bSNikolay Aleksandrov __grp_src_query_marked_and_rexmit(pg); 19695bf1e00bSNikolay Aleksandrov } 19705bf1e00bSNikolay Aleksandrov 19715bf1e00bSNikolay Aleksandrov /* State Msg type New state Actions 19725bf1e00bSNikolay Aleksandrov * EXCLUDE (X,Y) TO_EX (A) EXCLUDE (A-Y,Y*A) (A-X-Y)=Group Timer 19735bf1e00bSNikolay Aleksandrov * Delete (X-A) 19745bf1e00bSNikolay Aleksandrov * Delete (Y-A) 19755bf1e00bSNikolay Aleksandrov * Send Q(G,A-Y) 19765bf1e00bSNikolay Aleksandrov * Group Timer=GMI 19775bf1e00bSNikolay Aleksandrov */ 19785bf1e00bSNikolay Aleksandrov static bool __grp_src_toex_excl(struct net_bridge_port_group *pg, 19795bf1e00bSNikolay Aleksandrov void *srcs, u32 nsrcs, size_t src_size) 19805bf1e00bSNikolay Aleksandrov { 19815bf1e00bSNikolay Aleksandrov struct net_bridge_group_src *ent; 19825bf1e00bSNikolay Aleksandrov u32 src_idx, to_send = 0; 19835bf1e00bSNikolay Aleksandrov bool changed = false; 19845bf1e00bSNikolay Aleksandrov struct br_ip src_ip; 19855bf1e00bSNikolay Aleksandrov 19865bf1e00bSNikolay Aleksandrov hlist_for_each_entry(ent, &pg->src_list, node) 19875bf1e00bSNikolay Aleksandrov ent->flags = (ent->flags & ~BR_SGRP_F_SEND) | BR_SGRP_F_DELETE; 19885bf1e00bSNikolay Aleksandrov 19895bf1e00bSNikolay Aleksandrov memset(&src_ip, 0, sizeof(src_ip)); 1990085b53c8SNikolay Aleksandrov src_ip.proto = pg->key.addr.proto; 19915bf1e00bSNikolay Aleksandrov for (src_idx = 0; src_idx < nsrcs; src_idx++) { 1992deb96566SNikolay Aleksandrov memcpy(&src_ip.src, srcs, src_size); 19935bf1e00bSNikolay Aleksandrov ent = br_multicast_find_group_src(pg, &src_ip); 19945bf1e00bSNikolay Aleksandrov if (ent) { 19955bf1e00bSNikolay Aleksandrov ent->flags &= ~BR_SGRP_F_DELETE; 19965bf1e00bSNikolay Aleksandrov } else { 19975bf1e00bSNikolay Aleksandrov ent = br_multicast_new_group_src(pg, &src_ip); 19985bf1e00bSNikolay Aleksandrov if (ent) { 1999b0812368SNikolay Aleksandrov __grp_src_mod_timer(ent, pg->timer.expires); 20005bf1e00bSNikolay Aleksandrov changed = true; 20015bf1e00bSNikolay Aleksandrov } 20025bf1e00bSNikolay Aleksandrov } 20035bf1e00bSNikolay Aleksandrov if (ent && timer_pending(&ent->timer)) { 20045bf1e00bSNikolay Aleksandrov ent->flags |= BR_SGRP_F_SEND; 20055bf1e00bSNikolay Aleksandrov to_send++; 20065bf1e00bSNikolay Aleksandrov } 20075bf1e00bSNikolay Aleksandrov srcs += src_size; 20085bf1e00bSNikolay Aleksandrov } 20095bf1e00bSNikolay Aleksandrov 20105bf1e00bSNikolay Aleksandrov if (__grp_src_delete_marked(pg)) 20115bf1e00bSNikolay Aleksandrov changed = true; 20125bf1e00bSNikolay Aleksandrov if (to_send) 20135bf1e00bSNikolay Aleksandrov __grp_src_query_marked_and_rexmit(pg); 20145bf1e00bSNikolay Aleksandrov 20155bf1e00bSNikolay Aleksandrov return changed; 20165bf1e00bSNikolay Aleksandrov } 20175bf1e00bSNikolay Aleksandrov 20185bf1e00bSNikolay Aleksandrov static bool br_multicast_toex(struct net_bridge_port_group *pg, 20195bf1e00bSNikolay Aleksandrov void *srcs, u32 nsrcs, size_t src_size) 20205bf1e00bSNikolay Aleksandrov { 2021085b53c8SNikolay Aleksandrov struct net_bridge *br = pg->key.port->br; 20225bf1e00bSNikolay Aleksandrov bool changed = false; 20235bf1e00bSNikolay Aleksandrov 20245bf1e00bSNikolay Aleksandrov switch (pg->filter_mode) { 20255bf1e00bSNikolay Aleksandrov case MCAST_INCLUDE: 20265bf1e00bSNikolay Aleksandrov __grp_src_toex_incl(pg, srcs, nsrcs, src_size); 2027*8266a049SNikolay Aleksandrov br_multicast_star_g_handle_mode(pg, MCAST_EXCLUDE); 20285bf1e00bSNikolay Aleksandrov changed = true; 20295bf1e00bSNikolay Aleksandrov break; 20305bf1e00bSNikolay Aleksandrov case MCAST_EXCLUDE: 2031d5bf31ddSNikolay Aleksandrov changed = __grp_src_toex_excl(pg, srcs, nsrcs, src_size); 20325bf1e00bSNikolay Aleksandrov break; 20335bf1e00bSNikolay Aleksandrov } 20345bf1e00bSNikolay Aleksandrov 20355bf1e00bSNikolay Aleksandrov pg->filter_mode = MCAST_EXCLUDE; 20365bf1e00bSNikolay Aleksandrov mod_timer(&pg->timer, jiffies + br_multicast_gmi(br)); 20375bf1e00bSNikolay Aleksandrov 20385bf1e00bSNikolay Aleksandrov return changed; 20395bf1e00bSNikolay Aleksandrov } 20405bf1e00bSNikolay Aleksandrov 2041109865feSNikolay Aleksandrov /* State Msg type New state Actions 2042109865feSNikolay Aleksandrov * INCLUDE (A) BLOCK (B) INCLUDE (A) Send Q(G,A*B) 2043109865feSNikolay Aleksandrov */ 2044109865feSNikolay Aleksandrov static void __grp_src_block_incl(struct net_bridge_port_group *pg, 2045109865feSNikolay Aleksandrov void *srcs, u32 nsrcs, size_t src_size) 2046109865feSNikolay Aleksandrov { 2047109865feSNikolay Aleksandrov struct net_bridge_group_src *ent; 2048109865feSNikolay Aleksandrov u32 src_idx, to_send = 0; 2049109865feSNikolay Aleksandrov struct br_ip src_ip; 2050109865feSNikolay Aleksandrov 2051109865feSNikolay Aleksandrov hlist_for_each_entry(ent, &pg->src_list, node) 2052109865feSNikolay Aleksandrov ent->flags &= ~BR_SGRP_F_SEND; 2053109865feSNikolay Aleksandrov 2054109865feSNikolay Aleksandrov memset(&src_ip, 0, sizeof(src_ip)); 2055085b53c8SNikolay Aleksandrov src_ip.proto = pg->key.addr.proto; 2056109865feSNikolay Aleksandrov for (src_idx = 0; src_idx < nsrcs; src_idx++) { 2057deb96566SNikolay Aleksandrov memcpy(&src_ip.src, srcs, src_size); 2058109865feSNikolay Aleksandrov ent = br_multicast_find_group_src(pg, &src_ip); 2059109865feSNikolay Aleksandrov if (ent) { 2060109865feSNikolay Aleksandrov ent->flags |= BR_SGRP_F_SEND; 2061109865feSNikolay Aleksandrov to_send++; 2062109865feSNikolay Aleksandrov } 2063109865feSNikolay Aleksandrov srcs += src_size; 2064109865feSNikolay Aleksandrov } 2065109865feSNikolay Aleksandrov 2066109865feSNikolay Aleksandrov if (to_send) 2067109865feSNikolay Aleksandrov __grp_src_query_marked_and_rexmit(pg); 2068109865feSNikolay Aleksandrov 2069109865feSNikolay Aleksandrov if (pg->filter_mode == MCAST_INCLUDE && hlist_empty(&pg->src_list)) 2070085b53c8SNikolay Aleksandrov br_multicast_find_del_pg(pg->key.port->br, pg); 2071109865feSNikolay Aleksandrov } 2072109865feSNikolay Aleksandrov 2073109865feSNikolay Aleksandrov /* State Msg type New state Actions 2074109865feSNikolay Aleksandrov * EXCLUDE (X,Y) BLOCK (A) EXCLUDE (X+(A-Y),Y) (A-X-Y)=Group Timer 2075109865feSNikolay Aleksandrov * Send Q(G,A-Y) 2076109865feSNikolay Aleksandrov */ 2077109865feSNikolay Aleksandrov static bool __grp_src_block_excl(struct net_bridge_port_group *pg, 2078109865feSNikolay Aleksandrov void *srcs, u32 nsrcs, size_t src_size) 2079109865feSNikolay Aleksandrov { 2080109865feSNikolay Aleksandrov struct net_bridge_group_src *ent; 2081109865feSNikolay Aleksandrov u32 src_idx, to_send = 0; 2082109865feSNikolay Aleksandrov bool changed = false; 2083109865feSNikolay Aleksandrov struct br_ip src_ip; 2084109865feSNikolay Aleksandrov 2085109865feSNikolay Aleksandrov hlist_for_each_entry(ent, &pg->src_list, node) 2086109865feSNikolay Aleksandrov ent->flags &= ~BR_SGRP_F_SEND; 2087109865feSNikolay Aleksandrov 2088109865feSNikolay Aleksandrov memset(&src_ip, 0, sizeof(src_ip)); 2089085b53c8SNikolay Aleksandrov src_ip.proto = pg->key.addr.proto; 2090109865feSNikolay Aleksandrov for (src_idx = 0; src_idx < nsrcs; src_idx++) { 2091deb96566SNikolay Aleksandrov memcpy(&src_ip.src, srcs, src_size); 2092109865feSNikolay Aleksandrov ent = br_multicast_find_group_src(pg, &src_ip); 2093109865feSNikolay Aleksandrov if (!ent) { 2094109865feSNikolay Aleksandrov ent = br_multicast_new_group_src(pg, &src_ip); 2095109865feSNikolay Aleksandrov if (ent) { 2096b0812368SNikolay Aleksandrov __grp_src_mod_timer(ent, pg->timer.expires); 2097109865feSNikolay Aleksandrov changed = true; 2098109865feSNikolay Aleksandrov } 2099109865feSNikolay Aleksandrov } 2100109865feSNikolay Aleksandrov if (ent && timer_pending(&ent->timer)) { 2101109865feSNikolay Aleksandrov ent->flags |= BR_SGRP_F_SEND; 2102109865feSNikolay Aleksandrov to_send++; 2103109865feSNikolay Aleksandrov } 2104109865feSNikolay Aleksandrov srcs += src_size; 2105109865feSNikolay Aleksandrov } 2106109865feSNikolay Aleksandrov 2107109865feSNikolay Aleksandrov if (to_send) 2108109865feSNikolay Aleksandrov __grp_src_query_marked_and_rexmit(pg); 2109109865feSNikolay Aleksandrov 2110109865feSNikolay Aleksandrov return changed; 2111109865feSNikolay Aleksandrov } 2112109865feSNikolay Aleksandrov 2113109865feSNikolay Aleksandrov static bool br_multicast_block(struct net_bridge_port_group *pg, 2114109865feSNikolay Aleksandrov void *srcs, u32 nsrcs, size_t src_size) 2115109865feSNikolay Aleksandrov { 2116109865feSNikolay Aleksandrov bool changed = false; 2117109865feSNikolay Aleksandrov 2118109865feSNikolay Aleksandrov switch (pg->filter_mode) { 2119109865feSNikolay Aleksandrov case MCAST_INCLUDE: 2120109865feSNikolay Aleksandrov __grp_src_block_incl(pg, srcs, nsrcs, src_size); 2121109865feSNikolay Aleksandrov break; 2122109865feSNikolay Aleksandrov case MCAST_EXCLUDE: 2123109865feSNikolay Aleksandrov changed = __grp_src_block_excl(pg, srcs, nsrcs, src_size); 2124109865feSNikolay Aleksandrov break; 2125109865feSNikolay Aleksandrov } 2126109865feSNikolay Aleksandrov 2127109865feSNikolay Aleksandrov return changed; 2128109865feSNikolay Aleksandrov } 2129109865feSNikolay Aleksandrov 21300436862eSNikolay Aleksandrov static struct net_bridge_port_group * 21310436862eSNikolay Aleksandrov br_multicast_find_port(struct net_bridge_mdb_entry *mp, 21320436862eSNikolay Aleksandrov struct net_bridge_port *p, 21330436862eSNikolay Aleksandrov const unsigned char *src) 21340436862eSNikolay Aleksandrov { 2135071445c6SNikolay Aleksandrov struct net_bridge *br __maybe_unused = mp->br; 21360436862eSNikolay Aleksandrov struct net_bridge_port_group *pg; 21370436862eSNikolay Aleksandrov 21380436862eSNikolay Aleksandrov for (pg = mlock_dereference(mp->ports, br); 21390436862eSNikolay Aleksandrov pg; 21400436862eSNikolay Aleksandrov pg = mlock_dereference(pg->next, br)) 21410436862eSNikolay Aleksandrov if (br_port_group_equal(pg, p, src)) 21420436862eSNikolay Aleksandrov return pg; 21430436862eSNikolay Aleksandrov 21440436862eSNikolay Aleksandrov return NULL; 21450436862eSNikolay Aleksandrov } 21460436862eSNikolay Aleksandrov 21478ef2a9a5SYOSHIFUJI Hideaki static int br_ip4_multicast_igmp3_report(struct net_bridge *br, 2148eb1d1641SHerbert Xu struct net_bridge_port *port, 214906499098SVlad Yasevich struct sk_buff *skb, 215006499098SVlad Yasevich u16 vid) 2151eb1d1641SHerbert Xu { 21520436862eSNikolay Aleksandrov bool igmpv2 = br->multicast_igmp_version == 2; 21530436862eSNikolay Aleksandrov struct net_bridge_mdb_entry *mdst; 21540436862eSNikolay Aleksandrov struct net_bridge_port_group *pg; 21556db6f0eaSFelix Fietkau const unsigned char *src; 2156eb1d1641SHerbert Xu struct igmpv3_report *ih; 2157eb1d1641SHerbert Xu struct igmpv3_grec *grec; 21580436862eSNikolay Aleksandrov int i, len, num, type; 21590436862eSNikolay Aleksandrov bool changed = false; 2160eb1d1641SHerbert Xu __be32 group; 21610436862eSNikolay Aleksandrov int err = 0; 2162e57f6185SNikolay Aleksandrov u16 nsrcs; 2163eb1d1641SHerbert Xu 2164eb1d1641SHerbert Xu ih = igmpv3_report_hdr(skb); 2165eb1d1641SHerbert Xu num = ntohs(ih->ngrec); 2166c2d4fbd2SLinus Lüssing len = skb_transport_offset(skb) + sizeof(*ih); 2167eb1d1641SHerbert Xu 2168eb1d1641SHerbert Xu for (i = 0; i < num; i++) { 2169eb1d1641SHerbert Xu len += sizeof(*grec); 2170ba5ea614SLinus Lüssing if (!ip_mc_may_pull(skb, len)) 2171eb1d1641SHerbert Xu return -EINVAL; 2172eb1d1641SHerbert Xu 2173fd218cf9SHerbert Xu grec = (void *)(skb->data + len - sizeof(*grec)); 2174eb1d1641SHerbert Xu group = grec->grec_mca; 2175eb1d1641SHerbert Xu type = grec->grec_type; 2176e57f6185SNikolay Aleksandrov nsrcs = ntohs(grec->grec_nsrcs); 2177eb1d1641SHerbert Xu 2178e57f6185SNikolay Aleksandrov len += nsrcs * 4; 2179ba5ea614SLinus Lüssing if (!ip_mc_may_pull(skb, len)) 2180eb1d1641SHerbert Xu return -EINVAL; 2181eb1d1641SHerbert Xu 2182eb1d1641SHerbert Xu switch (type) { 2183eb1d1641SHerbert Xu case IGMPV3_MODE_IS_INCLUDE: 2184eb1d1641SHerbert Xu case IGMPV3_MODE_IS_EXCLUDE: 2185eb1d1641SHerbert Xu case IGMPV3_CHANGE_TO_INCLUDE: 2186eb1d1641SHerbert Xu case IGMPV3_CHANGE_TO_EXCLUDE: 2187eb1d1641SHerbert Xu case IGMPV3_ALLOW_NEW_SOURCES: 2188eb1d1641SHerbert Xu case IGMPV3_BLOCK_OLD_SOURCES: 2189eb1d1641SHerbert Xu break; 2190eb1d1641SHerbert Xu 2191eb1d1641SHerbert Xu default: 2192eb1d1641SHerbert Xu continue; 2193eb1d1641SHerbert Xu } 2194eb1d1641SHerbert Xu 21956db6f0eaSFelix Fietkau src = eth_hdr(skb)->h_source; 21960436862eSNikolay Aleksandrov if (nsrcs == 0 && 21970436862eSNikolay Aleksandrov (type == IGMPV3_CHANGE_TO_INCLUDE || 21980436862eSNikolay Aleksandrov type == IGMPV3_MODE_IS_INCLUDE)) { 21990436862eSNikolay Aleksandrov if (!port || igmpv2) { 22006db6f0eaSFelix Fietkau br_ip4_multicast_leave_group(br, port, group, vid, src); 22010436862eSNikolay Aleksandrov continue; 22020436862eSNikolay Aleksandrov } 2203bc8c20acSSatish Ashok } else { 22046db6f0eaSFelix Fietkau err = br_ip4_multicast_add_group(br, port, group, vid, 22050436862eSNikolay Aleksandrov src, igmpv2); 2206eb1d1641SHerbert Xu if (err) 2207eb1d1641SHerbert Xu break; 2208eb1d1641SHerbert Xu } 22090436862eSNikolay Aleksandrov 22100436862eSNikolay Aleksandrov if (!port || igmpv2) 22110436862eSNikolay Aleksandrov continue; 22120436862eSNikolay Aleksandrov 22130436862eSNikolay Aleksandrov spin_lock_bh(&br->multicast_lock); 22140436862eSNikolay Aleksandrov mdst = br_mdb_ip4_get(br, group, vid); 22150436862eSNikolay Aleksandrov if (!mdst) 22160436862eSNikolay Aleksandrov goto unlock_continue; 22170436862eSNikolay Aleksandrov pg = br_multicast_find_port(mdst, port, src); 22180436862eSNikolay Aleksandrov if (!pg || (pg->flags & MDB_PG_FLAGS_PERMANENT)) 22190436862eSNikolay Aleksandrov goto unlock_continue; 22200436862eSNikolay Aleksandrov /* reload grec */ 22210436862eSNikolay Aleksandrov grec = (void *)(skb->data + len - sizeof(*grec) - (nsrcs * 4)); 22220436862eSNikolay Aleksandrov switch (type) { 22230436862eSNikolay Aleksandrov case IGMPV3_ALLOW_NEW_SOURCES: 22240436862eSNikolay Aleksandrov changed = br_multicast_isinc_allow(pg, grec->grec_src, 22250436862eSNikolay Aleksandrov nsrcs, sizeof(__be32)); 22260436862eSNikolay Aleksandrov break; 2227e6231bcaSNikolay Aleksandrov case IGMPV3_MODE_IS_INCLUDE: 2228e6231bcaSNikolay Aleksandrov changed = br_multicast_isinc_allow(pg, grec->grec_src, nsrcs, 2229e6231bcaSNikolay Aleksandrov sizeof(__be32)); 2230e6231bcaSNikolay Aleksandrov break; 2231e6231bcaSNikolay Aleksandrov case IGMPV3_MODE_IS_EXCLUDE: 2232e6231bcaSNikolay Aleksandrov changed = br_multicast_isexc(pg, grec->grec_src, nsrcs, 2233e6231bcaSNikolay Aleksandrov sizeof(__be32)); 2234e6231bcaSNikolay Aleksandrov break; 22355bf1e00bSNikolay Aleksandrov case IGMPV3_CHANGE_TO_INCLUDE: 22365bf1e00bSNikolay Aleksandrov changed = br_multicast_toin(pg, grec->grec_src, nsrcs, 22375bf1e00bSNikolay Aleksandrov sizeof(__be32)); 22385bf1e00bSNikolay Aleksandrov break; 22395bf1e00bSNikolay Aleksandrov case IGMPV3_CHANGE_TO_EXCLUDE: 22405bf1e00bSNikolay Aleksandrov changed = br_multicast_toex(pg, grec->grec_src, nsrcs, 22415bf1e00bSNikolay Aleksandrov sizeof(__be32)); 22425bf1e00bSNikolay Aleksandrov break; 2243109865feSNikolay Aleksandrov case IGMPV3_BLOCK_OLD_SOURCES: 2244109865feSNikolay Aleksandrov changed = br_multicast_block(pg, grec->grec_src, nsrcs, 2245109865feSNikolay Aleksandrov sizeof(__be32)); 2246109865feSNikolay Aleksandrov break; 22470436862eSNikolay Aleksandrov } 22480436862eSNikolay Aleksandrov if (changed) 22490436862eSNikolay Aleksandrov br_mdb_notify(br->dev, mdst, pg, RTM_NEWMDB); 22500436862eSNikolay Aleksandrov unlock_continue: 22510436862eSNikolay Aleksandrov spin_unlock_bh(&br->multicast_lock); 2252bc8c20acSSatish Ashok } 2253eb1d1641SHerbert Xu 2254eb1d1641SHerbert Xu return err; 2255eb1d1641SHerbert Xu } 2256eb1d1641SHerbert Xu 2257dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 225808b202b6SYOSHIFUJI Hideaki static int br_ip6_multicast_mld2_report(struct net_bridge *br, 225908b202b6SYOSHIFUJI Hideaki struct net_bridge_port *port, 226006499098SVlad Yasevich struct sk_buff *skb, 226106499098SVlad Yasevich u16 vid) 226208b202b6SYOSHIFUJI Hideaki { 22630436862eSNikolay Aleksandrov bool mldv1 = br->multicast_mld_version == 1; 22640436862eSNikolay Aleksandrov struct net_bridge_mdb_entry *mdst; 22650436862eSNikolay Aleksandrov struct net_bridge_port_group *pg; 2266ba5ea614SLinus Lüssing unsigned int nsrcs_offset; 22676db6f0eaSFelix Fietkau const unsigned char *src; 226808b202b6SYOSHIFUJI Hideaki struct icmp6hdr *icmp6h; 226908b202b6SYOSHIFUJI Hideaki struct mld2_grec *grec; 2270ba5ea614SLinus Lüssing unsigned int grec_len; 22710436862eSNikolay Aleksandrov bool changed = false; 22720436862eSNikolay Aleksandrov int i, len, num; 227308b202b6SYOSHIFUJI Hideaki int err = 0; 227408b202b6SYOSHIFUJI Hideaki 2275ba5ea614SLinus Lüssing if (!ipv6_mc_may_pull(skb, sizeof(*icmp6h))) 227608b202b6SYOSHIFUJI Hideaki return -EINVAL; 227708b202b6SYOSHIFUJI Hideaki 227808b202b6SYOSHIFUJI Hideaki icmp6h = icmp6_hdr(skb); 227908b202b6SYOSHIFUJI Hideaki num = ntohs(icmp6h->icmp6_dataun.un_data16[1]); 2280c2d4fbd2SLinus Lüssing len = skb_transport_offset(skb) + sizeof(*icmp6h); 228108b202b6SYOSHIFUJI Hideaki 228208b202b6SYOSHIFUJI Hideaki for (i = 0; i < num; i++) { 2283e57f6185SNikolay Aleksandrov __be16 *_nsrcs, __nsrcs; 2284e57f6185SNikolay Aleksandrov u16 nsrcs; 228508b202b6SYOSHIFUJI Hideaki 2286ba5ea614SLinus Lüssing nsrcs_offset = len + offsetof(struct mld2_grec, grec_nsrcs); 2287ba5ea614SLinus Lüssing 2288ba5ea614SLinus Lüssing if (skb_transport_offset(skb) + ipv6_transport_len(skb) < 22895fc6266aSLinus Lüssing nsrcs_offset + sizeof(__nsrcs)) 2290ba5ea614SLinus Lüssing return -EINVAL; 2291ba5ea614SLinus Lüssing 2292e57f6185SNikolay Aleksandrov _nsrcs = skb_header_pointer(skb, nsrcs_offset, 2293e57f6185SNikolay Aleksandrov sizeof(__nsrcs), &__nsrcs); 2294e57f6185SNikolay Aleksandrov if (!_nsrcs) 229508b202b6SYOSHIFUJI Hideaki return -EINVAL; 229608b202b6SYOSHIFUJI Hideaki 2297e57f6185SNikolay Aleksandrov nsrcs = ntohs(*_nsrcs); 2298e57f6185SNikolay Aleksandrov grec_len = struct_size(grec, grec_src, nsrcs); 2299ba5ea614SLinus Lüssing 2300ba5ea614SLinus Lüssing if (!ipv6_mc_may_pull(skb, len + grec_len)) 230108b202b6SYOSHIFUJI Hideaki return -EINVAL; 230208b202b6SYOSHIFUJI Hideaki 230308b202b6SYOSHIFUJI Hideaki grec = (struct mld2_grec *)(skb->data + len); 2304ba5ea614SLinus Lüssing len += grec_len; 230508b202b6SYOSHIFUJI Hideaki 230608b202b6SYOSHIFUJI Hideaki switch (grec->grec_type) { 230708b202b6SYOSHIFUJI Hideaki case MLD2_MODE_IS_INCLUDE: 230808b202b6SYOSHIFUJI Hideaki case MLD2_MODE_IS_EXCLUDE: 230908b202b6SYOSHIFUJI Hideaki case MLD2_CHANGE_TO_INCLUDE: 231008b202b6SYOSHIFUJI Hideaki case MLD2_CHANGE_TO_EXCLUDE: 231108b202b6SYOSHIFUJI Hideaki case MLD2_ALLOW_NEW_SOURCES: 231208b202b6SYOSHIFUJI Hideaki case MLD2_BLOCK_OLD_SOURCES: 231308b202b6SYOSHIFUJI Hideaki break; 231408b202b6SYOSHIFUJI Hideaki 231508b202b6SYOSHIFUJI Hideaki default: 231608b202b6SYOSHIFUJI Hideaki continue; 231708b202b6SYOSHIFUJI Hideaki } 231808b202b6SYOSHIFUJI Hideaki 23196db6f0eaSFelix Fietkau src = eth_hdr(skb)->h_source; 2320bc8c20acSSatish Ashok if ((grec->grec_type == MLD2_CHANGE_TO_INCLUDE || 2321bc8c20acSSatish Ashok grec->grec_type == MLD2_MODE_IS_INCLUDE) && 2322e57f6185SNikolay Aleksandrov nsrcs == 0) { 23230436862eSNikolay Aleksandrov if (!port || mldv1) { 23240436862eSNikolay Aleksandrov br_ip6_multicast_leave_group(br, port, 23250436862eSNikolay Aleksandrov &grec->grec_mca, 23266db6f0eaSFelix Fietkau vid, src); 23270436862eSNikolay Aleksandrov continue; 23280436862eSNikolay Aleksandrov } 2329bc8c20acSSatish Ashok } else { 2330bc8c20acSSatish Ashok err = br_ip6_multicast_add_group(br, port, 23316db6f0eaSFelix Fietkau &grec->grec_mca, vid, 23320436862eSNikolay Aleksandrov src, mldv1); 23339264251eSDavide Caratti if (err) 233408b202b6SYOSHIFUJI Hideaki break; 233508b202b6SYOSHIFUJI Hideaki } 23360436862eSNikolay Aleksandrov 23370436862eSNikolay Aleksandrov if (!port || mldv1) 23380436862eSNikolay Aleksandrov continue; 23390436862eSNikolay Aleksandrov 23400436862eSNikolay Aleksandrov spin_lock_bh(&br->multicast_lock); 23410436862eSNikolay Aleksandrov mdst = br_mdb_ip6_get(br, &grec->grec_mca, vid); 23420436862eSNikolay Aleksandrov if (!mdst) 23430436862eSNikolay Aleksandrov goto unlock_continue; 23440436862eSNikolay Aleksandrov pg = br_multicast_find_port(mdst, port, src); 23450436862eSNikolay Aleksandrov if (!pg || (pg->flags & MDB_PG_FLAGS_PERMANENT)) 23460436862eSNikolay Aleksandrov goto unlock_continue; 23470436862eSNikolay Aleksandrov switch (grec->grec_type) { 23480436862eSNikolay Aleksandrov case MLD2_ALLOW_NEW_SOURCES: 23490436862eSNikolay Aleksandrov changed = br_multicast_isinc_allow(pg, grec->grec_src, 23500436862eSNikolay Aleksandrov nsrcs, 23510436862eSNikolay Aleksandrov sizeof(struct in6_addr)); 23520436862eSNikolay Aleksandrov break; 2353e6231bcaSNikolay Aleksandrov case MLD2_MODE_IS_INCLUDE: 2354e6231bcaSNikolay Aleksandrov changed = br_multicast_isinc_allow(pg, grec->grec_src, nsrcs, 2355e6231bcaSNikolay Aleksandrov sizeof(struct in6_addr)); 2356e6231bcaSNikolay Aleksandrov break; 2357e6231bcaSNikolay Aleksandrov case MLD2_MODE_IS_EXCLUDE: 2358e6231bcaSNikolay Aleksandrov changed = br_multicast_isexc(pg, grec->grec_src, nsrcs, 2359e6231bcaSNikolay Aleksandrov sizeof(struct in6_addr)); 2360e6231bcaSNikolay Aleksandrov break; 23615bf1e00bSNikolay Aleksandrov case MLD2_CHANGE_TO_INCLUDE: 23625bf1e00bSNikolay Aleksandrov changed = br_multicast_toin(pg, grec->grec_src, nsrcs, 23635bf1e00bSNikolay Aleksandrov sizeof(struct in6_addr)); 23645bf1e00bSNikolay Aleksandrov break; 23655bf1e00bSNikolay Aleksandrov case MLD2_CHANGE_TO_EXCLUDE: 23665bf1e00bSNikolay Aleksandrov changed = br_multicast_toex(pg, grec->grec_src, nsrcs, 23675bf1e00bSNikolay Aleksandrov sizeof(struct in6_addr)); 23685bf1e00bSNikolay Aleksandrov break; 2369109865feSNikolay Aleksandrov case MLD2_BLOCK_OLD_SOURCES: 2370109865feSNikolay Aleksandrov changed = br_multicast_block(pg, grec->grec_src, nsrcs, 2371109865feSNikolay Aleksandrov sizeof(struct in6_addr)); 2372109865feSNikolay Aleksandrov break; 23730436862eSNikolay Aleksandrov } 23740436862eSNikolay Aleksandrov if (changed) 23750436862eSNikolay Aleksandrov br_mdb_notify(br->dev, mdst, pg, RTM_NEWMDB); 23760436862eSNikolay Aleksandrov unlock_continue: 23770436862eSNikolay Aleksandrov spin_unlock_bh(&br->multicast_lock); 2378bc8c20acSSatish Ashok } 237908b202b6SYOSHIFUJI Hideaki 238008b202b6SYOSHIFUJI Hideaki return err; 238108b202b6SYOSHIFUJI Hideaki } 238208b202b6SYOSHIFUJI Hideaki #endif 238308b202b6SYOSHIFUJI Hideaki 2384dc4eb53aSLinus Lüssing static bool br_ip4_multicast_select_querier(struct net_bridge *br, 23852cd41431SLinus Lüssing struct net_bridge_port *port, 2386dc4eb53aSLinus Lüssing __be32 saddr) 2387dc4eb53aSLinus Lüssing { 2388dc4eb53aSLinus Lüssing if (!timer_pending(&br->ip4_own_query.timer) && 2389dc4eb53aSLinus Lüssing !timer_pending(&br->ip4_other_query.timer)) 2390dc4eb53aSLinus Lüssing goto update; 2391dc4eb53aSLinus Lüssing 2392deb96566SNikolay Aleksandrov if (!br->ip4_querier.addr.src.ip4) 2393dc4eb53aSLinus Lüssing goto update; 2394dc4eb53aSLinus Lüssing 2395deb96566SNikolay Aleksandrov if (ntohl(saddr) <= ntohl(br->ip4_querier.addr.src.ip4)) 2396dc4eb53aSLinus Lüssing goto update; 2397dc4eb53aSLinus Lüssing 2398dc4eb53aSLinus Lüssing return false; 2399dc4eb53aSLinus Lüssing 2400dc4eb53aSLinus Lüssing update: 2401deb96566SNikolay Aleksandrov br->ip4_querier.addr.src.ip4 = saddr; 2402dc4eb53aSLinus Lüssing 24032cd41431SLinus Lüssing /* update protected by general multicast_lock by caller */ 24042cd41431SLinus Lüssing rcu_assign_pointer(br->ip4_querier.port, port); 24052cd41431SLinus Lüssing 2406dc4eb53aSLinus Lüssing return true; 2407dc4eb53aSLinus Lüssing } 2408dc4eb53aSLinus Lüssing 2409dc4eb53aSLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 2410dc4eb53aSLinus Lüssing static bool br_ip6_multicast_select_querier(struct net_bridge *br, 24112cd41431SLinus Lüssing struct net_bridge_port *port, 2412dc4eb53aSLinus Lüssing struct in6_addr *saddr) 2413dc4eb53aSLinus Lüssing { 2414dc4eb53aSLinus Lüssing if (!timer_pending(&br->ip6_own_query.timer) && 2415dc4eb53aSLinus Lüssing !timer_pending(&br->ip6_other_query.timer)) 2416dc4eb53aSLinus Lüssing goto update; 2417dc4eb53aSLinus Lüssing 2418deb96566SNikolay Aleksandrov if (ipv6_addr_cmp(saddr, &br->ip6_querier.addr.src.ip6) <= 0) 2419dc4eb53aSLinus Lüssing goto update; 2420dc4eb53aSLinus Lüssing 2421dc4eb53aSLinus Lüssing return false; 2422dc4eb53aSLinus Lüssing 2423dc4eb53aSLinus Lüssing update: 2424deb96566SNikolay Aleksandrov br->ip6_querier.addr.src.ip6 = *saddr; 2425dc4eb53aSLinus Lüssing 24262cd41431SLinus Lüssing /* update protected by general multicast_lock by caller */ 24272cd41431SLinus Lüssing rcu_assign_pointer(br->ip6_querier.port, port); 24282cd41431SLinus Lüssing 2429dc4eb53aSLinus Lüssing return true; 2430dc4eb53aSLinus Lüssing } 2431dc4eb53aSLinus Lüssing #endif 2432dc4eb53aSLinus Lüssing 2433dc4eb53aSLinus Lüssing static bool br_multicast_select_querier(struct net_bridge *br, 24342cd41431SLinus Lüssing struct net_bridge_port *port, 2435dc4eb53aSLinus Lüssing struct br_ip *saddr) 2436dc4eb53aSLinus Lüssing { 2437dc4eb53aSLinus Lüssing switch (saddr->proto) { 2438dc4eb53aSLinus Lüssing case htons(ETH_P_IP): 2439deb96566SNikolay Aleksandrov return br_ip4_multicast_select_querier(br, port, saddr->src.ip4); 2440dc4eb53aSLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 2441dc4eb53aSLinus Lüssing case htons(ETH_P_IPV6): 2442deb96566SNikolay Aleksandrov return br_ip6_multicast_select_querier(br, port, &saddr->src.ip6); 2443dc4eb53aSLinus Lüssing #endif 2444dc4eb53aSLinus Lüssing } 2445dc4eb53aSLinus Lüssing 2446dc4eb53aSLinus Lüssing return false; 2447dc4eb53aSLinus Lüssing } 2448dc4eb53aSLinus Lüssing 2449cc0fdd80SLinus Lüssing static void 245090010b36SLinus Lüssing br_multicast_update_query_timer(struct net_bridge *br, 245190010b36SLinus Lüssing struct bridge_mcast_other_query *query, 2452b00589afSLinus Lüssing unsigned long max_delay) 2453b00589afSLinus Lüssing { 245490010b36SLinus Lüssing if (!timer_pending(&query->timer)) 245590010b36SLinus Lüssing query->delay_time = jiffies + max_delay; 2456b00589afSLinus Lüssing 245790010b36SLinus Lüssing mod_timer(&query->timer, jiffies + br->multicast_querier_interval); 2458b00589afSLinus Lüssing } 2459b00589afSLinus Lüssing 24606d549648SNogah Frankel static void br_port_mc_router_state_change(struct net_bridge_port *p, 24616d549648SNogah Frankel bool is_mc_router) 24626d549648SNogah Frankel { 24636d549648SNogah Frankel struct switchdev_attr attr = { 24646d549648SNogah Frankel .orig_dev = p->dev, 24656d549648SNogah Frankel .id = SWITCHDEV_ATTR_ID_PORT_MROUTER, 24666d549648SNogah Frankel .flags = SWITCHDEV_F_DEFER, 24676d549648SNogah Frankel .u.mrouter = is_mc_router, 24686d549648SNogah Frankel }; 24696d549648SNogah Frankel 24706d549648SNogah Frankel switchdev_port_attr_set(p->dev, &attr); 24716d549648SNogah Frankel } 24726d549648SNogah Frankel 24737e80c124Sstephen hemminger /* 24747c77602fSCong Wang * Add port to router_list 24757e80c124Sstephen hemminger * list is maintained ordered by pointer value 24767e80c124Sstephen hemminger * and locked by br->multicast_lock and RCU 24777e80c124Sstephen hemminger */ 24780909e117SHerbert Xu static void br_multicast_add_router(struct net_bridge *br, 24790909e117SHerbert Xu struct net_bridge_port *port) 24800909e117SHerbert Xu { 2481dcdca2c4Sstephen hemminger struct net_bridge_port *p; 2482b67bfe0dSSasha Levin struct hlist_node *slot = NULL; 24830909e117SHerbert Xu 24841a040eacSNikolay Aleksandrov if (!hlist_unhashed(&port->rlist)) 24851a040eacSNikolay Aleksandrov return; 24861a040eacSNikolay Aleksandrov 2487b67bfe0dSSasha Levin hlist_for_each_entry(p, &br->router_list, rlist) { 24887e80c124Sstephen hemminger if ((unsigned long) port >= (unsigned long) p) 24897e80c124Sstephen hemminger break; 2490b67bfe0dSSasha Levin slot = &p->rlist; 2491dcdca2c4Sstephen hemminger } 24920909e117SHerbert Xu 24937e80c124Sstephen hemminger if (slot) 24941d023284SKen Helias hlist_add_behind_rcu(&port->rlist, slot); 2495dcdca2c4Sstephen hemminger else 2496dcdca2c4Sstephen hemminger hlist_add_head_rcu(&port->rlist, &br->router_list); 2497949f1e39SSatish Ashok br_rtr_notify(br->dev, port, RTM_NEWMDB); 24986d549648SNogah Frankel br_port_mc_router_state_change(port, true); 24990909e117SHerbert Xu } 25000909e117SHerbert Xu 2501eb1d1641SHerbert Xu static void br_multicast_mark_router(struct net_bridge *br, 2502eb1d1641SHerbert Xu struct net_bridge_port *port) 2503eb1d1641SHerbert Xu { 2504eb1d1641SHerbert Xu unsigned long now = jiffies; 2505eb1d1641SHerbert Xu 2506eb1d1641SHerbert Xu if (!port) { 250777041420SYotam Gigi if (br->multicast_router == MDB_RTR_TYPE_TEMP_QUERY) { 250877041420SYotam Gigi if (!timer_pending(&br->multicast_router_timer)) 250977041420SYotam Gigi br_mc_router_state_change(br, true); 2510eb1d1641SHerbert Xu mod_timer(&br->multicast_router_timer, 2511eb1d1641SHerbert Xu now + br->multicast_querier_interval); 251277041420SYotam Gigi } 2513eb1d1641SHerbert Xu return; 2514eb1d1641SHerbert Xu } 2515eb1d1641SHerbert Xu 2516a55d8246SNikolay Aleksandrov if (port->multicast_router == MDB_RTR_TYPE_DISABLED || 2517a55d8246SNikolay Aleksandrov port->multicast_router == MDB_RTR_TYPE_PERM) 2518eb1d1641SHerbert Xu return; 2519eb1d1641SHerbert Xu 25200909e117SHerbert Xu br_multicast_add_router(br, port); 2521eb1d1641SHerbert Xu 2522eb1d1641SHerbert Xu mod_timer(&port->multicast_router_timer, 2523eb1d1641SHerbert Xu now + br->multicast_querier_interval); 2524eb1d1641SHerbert Xu } 2525eb1d1641SHerbert Xu 2526eb1d1641SHerbert Xu static void br_multicast_query_received(struct net_bridge *br, 2527eb1d1641SHerbert Xu struct net_bridge_port *port, 252890010b36SLinus Lüssing struct bridge_mcast_other_query *query, 2529dc4eb53aSLinus Lüssing struct br_ip *saddr, 2530b00589afSLinus Lüssing unsigned long max_delay) 2531eb1d1641SHerbert Xu { 25322cd41431SLinus Lüssing if (!br_multicast_select_querier(br, port, saddr)) 2533eb1d1641SHerbert Xu return; 2534eb1d1641SHerbert Xu 2535dc4eb53aSLinus Lüssing br_multicast_update_query_timer(br, query, max_delay); 2536eb1d1641SHerbert Xu br_multicast_mark_router(br, port); 2537eb1d1641SHerbert Xu } 2538eb1d1641SHerbert Xu 25399c2e955cSzhong jiang static void br_ip4_multicast_query(struct net_bridge *br, 2540eb1d1641SHerbert Xu struct net_bridge_port *port, 254106499098SVlad Yasevich struct sk_buff *skb, 254206499098SVlad Yasevich u16 vid) 2543eb1d1641SHerbert Xu { 2544ba5ea614SLinus Lüssing unsigned int transport_len = ip_transport_len(skb); 2545b71d1d42SEric Dumazet const struct iphdr *iph = ip_hdr(skb); 2546eb1d1641SHerbert Xu struct igmphdr *ih = igmp_hdr(skb); 2547eb1d1641SHerbert Xu struct net_bridge_mdb_entry *mp; 2548eb1d1641SHerbert Xu struct igmpv3_query *ih3; 2549eb1d1641SHerbert Xu struct net_bridge_port_group *p; 2550e8051688SEric Dumazet struct net_bridge_port_group __rcu **pp; 2551dc4eb53aSLinus Lüssing struct br_ip saddr; 2552eb1d1641SHerbert Xu unsigned long max_delay; 2553eb1d1641SHerbert Xu unsigned long now = jiffies; 2554eb1d1641SHerbert Xu __be32 group; 2555eb1d1641SHerbert Xu 2556eb1d1641SHerbert Xu spin_lock(&br->multicast_lock); 2557eb1d1641SHerbert Xu if (!netif_running(br->dev) || 2558eb1d1641SHerbert Xu (port && port->state == BR_STATE_DISABLED)) 2559eb1d1641SHerbert Xu goto out; 2560eb1d1641SHerbert Xu 2561eb1d1641SHerbert Xu group = ih->group; 2562eb1d1641SHerbert Xu 2563ba5ea614SLinus Lüssing if (transport_len == sizeof(*ih)) { 2564eb1d1641SHerbert Xu max_delay = ih->code * (HZ / IGMP_TIMER_SCALE); 2565eb1d1641SHerbert Xu 2566eb1d1641SHerbert Xu if (!max_delay) { 2567eb1d1641SHerbert Xu max_delay = 10 * HZ; 2568eb1d1641SHerbert Xu group = 0; 2569eb1d1641SHerbert Xu } 2570ba5ea614SLinus Lüssing } else if (transport_len >= sizeof(*ih3)) { 2571eb1d1641SHerbert Xu ih3 = igmpv3_query_hdr(skb); 257223550b83SNikolay Aleksandrov if (ih3->nsrcs || 257323550b83SNikolay Aleksandrov (br->multicast_igmp_version == 3 && group && ih3->suppress)) 2574bec68ff1SYOSHIFUJI Hideaki goto out; 2575eb1d1641SHerbert Xu 25760ba8c9ecSYOSHIFUJI Hideaki / 吉藤英明 max_delay = ih3->code ? 25770ba8c9ecSYOSHIFUJI Hideaki / 吉藤英明 IGMPV3_MRC(ih3->code) * (HZ / IGMP_TIMER_SCALE) : 1; 25789afd85c9SLinus Lüssing } else { 25799ed973ccSLinus Lüssing goto out; 25809ed973ccSLinus Lüssing } 25819ed973ccSLinus Lüssing 2582dc4eb53aSLinus Lüssing if (!group) { 2583dc4eb53aSLinus Lüssing saddr.proto = htons(ETH_P_IP); 2584deb96566SNikolay Aleksandrov saddr.src.ip4 = iph->saddr; 2585b00589afSLinus Lüssing 2586dc4eb53aSLinus Lüssing br_multicast_query_received(br, port, &br->ip4_other_query, 2587dc4eb53aSLinus Lüssing &saddr, max_delay); 2588eb1d1641SHerbert Xu goto out; 2589dc4eb53aSLinus Lüssing } 2590eb1d1641SHerbert Xu 259119e3a9c9SNikolay Aleksandrov mp = br_mdb_ip4_get(br, group, vid); 2592eb1d1641SHerbert Xu if (!mp) 2593eb1d1641SHerbert Xu goto out; 2594eb1d1641SHerbert Xu 2595eb1d1641SHerbert Xu max_delay *= br->multicast_last_member_count; 2596eb1d1641SHerbert Xu 2597ff0fd34eSAndrew Lunn if (mp->host_joined && 2598eb1d1641SHerbert Xu (timer_pending(&mp->timer) ? 2599eb1d1641SHerbert Xu time_after(mp->timer.expires, now + max_delay) : 2600eb1d1641SHerbert Xu try_to_del_timer_sync(&mp->timer) >= 0)) 2601eb1d1641SHerbert Xu mod_timer(&mp->timer, now + max_delay); 2602eb1d1641SHerbert Xu 2603e8051688SEric Dumazet for (pp = &mp->ports; 2604e8051688SEric Dumazet (p = mlock_dereference(*pp, br)) != NULL; 2605e8051688SEric Dumazet pp = &p->next) { 2606eb1d1641SHerbert Xu if (timer_pending(&p->timer) ? 2607eb1d1641SHerbert Xu time_after(p->timer.expires, now + max_delay) : 260823550b83SNikolay Aleksandrov try_to_del_timer_sync(&p->timer) >= 0 && 260923550b83SNikolay Aleksandrov (br->multicast_igmp_version == 2 || 261023550b83SNikolay Aleksandrov p->filter_mode == MCAST_EXCLUDE)) 261124f9cdcbSHerbert Xu mod_timer(&p->timer, now + max_delay); 2612eb1d1641SHerbert Xu } 2613eb1d1641SHerbert Xu 2614eb1d1641SHerbert Xu out: 2615eb1d1641SHerbert Xu spin_unlock(&br->multicast_lock); 2616eb1d1641SHerbert Xu } 2617eb1d1641SHerbert Xu 2618dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 261908b202b6SYOSHIFUJI Hideaki static int br_ip6_multicast_query(struct net_bridge *br, 262008b202b6SYOSHIFUJI Hideaki struct net_bridge_port *port, 262106499098SVlad Yasevich struct sk_buff *skb, 262206499098SVlad Yasevich u16 vid) 262308b202b6SYOSHIFUJI Hideaki { 2624ba5ea614SLinus Lüssing unsigned int transport_len = ipv6_transport_len(skb); 2625eca2a43bSstephen hemminger struct mld_msg *mld; 262608b202b6SYOSHIFUJI Hideaki struct net_bridge_mdb_entry *mp; 262708b202b6SYOSHIFUJI Hideaki struct mld2_query *mld2q; 2628e8051688SEric Dumazet struct net_bridge_port_group *p; 2629e8051688SEric Dumazet struct net_bridge_port_group __rcu **pp; 2630dc4eb53aSLinus Lüssing struct br_ip saddr; 263108b202b6SYOSHIFUJI Hideaki unsigned long max_delay; 263208b202b6SYOSHIFUJI Hideaki unsigned long now = jiffies; 2633856ce5d0SLinus Lüssing unsigned int offset = skb_transport_offset(skb); 2634b71d1d42SEric Dumazet const struct in6_addr *group = NULL; 26359ed973ccSLinus Lüssing bool is_general_query; 263608b202b6SYOSHIFUJI Hideaki int err = 0; 263708b202b6SYOSHIFUJI Hideaki 263808b202b6SYOSHIFUJI Hideaki spin_lock(&br->multicast_lock); 263908b202b6SYOSHIFUJI Hideaki if (!netif_running(br->dev) || 264008b202b6SYOSHIFUJI Hideaki (port && port->state == BR_STATE_DISABLED)) 264108b202b6SYOSHIFUJI Hideaki goto out; 264208b202b6SYOSHIFUJI Hideaki 2643ba5ea614SLinus Lüssing if (transport_len == sizeof(*mld)) { 2644856ce5d0SLinus Lüssing if (!pskb_may_pull(skb, offset + sizeof(*mld))) { 264508b202b6SYOSHIFUJI Hideaki err = -EINVAL; 264608b202b6SYOSHIFUJI Hideaki goto out; 264708b202b6SYOSHIFUJI Hideaki } 264808b202b6SYOSHIFUJI Hideaki mld = (struct mld_msg *) icmp6_hdr(skb); 26494715213dSLi RongQing max_delay = msecs_to_jiffies(ntohs(mld->mld_maxdelay)); 265008b202b6SYOSHIFUJI Hideaki if (max_delay) 265108b202b6SYOSHIFUJI Hideaki group = &mld->mld_mca; 2652248ba8ecSLinus Lüssing } else { 2653856ce5d0SLinus Lüssing if (!pskb_may_pull(skb, offset + sizeof(*mld2q))) { 265408b202b6SYOSHIFUJI Hideaki err = -EINVAL; 265508b202b6SYOSHIFUJI Hideaki goto out; 265608b202b6SYOSHIFUJI Hideaki } 265708b202b6SYOSHIFUJI Hideaki mld2q = (struct mld2_query *)icmp6_hdr(skb); 265808b202b6SYOSHIFUJI Hideaki if (!mld2q->mld2q_nsrcs) 265908b202b6SYOSHIFUJI Hideaki group = &mld2q->mld2q_mca; 266023550b83SNikolay Aleksandrov if (br->multicast_mld_version == 2 && 266123550b83SNikolay Aleksandrov !ipv6_addr_any(&mld2q->mld2q_mca) && 266223550b83SNikolay Aleksandrov mld2q->mld2q_suppress) 266323550b83SNikolay Aleksandrov goto out; 2664e3f5b170SDaniel Borkmann 2665e3f5b170SDaniel Borkmann max_delay = max(msecs_to_jiffies(mldv2_mrc(mld2q)), 1UL); 266608b202b6SYOSHIFUJI Hideaki } 266708b202b6SYOSHIFUJI Hideaki 26689ed973ccSLinus Lüssing is_general_query = group && ipv6_addr_any(group); 26699ed973ccSLinus Lüssing 2670dc4eb53aSLinus Lüssing if (is_general_query) { 2671dc4eb53aSLinus Lüssing saddr.proto = htons(ETH_P_IPV6); 2672deb96566SNikolay Aleksandrov saddr.src.ip6 = ipv6_hdr(skb)->saddr; 2673b00589afSLinus Lüssing 2674dc4eb53aSLinus Lüssing br_multicast_query_received(br, port, &br->ip6_other_query, 2675dc4eb53aSLinus Lüssing &saddr, max_delay); 267608b202b6SYOSHIFUJI Hideaki goto out; 26776c03ee8bSLinus Lüssing } else if (!group) { 26786c03ee8bSLinus Lüssing goto out; 2679dc4eb53aSLinus Lüssing } 268008b202b6SYOSHIFUJI Hideaki 268119e3a9c9SNikolay Aleksandrov mp = br_mdb_ip6_get(br, group, vid); 268208b202b6SYOSHIFUJI Hideaki if (!mp) 268308b202b6SYOSHIFUJI Hideaki goto out; 268408b202b6SYOSHIFUJI Hideaki 268508b202b6SYOSHIFUJI Hideaki max_delay *= br->multicast_last_member_count; 2686ff0fd34eSAndrew Lunn if (mp->host_joined && 268708b202b6SYOSHIFUJI Hideaki (timer_pending(&mp->timer) ? 268808b202b6SYOSHIFUJI Hideaki time_after(mp->timer.expires, now + max_delay) : 268908b202b6SYOSHIFUJI Hideaki try_to_del_timer_sync(&mp->timer) >= 0)) 269008b202b6SYOSHIFUJI Hideaki mod_timer(&mp->timer, now + max_delay); 269108b202b6SYOSHIFUJI Hideaki 2692e8051688SEric Dumazet for (pp = &mp->ports; 2693e8051688SEric Dumazet (p = mlock_dereference(*pp, br)) != NULL; 2694e8051688SEric Dumazet pp = &p->next) { 269508b202b6SYOSHIFUJI Hideaki if (timer_pending(&p->timer) ? 269608b202b6SYOSHIFUJI Hideaki time_after(p->timer.expires, now + max_delay) : 269723550b83SNikolay Aleksandrov try_to_del_timer_sync(&p->timer) >= 0 && 269823550b83SNikolay Aleksandrov (br->multicast_mld_version == 1 || 269923550b83SNikolay Aleksandrov p->filter_mode == MCAST_EXCLUDE)) 270024f9cdcbSHerbert Xu mod_timer(&p->timer, now + max_delay); 270108b202b6SYOSHIFUJI Hideaki } 270208b202b6SYOSHIFUJI Hideaki 270308b202b6SYOSHIFUJI Hideaki out: 270408b202b6SYOSHIFUJI Hideaki spin_unlock(&br->multicast_lock); 270508b202b6SYOSHIFUJI Hideaki return err; 270608b202b6SYOSHIFUJI Hideaki } 270708b202b6SYOSHIFUJI Hideaki #endif 270808b202b6SYOSHIFUJI Hideaki 270990010b36SLinus Lüssing static void 271090010b36SLinus Lüssing br_multicast_leave_group(struct net_bridge *br, 2711eb1d1641SHerbert Xu struct net_bridge_port *port, 2712cc0fdd80SLinus Lüssing struct br_ip *group, 271390010b36SLinus Lüssing struct bridge_mcast_other_query *other_query, 27146db6f0eaSFelix Fietkau struct bridge_mcast_own_query *own_query, 27156db6f0eaSFelix Fietkau const unsigned char *src) 2716eb1d1641SHerbert Xu { 2717eb1d1641SHerbert Xu struct net_bridge_mdb_entry *mp; 2718eb1d1641SHerbert Xu struct net_bridge_port_group *p; 2719eb1d1641SHerbert Xu unsigned long now; 2720eb1d1641SHerbert Xu unsigned long time; 2721eb1d1641SHerbert Xu 2722eb1d1641SHerbert Xu spin_lock(&br->multicast_lock); 2723eb1d1641SHerbert Xu if (!netif_running(br->dev) || 2724544586f7SSatish Ashok (port && port->state == BR_STATE_DISABLED)) 2725eb1d1641SHerbert Xu goto out; 2726eb1d1641SHerbert Xu 272719e3a9c9SNikolay Aleksandrov mp = br_mdb_ip_get(br, group); 2728eb1d1641SHerbert Xu if (!mp) 2729eb1d1641SHerbert Xu goto out; 2730eb1d1641SHerbert Xu 2731544586f7SSatish Ashok if (port && (port->flags & BR_MULTICAST_FAST_LEAVE)) { 2732544586f7SSatish Ashok struct net_bridge_port_group __rcu **pp; 2733544586f7SSatish Ashok 2734544586f7SSatish Ashok for (pp = &mp->ports; 2735544586f7SSatish Ashok (p = mlock_dereference(*pp, br)) != NULL; 2736544586f7SSatish Ashok pp = &p->next) { 27376db6f0eaSFelix Fietkau if (!br_port_group_equal(p, port, src)) 2738544586f7SSatish Ashok continue; 2739544586f7SSatish Ashok 27405c725b6bSNikolay Aleksandrov if (p->flags & MDB_PG_FLAGS_PERMANENT) 27415c725b6bSNikolay Aleksandrov break; 27425c725b6bSNikolay Aleksandrov 2743681590bdSNikolay Aleksandrov p->flags |= MDB_PG_FLAGS_FAST_LEAVE; 2744681590bdSNikolay Aleksandrov br_multicast_del_pg(mp, p, pp); 2745544586f7SSatish Ashok } 2746544586f7SSatish Ashok goto out; 2747544586f7SSatish Ashok } 2748544586f7SSatish Ashok 2749544586f7SSatish Ashok if (timer_pending(&other_query->timer)) 2750544586f7SSatish Ashok goto out; 2751544586f7SSatish Ashok 2752675779adSNikolay Aleksandrov if (br_opt_get(br, BROPT_MULTICAST_QUERIER)) { 2753438ef2d0SNikolay Aleksandrov __br_multicast_send_query(br, port, NULL, NULL, &mp->addr, 275442c11ccfSNikolay Aleksandrov false, 0, NULL); 27556b7df111SCong Wang 27566b7df111SCong Wang time = jiffies + br->multicast_last_member_count * 27576b7df111SCong Wang br->multicast_last_member_interval; 2758cc0fdd80SLinus Lüssing 275990010b36SLinus Lüssing mod_timer(&own_query->timer, time); 27606b7df111SCong Wang 27616b7df111SCong Wang for (p = mlock_dereference(mp->ports, br); 27626b7df111SCong Wang p != NULL; 27636b7df111SCong Wang p = mlock_dereference(p->next, br)) { 27646db6f0eaSFelix Fietkau if (!br_port_group_equal(p, port, src)) 27656b7df111SCong Wang continue; 27666b7df111SCong Wang 27676b7df111SCong Wang if (!hlist_unhashed(&p->mglist) && 27686b7df111SCong Wang (timer_pending(&p->timer) ? 27696b7df111SCong Wang time_after(p->timer.expires, time) : 27706b7df111SCong Wang try_to_del_timer_sync(&p->timer) >= 0)) { 27716b7df111SCong Wang mod_timer(&p->timer, time); 27726b7df111SCong Wang } 27736b7df111SCong Wang 27746b7df111SCong Wang break; 27756b7df111SCong Wang } 27766b7df111SCong Wang } 27776b7df111SCong Wang 2778eb1d1641SHerbert Xu now = jiffies; 2779eb1d1641SHerbert Xu time = now + br->multicast_last_member_count * 2780eb1d1641SHerbert Xu br->multicast_last_member_interval; 2781eb1d1641SHerbert Xu 2782eb1d1641SHerbert Xu if (!port) { 2783ff0fd34eSAndrew Lunn if (mp->host_joined && 2784eb1d1641SHerbert Xu (timer_pending(&mp->timer) ? 2785eb1d1641SHerbert Xu time_after(mp->timer.expires, time) : 2786eb1d1641SHerbert Xu try_to_del_timer_sync(&mp->timer) >= 0)) { 2787eb1d1641SHerbert Xu mod_timer(&mp->timer, time); 2788eb1d1641SHerbert Xu } 2789454594f3SLinus Lüssing 2790454594f3SLinus Lüssing goto out; 2791454594f3SLinus Lüssing } 2792454594f3SLinus Lüssing 2793454594f3SLinus Lüssing for (p = mlock_dereference(mp->ports, br); 2794454594f3SLinus Lüssing p != NULL; 2795454594f3SLinus Lüssing p = mlock_dereference(p->next, br)) { 2796085b53c8SNikolay Aleksandrov if (p->key.port != port) 2797454594f3SLinus Lüssing continue; 2798454594f3SLinus Lüssing 2799454594f3SLinus Lüssing if (!hlist_unhashed(&p->mglist) && 2800454594f3SLinus Lüssing (timer_pending(&p->timer) ? 2801454594f3SLinus Lüssing time_after(p->timer.expires, time) : 2802454594f3SLinus Lüssing try_to_del_timer_sync(&p->timer) >= 0)) { 2803454594f3SLinus Lüssing mod_timer(&p->timer, time); 2804454594f3SLinus Lüssing } 2805454594f3SLinus Lüssing 2806454594f3SLinus Lüssing break; 2807eb1d1641SHerbert Xu } 2808eb1d1641SHerbert Xu out: 2809eb1d1641SHerbert Xu spin_unlock(&br->multicast_lock); 2810eb1d1641SHerbert Xu } 2811eb1d1641SHerbert Xu 28128ef2a9a5SYOSHIFUJI Hideaki static void br_ip4_multicast_leave_group(struct net_bridge *br, 28138ef2a9a5SYOSHIFUJI Hideaki struct net_bridge_port *port, 2814b0e9a30dSVlad Yasevich __be32 group, 28156db6f0eaSFelix Fietkau __u16 vid, 28166db6f0eaSFelix Fietkau const unsigned char *src) 28178ef2a9a5SYOSHIFUJI Hideaki { 28188ef2a9a5SYOSHIFUJI Hideaki struct br_ip br_group; 281990010b36SLinus Lüssing struct bridge_mcast_own_query *own_query; 28208ef2a9a5SYOSHIFUJI Hideaki 28218ef2a9a5SYOSHIFUJI Hideaki if (ipv4_is_local_multicast(group)) 28228ef2a9a5SYOSHIFUJI Hideaki return; 28238ef2a9a5SYOSHIFUJI Hideaki 282490010b36SLinus Lüssing own_query = port ? &port->ip4_own_query : &br->ip4_own_query; 282590010b36SLinus Lüssing 28261515a63fSNikolay Aleksandrov memset(&br_group, 0, sizeof(br_group)); 2827eab3227bSNikolay Aleksandrov br_group.dst.ip4 = group; 28288ef2a9a5SYOSHIFUJI Hideaki br_group.proto = htons(ETH_P_IP); 2829b0e9a30dSVlad Yasevich br_group.vid = vid; 28308ef2a9a5SYOSHIFUJI Hideaki 283190010b36SLinus Lüssing br_multicast_leave_group(br, port, &br_group, &br->ip4_other_query, 28326db6f0eaSFelix Fietkau own_query, src); 28338ef2a9a5SYOSHIFUJI Hideaki } 28348ef2a9a5SYOSHIFUJI Hideaki 2835dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 283608b202b6SYOSHIFUJI Hideaki static void br_ip6_multicast_leave_group(struct net_bridge *br, 283708b202b6SYOSHIFUJI Hideaki struct net_bridge_port *port, 2838b0e9a30dSVlad Yasevich const struct in6_addr *group, 28396db6f0eaSFelix Fietkau __u16 vid, 28406db6f0eaSFelix Fietkau const unsigned char *src) 284108b202b6SYOSHIFUJI Hideaki { 284208b202b6SYOSHIFUJI Hideaki struct br_ip br_group; 284390010b36SLinus Lüssing struct bridge_mcast_own_query *own_query; 284408b202b6SYOSHIFUJI Hideaki 28453c3769e6SLinus Lüssing if (ipv6_addr_is_ll_all_nodes(group)) 284608b202b6SYOSHIFUJI Hideaki return; 284708b202b6SYOSHIFUJI Hideaki 284890010b36SLinus Lüssing own_query = port ? &port->ip6_own_query : &br->ip6_own_query; 284990010b36SLinus Lüssing 28501515a63fSNikolay Aleksandrov memset(&br_group, 0, sizeof(br_group)); 2851eab3227bSNikolay Aleksandrov br_group.dst.ip6 = *group; 285208b202b6SYOSHIFUJI Hideaki br_group.proto = htons(ETH_P_IPV6); 2853b0e9a30dSVlad Yasevich br_group.vid = vid; 285408b202b6SYOSHIFUJI Hideaki 285590010b36SLinus Lüssing br_multicast_leave_group(br, port, &br_group, &br->ip6_other_query, 28566db6f0eaSFelix Fietkau own_query, src); 285708b202b6SYOSHIFUJI Hideaki } 285808b202b6SYOSHIFUJI Hideaki #endif 28598ef2a9a5SYOSHIFUJI Hideaki 28601080ab95SNikolay Aleksandrov static void br_multicast_err_count(const struct net_bridge *br, 28611080ab95SNikolay Aleksandrov const struct net_bridge_port *p, 28621080ab95SNikolay Aleksandrov __be16 proto) 28631080ab95SNikolay Aleksandrov { 28641080ab95SNikolay Aleksandrov struct bridge_mcast_stats __percpu *stats; 28651080ab95SNikolay Aleksandrov struct bridge_mcast_stats *pstats; 28661080ab95SNikolay Aleksandrov 2867675779adSNikolay Aleksandrov if (!br_opt_get(br, BROPT_MULTICAST_STATS_ENABLED)) 28681080ab95SNikolay Aleksandrov return; 28691080ab95SNikolay Aleksandrov 28701080ab95SNikolay Aleksandrov if (p) 28711080ab95SNikolay Aleksandrov stats = p->mcast_stats; 28721080ab95SNikolay Aleksandrov else 28731080ab95SNikolay Aleksandrov stats = br->mcast_stats; 28741080ab95SNikolay Aleksandrov if (WARN_ON(!stats)) 28751080ab95SNikolay Aleksandrov return; 28761080ab95SNikolay Aleksandrov 28771080ab95SNikolay Aleksandrov pstats = this_cpu_ptr(stats); 28781080ab95SNikolay Aleksandrov 28791080ab95SNikolay Aleksandrov u64_stats_update_begin(&pstats->syncp); 28801080ab95SNikolay Aleksandrov switch (proto) { 28811080ab95SNikolay Aleksandrov case htons(ETH_P_IP): 28821080ab95SNikolay Aleksandrov pstats->mstats.igmp_parse_errors++; 28831080ab95SNikolay Aleksandrov break; 28841080ab95SNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6) 28851080ab95SNikolay Aleksandrov case htons(ETH_P_IPV6): 28861080ab95SNikolay Aleksandrov pstats->mstats.mld_parse_errors++; 28871080ab95SNikolay Aleksandrov break; 28881080ab95SNikolay Aleksandrov #endif 28891080ab95SNikolay Aleksandrov } 28901080ab95SNikolay Aleksandrov u64_stats_update_end(&pstats->syncp); 28911080ab95SNikolay Aleksandrov } 28921080ab95SNikolay Aleksandrov 289391b02d3dSNikolay Aleksandrov static void br_multicast_pim(struct net_bridge *br, 289491b02d3dSNikolay Aleksandrov struct net_bridge_port *port, 289591b02d3dSNikolay Aleksandrov const struct sk_buff *skb) 289691b02d3dSNikolay Aleksandrov { 289791b02d3dSNikolay Aleksandrov unsigned int offset = skb_transport_offset(skb); 289891b02d3dSNikolay Aleksandrov struct pimhdr *pimhdr, _pimhdr; 289991b02d3dSNikolay Aleksandrov 290091b02d3dSNikolay Aleksandrov pimhdr = skb_header_pointer(skb, offset, sizeof(_pimhdr), &_pimhdr); 290191b02d3dSNikolay Aleksandrov if (!pimhdr || pim_hdr_version(pimhdr) != PIM_VERSION || 290291b02d3dSNikolay Aleksandrov pim_hdr_type(pimhdr) != PIM_TYPE_HELLO) 290391b02d3dSNikolay Aleksandrov return; 290491b02d3dSNikolay Aleksandrov 290591b02d3dSNikolay Aleksandrov br_multicast_mark_router(br, port); 290691b02d3dSNikolay Aleksandrov } 290791b02d3dSNikolay Aleksandrov 29084b3087c7SLinus Lüssing static int br_ip4_multicast_mrd_rcv(struct net_bridge *br, 29094b3087c7SLinus Lüssing struct net_bridge_port *port, 29104b3087c7SLinus Lüssing struct sk_buff *skb) 29114b3087c7SLinus Lüssing { 29124b3087c7SLinus Lüssing if (ip_hdr(skb)->protocol != IPPROTO_IGMP || 29134b3087c7SLinus Lüssing igmp_hdr(skb)->type != IGMP_MRDISC_ADV) 29144b3087c7SLinus Lüssing return -ENOMSG; 29154b3087c7SLinus Lüssing 29164b3087c7SLinus Lüssing br_multicast_mark_router(br, port); 29174b3087c7SLinus Lüssing 29184b3087c7SLinus Lüssing return 0; 29194b3087c7SLinus Lüssing } 29204b3087c7SLinus Lüssing 2921eb1d1641SHerbert Xu static int br_multicast_ipv4_rcv(struct net_bridge *br, 2922eb1d1641SHerbert Xu struct net_bridge_port *port, 292306499098SVlad Yasevich struct sk_buff *skb, 292406499098SVlad Yasevich u16 vid) 2925eb1d1641SHerbert Xu { 29266db6f0eaSFelix Fietkau const unsigned char *src; 2927eb1d1641SHerbert Xu struct igmphdr *ih; 2928eb1d1641SHerbert Xu int err; 2929eb1d1641SHerbert Xu 2930ba5ea614SLinus Lüssing err = ip_mc_check_igmp(skb); 2931eb1d1641SHerbert Xu 29329afd85c9SLinus Lüssing if (err == -ENOMSG) { 293391b02d3dSNikolay Aleksandrov if (!ipv4_is_local_multicast(ip_hdr(skb)->daddr)) { 2934bd4265feSHerbert Xu BR_INPUT_SKB_CB(skb)->mrouters_only = 1; 293591b02d3dSNikolay Aleksandrov } else if (pim_ipv4_all_pim_routers(ip_hdr(skb)->daddr)) { 293691b02d3dSNikolay Aleksandrov if (ip_hdr(skb)->protocol == IPPROTO_PIM) 293791b02d3dSNikolay Aleksandrov br_multicast_pim(br, port, skb); 29384b3087c7SLinus Lüssing } else if (ipv4_is_all_snoopers(ip_hdr(skb)->daddr)) { 293908e71623SLi RongQing br_ip4_multicast_mrd_rcv(br, port, skb); 29404b3087c7SLinus Lüssing } 29414b3087c7SLinus Lüssing 2942eb1d1641SHerbert Xu return 0; 29439afd85c9SLinus Lüssing } else if (err < 0) { 29441080ab95SNikolay Aleksandrov br_multicast_err_count(br, port, skb->protocol); 29459afd85c9SLinus Lüssing return err; 2946bd4265feSHerbert Xu } 2947eb1d1641SHerbert Xu 29489afd85c9SLinus Lüssing ih = igmp_hdr(skb); 29496db6f0eaSFelix Fietkau src = eth_hdr(skb)->h_source; 29501080ab95SNikolay Aleksandrov BR_INPUT_SKB_CB(skb)->igmp = ih->type; 2951eb1d1641SHerbert Xu 2952eb1d1641SHerbert Xu switch (ih->type) { 2953eb1d1641SHerbert Xu case IGMP_HOST_MEMBERSHIP_REPORT: 2954eb1d1641SHerbert Xu case IGMPV2_HOST_MEMBERSHIP_REPORT: 295562b2bcb4SFernando Luis Vázquez Cao BR_INPUT_SKB_CB(skb)->mrouters_only = 1; 29568b671779SNikolay Aleksandrov err = br_ip4_multicast_add_group(br, port, ih->group, vid, src, 29578b671779SNikolay Aleksandrov true); 2958eb1d1641SHerbert Xu break; 2959eb1d1641SHerbert Xu case IGMPV3_HOST_MEMBERSHIP_REPORT: 2960ba5ea614SLinus Lüssing err = br_ip4_multicast_igmp3_report(br, port, skb, vid); 2961eb1d1641SHerbert Xu break; 2962eb1d1641SHerbert Xu case IGMP_HOST_MEMBERSHIP_QUERY: 2963ba5ea614SLinus Lüssing br_ip4_multicast_query(br, port, skb, vid); 2964eb1d1641SHerbert Xu break; 2965eb1d1641SHerbert Xu case IGMP_HOST_LEAVE_MESSAGE: 29666db6f0eaSFelix Fietkau br_ip4_multicast_leave_group(br, port, ih->group, vid, src); 2967eb1d1641SHerbert Xu break; 2968eb1d1641SHerbert Xu } 2969eb1d1641SHerbert Xu 2970a65056ecSNikolay Aleksandrov br_multicast_count(br, port, skb, BR_INPUT_SKB_CB(skb)->igmp, 29711080ab95SNikolay Aleksandrov BR_MCAST_DIR_RX); 29721080ab95SNikolay Aleksandrov 2973eb1d1641SHerbert Xu return err; 2974eb1d1641SHerbert Xu } 2975eb1d1641SHerbert Xu 2976dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 29774b3087c7SLinus Lüssing static int br_ip6_multicast_mrd_rcv(struct net_bridge *br, 29784b3087c7SLinus Lüssing struct net_bridge_port *port, 29794b3087c7SLinus Lüssing struct sk_buff *skb) 29804b3087c7SLinus Lüssing { 29814b3087c7SLinus Lüssing int ret; 29824b3087c7SLinus Lüssing 29834b3087c7SLinus Lüssing if (ipv6_hdr(skb)->nexthdr != IPPROTO_ICMPV6) 29844b3087c7SLinus Lüssing return -ENOMSG; 29854b3087c7SLinus Lüssing 29864b3087c7SLinus Lüssing ret = ipv6_mc_check_icmpv6(skb); 29874b3087c7SLinus Lüssing if (ret < 0) 29884b3087c7SLinus Lüssing return ret; 29894b3087c7SLinus Lüssing 29904b3087c7SLinus Lüssing if (icmp6_hdr(skb)->icmp6_type != ICMPV6_MRDISC_ADV) 29914b3087c7SLinus Lüssing return -ENOMSG; 29924b3087c7SLinus Lüssing 29934b3087c7SLinus Lüssing br_multicast_mark_router(br, port); 29944b3087c7SLinus Lüssing 29954b3087c7SLinus Lüssing return 0; 29964b3087c7SLinus Lüssing } 29974b3087c7SLinus Lüssing 299808b202b6SYOSHIFUJI Hideaki static int br_multicast_ipv6_rcv(struct net_bridge *br, 299908b202b6SYOSHIFUJI Hideaki struct net_bridge_port *port, 300006499098SVlad Yasevich struct sk_buff *skb, 300106499098SVlad Yasevich u16 vid) 300208b202b6SYOSHIFUJI Hideaki { 30036db6f0eaSFelix Fietkau const unsigned char *src; 30049afd85c9SLinus Lüssing struct mld_msg *mld; 300508b202b6SYOSHIFUJI Hideaki int err; 300608b202b6SYOSHIFUJI Hideaki 3007ba5ea614SLinus Lüssing err = ipv6_mc_check_mld(skb); 300808b202b6SYOSHIFUJI Hideaki 30099afd85c9SLinus Lüssing if (err == -ENOMSG) { 30109afd85c9SLinus Lüssing if (!ipv6_addr_is_ll_all_nodes(&ipv6_hdr(skb)->daddr)) 30118fad9c39SLinus Lüssing BR_INPUT_SKB_CB(skb)->mrouters_only = 1; 30124b3087c7SLinus Lüssing 30134b3087c7SLinus Lüssing if (ipv6_addr_is_all_snoopers(&ipv6_hdr(skb)->daddr)) { 30144b3087c7SLinus Lüssing err = br_ip6_multicast_mrd_rcv(br, port, skb); 30154b3087c7SLinus Lüssing 30164b3087c7SLinus Lüssing if (err < 0 && err != -ENOMSG) { 30174b3087c7SLinus Lüssing br_multicast_err_count(br, port, skb->protocol); 30184b3087c7SLinus Lüssing return err; 30194b3087c7SLinus Lüssing } 30204b3087c7SLinus Lüssing } 30214b3087c7SLinus Lüssing 302208b202b6SYOSHIFUJI Hideaki return 0; 30239afd85c9SLinus Lüssing } else if (err < 0) { 30241080ab95SNikolay Aleksandrov br_multicast_err_count(br, port, skb->protocol); 30259afd85c9SLinus Lüssing return err; 302608b202b6SYOSHIFUJI Hideaki } 302708b202b6SYOSHIFUJI Hideaki 30289afd85c9SLinus Lüssing mld = (struct mld_msg *)skb_transport_header(skb); 30291080ab95SNikolay Aleksandrov BR_INPUT_SKB_CB(skb)->igmp = mld->mld_type; 303008b202b6SYOSHIFUJI Hideaki 30319afd85c9SLinus Lüssing switch (mld->mld_type) { 303208b202b6SYOSHIFUJI Hideaki case ICMPV6_MGM_REPORT: 30336db6f0eaSFelix Fietkau src = eth_hdr(skb)->h_source; 3034fc2af6c7SFernando Luis Vázquez Cao BR_INPUT_SKB_CB(skb)->mrouters_only = 1; 30356db6f0eaSFelix Fietkau err = br_ip6_multicast_add_group(br, port, &mld->mld_mca, vid, 30368b671779SNikolay Aleksandrov src, true); 303708b202b6SYOSHIFUJI Hideaki break; 303808b202b6SYOSHIFUJI Hideaki case ICMPV6_MLD2_REPORT: 3039ba5ea614SLinus Lüssing err = br_ip6_multicast_mld2_report(br, port, skb, vid); 304008b202b6SYOSHIFUJI Hideaki break; 304108b202b6SYOSHIFUJI Hideaki case ICMPV6_MGM_QUERY: 3042ba5ea614SLinus Lüssing err = br_ip6_multicast_query(br, port, skb, vid); 304308b202b6SYOSHIFUJI Hideaki break; 304408b202b6SYOSHIFUJI Hideaki case ICMPV6_MGM_REDUCTION: 30456db6f0eaSFelix Fietkau src = eth_hdr(skb)->h_source; 30466db6f0eaSFelix Fietkau br_ip6_multicast_leave_group(br, port, &mld->mld_mca, vid, src); 30479afd85c9SLinus Lüssing break; 304808b202b6SYOSHIFUJI Hideaki } 304908b202b6SYOSHIFUJI Hideaki 3050a65056ecSNikolay Aleksandrov br_multicast_count(br, port, skb, BR_INPUT_SKB_CB(skb)->igmp, 30511080ab95SNikolay Aleksandrov BR_MCAST_DIR_RX); 30521080ab95SNikolay Aleksandrov 305308b202b6SYOSHIFUJI Hideaki return err; 305408b202b6SYOSHIFUJI Hideaki } 305508b202b6SYOSHIFUJI Hideaki #endif 305608b202b6SYOSHIFUJI Hideaki 3057eb1d1641SHerbert Xu int br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port, 305806499098SVlad Yasevich struct sk_buff *skb, u16 vid) 3059eb1d1641SHerbert Xu { 30601080ab95SNikolay Aleksandrov int ret = 0; 30611080ab95SNikolay Aleksandrov 30621fafc7a9SYOSHIFUJI Hideaki / 吉藤英明 BR_INPUT_SKB_CB(skb)->igmp = 0; 30631fafc7a9SYOSHIFUJI Hideaki / 吉藤英明 BR_INPUT_SKB_CB(skb)->mrouters_only = 0; 30641fafc7a9SYOSHIFUJI Hideaki / 吉藤英明 306513cefad2SNikolay Aleksandrov if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) 3066eb1d1641SHerbert Xu return 0; 3067eb1d1641SHerbert Xu 3068eb1d1641SHerbert Xu switch (skb->protocol) { 3069eb1d1641SHerbert Xu case htons(ETH_P_IP): 30701080ab95SNikolay Aleksandrov ret = br_multicast_ipv4_rcv(br, port, skb, vid); 30711080ab95SNikolay Aleksandrov break; 3072dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 307308b202b6SYOSHIFUJI Hideaki case htons(ETH_P_IPV6): 30741080ab95SNikolay Aleksandrov ret = br_multicast_ipv6_rcv(br, port, skb, vid); 30751080ab95SNikolay Aleksandrov break; 307608b202b6SYOSHIFUJI Hideaki #endif 3077eb1d1641SHerbert Xu } 3078eb1d1641SHerbert Xu 30791080ab95SNikolay Aleksandrov return ret; 3080eb1d1641SHerbert Xu } 3081eb1d1641SHerbert Xu 3082cc0fdd80SLinus Lüssing static void br_multicast_query_expired(struct net_bridge *br, 30832cd41431SLinus Lüssing struct bridge_mcast_own_query *query, 30842cd41431SLinus Lüssing struct bridge_mcast_querier *querier) 3085cc0fdd80SLinus Lüssing { 3086cc0fdd80SLinus Lüssing spin_lock(&br->multicast_lock); 3087cc0fdd80SLinus Lüssing if (query->startup_sent < br->multicast_startup_query_count) 3088cc0fdd80SLinus Lüssing query->startup_sent++; 3089cc0fdd80SLinus Lüssing 309071d9f614SEric Dumazet RCU_INIT_POINTER(querier->port, NULL); 3091cc0fdd80SLinus Lüssing br_multicast_send_query(br, NULL, query); 3092cc0fdd80SLinus Lüssing spin_unlock(&br->multicast_lock); 3093cc0fdd80SLinus Lüssing } 3094cc0fdd80SLinus Lüssing 309588c1f37fSAllen Pais static void br_ip4_multicast_query_expired(struct timer_list *t) 3096eb1d1641SHerbert Xu { 309788c1f37fSAllen Pais struct net_bridge *br = from_timer(br, t, ip4_own_query.timer); 3098eb1d1641SHerbert Xu 30992cd41431SLinus Lüssing br_multicast_query_expired(br, &br->ip4_own_query, &br->ip4_querier); 3100eb1d1641SHerbert Xu } 3101eb1d1641SHerbert Xu 3102cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 310388c1f37fSAllen Pais static void br_ip6_multicast_query_expired(struct timer_list *t) 3104cc0fdd80SLinus Lüssing { 310588c1f37fSAllen Pais struct net_bridge *br = from_timer(br, t, ip6_own_query.timer); 3106cc0fdd80SLinus Lüssing 31072cd41431SLinus Lüssing br_multicast_query_expired(br, &br->ip6_own_query, &br->ip6_querier); 3108cc0fdd80SLinus Lüssing } 3109cc0fdd80SLinus Lüssing #endif 3110cc0fdd80SLinus Lüssing 3111e12cec65SNikolay Aleksandrov static void br_multicast_gc_work(struct work_struct *work) 31128b671779SNikolay Aleksandrov { 31138b671779SNikolay Aleksandrov struct net_bridge *br = container_of(work, struct net_bridge, 3114e12cec65SNikolay Aleksandrov mcast_gc_work); 31158b671779SNikolay Aleksandrov HLIST_HEAD(deleted_head); 31168b671779SNikolay Aleksandrov 31178b671779SNikolay Aleksandrov spin_lock_bh(&br->multicast_lock); 3118e12cec65SNikolay Aleksandrov hlist_move_list(&br->mcast_gc_list, &deleted_head); 31198b671779SNikolay Aleksandrov spin_unlock_bh(&br->multicast_lock); 31208b671779SNikolay Aleksandrov 3121e12cec65SNikolay Aleksandrov br_multicast_gc(&deleted_head); 31228b671779SNikolay Aleksandrov } 31238b671779SNikolay Aleksandrov 3124eb1d1641SHerbert Xu void br_multicast_init(struct net_bridge *br) 3125eb1d1641SHerbert Xu { 3126d08c6bc0SNikolay Aleksandrov br->hash_max = BR_MULTICAST_DEFAULT_HASH_MAX; 3127eb1d1641SHerbert Xu 31287f0aec7aSNikolay Aleksandrov br->multicast_router = MDB_RTR_TYPE_TEMP_QUERY; 3129eb1d1641SHerbert Xu br->multicast_last_member_count = 2; 3130eb1d1641SHerbert Xu br->multicast_startup_query_count = 2; 3131eb1d1641SHerbert Xu 3132eb1d1641SHerbert Xu br->multicast_last_member_interval = HZ; 3133eb1d1641SHerbert Xu br->multicast_query_response_interval = 10 * HZ; 3134eb1d1641SHerbert Xu br->multicast_startup_query_interval = 125 * HZ / 4; 3135eb1d1641SHerbert Xu br->multicast_query_interval = 125 * HZ; 3136eb1d1641SHerbert Xu br->multicast_querier_interval = 255 * HZ; 3137eb1d1641SHerbert Xu br->multicast_membership_interval = 260 * HZ; 3138eb1d1641SHerbert Xu 313990010b36SLinus Lüssing br->ip4_other_query.delay_time = 0; 31402cd41431SLinus Lüssing br->ip4_querier.port = NULL; 3141aa2ae3e7SNikolay Aleksandrov br->multicast_igmp_version = 2; 3142cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 3143aa2ae3e7SNikolay Aleksandrov br->multicast_mld_version = 1; 314490010b36SLinus Lüssing br->ip6_other_query.delay_time = 0; 31452cd41431SLinus Lüssing br->ip6_querier.port = NULL; 3146cc0fdd80SLinus Lüssing #endif 31476919622aSIdo Schimmel br_opt_toggle(br, BROPT_MULTICAST_ENABLED, true); 3148675779adSNikolay Aleksandrov br_opt_toggle(br, BROPT_HAS_IPV6_ADDR, true); 3149b00589afSLinus Lüssing 3150eb1d1641SHerbert Xu spin_lock_init(&br->multicast_lock); 315188c1f37fSAllen Pais timer_setup(&br->multicast_router_timer, 315288c1f37fSAllen Pais br_multicast_local_router_expired, 0); 315388c1f37fSAllen Pais timer_setup(&br->ip4_other_query.timer, 315488c1f37fSAllen Pais br_ip4_multicast_querier_expired, 0); 315588c1f37fSAllen Pais timer_setup(&br->ip4_own_query.timer, 315688c1f37fSAllen Pais br_ip4_multicast_query_expired, 0); 3157cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 315888c1f37fSAllen Pais timer_setup(&br->ip6_other_query.timer, 315988c1f37fSAllen Pais br_ip6_multicast_querier_expired, 0); 316088c1f37fSAllen Pais timer_setup(&br->ip6_own_query.timer, 316188c1f37fSAllen Pais br_ip6_multicast_query_expired, 0); 3162cc0fdd80SLinus Lüssing #endif 316319e3a9c9SNikolay Aleksandrov INIT_HLIST_HEAD(&br->mdb_list); 3164e12cec65SNikolay Aleksandrov INIT_HLIST_HEAD(&br->mcast_gc_list); 3165e12cec65SNikolay Aleksandrov INIT_WORK(&br->mcast_gc_work, br_multicast_gc_work); 3166eb1d1641SHerbert Xu } 3167eb1d1641SHerbert Xu 31684effd28cSLinus Lüssing static void br_ip4_multicast_join_snoopers(struct net_bridge *br) 31694effd28cSLinus Lüssing { 31704effd28cSLinus Lüssing struct in_device *in_dev = in_dev_get(br->dev); 31714effd28cSLinus Lüssing 31724effd28cSLinus Lüssing if (!in_dev) 31734effd28cSLinus Lüssing return; 31744effd28cSLinus Lüssing 31759fb20801SFlorian Fainelli __ip_mc_inc_group(in_dev, htonl(INADDR_ALLSNOOPERS_GROUP), GFP_ATOMIC); 31764effd28cSLinus Lüssing in_dev_put(in_dev); 31774effd28cSLinus Lüssing } 31784effd28cSLinus Lüssing 31794effd28cSLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 31804effd28cSLinus Lüssing static void br_ip6_multicast_join_snoopers(struct net_bridge *br) 31814effd28cSLinus Lüssing { 31824effd28cSLinus Lüssing struct in6_addr addr; 31834effd28cSLinus Lüssing 31844effd28cSLinus Lüssing ipv6_addr_set(&addr, htonl(0xff020000), 0, 0, htonl(0x6a)); 31854effd28cSLinus Lüssing ipv6_dev_mc_inc(br->dev, &addr); 31864effd28cSLinus Lüssing } 31874effd28cSLinus Lüssing #else 31884effd28cSLinus Lüssing static inline void br_ip6_multicast_join_snoopers(struct net_bridge *br) 31894effd28cSLinus Lüssing { 31904effd28cSLinus Lüssing } 31914effd28cSLinus Lüssing #endif 31924effd28cSLinus Lüssing 31934effd28cSLinus Lüssing static void br_multicast_join_snoopers(struct net_bridge *br) 31944effd28cSLinus Lüssing { 31954effd28cSLinus Lüssing br_ip4_multicast_join_snoopers(br); 31964effd28cSLinus Lüssing br_ip6_multicast_join_snoopers(br); 31974effd28cSLinus Lüssing } 31984effd28cSLinus Lüssing 31994effd28cSLinus Lüssing static void br_ip4_multicast_leave_snoopers(struct net_bridge *br) 32004effd28cSLinus Lüssing { 32014effd28cSLinus Lüssing struct in_device *in_dev = in_dev_get(br->dev); 32024effd28cSLinus Lüssing 32034effd28cSLinus Lüssing if (WARN_ON(!in_dev)) 32044effd28cSLinus Lüssing return; 32054effd28cSLinus Lüssing 32069fb20801SFlorian Fainelli __ip_mc_dec_group(in_dev, htonl(INADDR_ALLSNOOPERS_GROUP), GFP_ATOMIC); 32074effd28cSLinus Lüssing in_dev_put(in_dev); 32084effd28cSLinus Lüssing } 32094effd28cSLinus Lüssing 32104effd28cSLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 32114effd28cSLinus Lüssing static void br_ip6_multicast_leave_snoopers(struct net_bridge *br) 32124effd28cSLinus Lüssing { 32134effd28cSLinus Lüssing struct in6_addr addr; 32144effd28cSLinus Lüssing 32154effd28cSLinus Lüssing ipv6_addr_set(&addr, htonl(0xff020000), 0, 0, htonl(0x6a)); 32164effd28cSLinus Lüssing ipv6_dev_mc_dec(br->dev, &addr); 32174effd28cSLinus Lüssing } 32184effd28cSLinus Lüssing #else 32194effd28cSLinus Lüssing static inline void br_ip6_multicast_leave_snoopers(struct net_bridge *br) 32204effd28cSLinus Lüssing { 32214effd28cSLinus Lüssing } 32224effd28cSLinus Lüssing #endif 32234effd28cSLinus Lüssing 32244effd28cSLinus Lüssing static void br_multicast_leave_snoopers(struct net_bridge *br) 32254effd28cSLinus Lüssing { 32264effd28cSLinus Lüssing br_ip4_multicast_leave_snoopers(br); 32274effd28cSLinus Lüssing br_ip6_multicast_leave_snoopers(br); 32284effd28cSLinus Lüssing } 32294effd28cSLinus Lüssing 3230cc0fdd80SLinus Lüssing static void __br_multicast_open(struct net_bridge *br, 323190010b36SLinus Lüssing struct bridge_mcast_own_query *query) 3232eb1d1641SHerbert Xu { 3233cc0fdd80SLinus Lüssing query->startup_sent = 0; 3234eb1d1641SHerbert Xu 323513cefad2SNikolay Aleksandrov if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) 3236eb1d1641SHerbert Xu return; 3237eb1d1641SHerbert Xu 3238cc0fdd80SLinus Lüssing mod_timer(&query->timer, jiffies); 3239cc0fdd80SLinus Lüssing } 3240cc0fdd80SLinus Lüssing 3241cc0fdd80SLinus Lüssing void br_multicast_open(struct net_bridge *br) 3242cc0fdd80SLinus Lüssing { 32434effd28cSLinus Lüssing if (br_opt_get(br, BROPT_MULTICAST_ENABLED)) 32444effd28cSLinus Lüssing br_multicast_join_snoopers(br); 32454effd28cSLinus Lüssing 324690010b36SLinus Lüssing __br_multicast_open(br, &br->ip4_own_query); 3247cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 324890010b36SLinus Lüssing __br_multicast_open(br, &br->ip6_own_query); 3249cc0fdd80SLinus Lüssing #endif 3250eb1d1641SHerbert Xu } 3251eb1d1641SHerbert Xu 3252eb1d1641SHerbert Xu void br_multicast_stop(struct net_bridge *br) 3253eb1d1641SHerbert Xu { 3254eb1d1641SHerbert Xu del_timer_sync(&br->multicast_router_timer); 325590010b36SLinus Lüssing del_timer_sync(&br->ip4_other_query.timer); 325690010b36SLinus Lüssing del_timer_sync(&br->ip4_own_query.timer); 3257cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 325890010b36SLinus Lüssing del_timer_sync(&br->ip6_other_query.timer); 325990010b36SLinus Lüssing del_timer_sync(&br->ip6_own_query.timer); 3260cc0fdd80SLinus Lüssing #endif 32614effd28cSLinus Lüssing 32624effd28cSLinus Lüssing if (br_opt_get(br, BROPT_MULTICAST_ENABLED)) 32634effd28cSLinus Lüssing br_multicast_leave_snoopers(br); 3264e10177abSSatish Ashok } 3265e10177abSSatish Ashok 3266e10177abSSatish Ashok void br_multicast_dev_del(struct net_bridge *br) 3267e10177abSSatish Ashok { 3268e10177abSSatish Ashok struct net_bridge_mdb_entry *mp; 32698b671779SNikolay Aleksandrov HLIST_HEAD(deleted_head); 327019e3a9c9SNikolay Aleksandrov struct hlist_node *tmp; 3271eb1d1641SHerbert Xu 3272eb1d1641SHerbert Xu spin_lock_bh(&br->multicast_lock); 3273e12cec65SNikolay Aleksandrov hlist_for_each_entry_safe(mp, tmp, &br->mdb_list, mdb_node) 3274e12cec65SNikolay Aleksandrov br_multicast_del_mdb_entry(mp); 3275e12cec65SNikolay Aleksandrov hlist_move_list(&br->mcast_gc_list, &deleted_head); 3276eb1d1641SHerbert Xu spin_unlock_bh(&br->multicast_lock); 327719e3a9c9SNikolay Aleksandrov 3278e12cec65SNikolay Aleksandrov br_multicast_gc(&deleted_head); 3279e12cec65SNikolay Aleksandrov cancel_work_sync(&br->mcast_gc_work); 32808b671779SNikolay Aleksandrov 32814329596cSNikolay Aleksandrov rcu_barrier(); 3282eb1d1641SHerbert Xu } 32830909e117SHerbert Xu 32840909e117SHerbert Xu int br_multicast_set_router(struct net_bridge *br, unsigned long val) 32850909e117SHerbert Xu { 32866ae4ae8eSLinus Lüssing int err = -EINVAL; 32870909e117SHerbert Xu 32880909e117SHerbert Xu spin_lock_bh(&br->multicast_lock); 32890909e117SHerbert Xu 32900909e117SHerbert Xu switch (val) { 32917f0aec7aSNikolay Aleksandrov case MDB_RTR_TYPE_DISABLED: 32927f0aec7aSNikolay Aleksandrov case MDB_RTR_TYPE_PERM: 329377041420SYotam Gigi br_mc_router_state_change(br, val == MDB_RTR_TYPE_PERM); 32940909e117SHerbert Xu del_timer(&br->multicast_router_timer); 329577041420SYotam Gigi br->multicast_router = val; 329677041420SYotam Gigi err = 0; 329777041420SYotam Gigi break; 32987f0aec7aSNikolay Aleksandrov case MDB_RTR_TYPE_TEMP_QUERY: 329977041420SYotam Gigi if (br->multicast_router != MDB_RTR_TYPE_TEMP_QUERY) 330077041420SYotam Gigi br_mc_router_state_change(br, false); 33010909e117SHerbert Xu br->multicast_router = val; 33020909e117SHerbert Xu err = 0; 33030909e117SHerbert Xu break; 33040909e117SHerbert Xu } 33050909e117SHerbert Xu 33060909e117SHerbert Xu spin_unlock_bh(&br->multicast_lock); 33070909e117SHerbert Xu 33080909e117SHerbert Xu return err; 33090909e117SHerbert Xu } 33100909e117SHerbert Xu 33117f0aec7aSNikolay Aleksandrov static void __del_port_router(struct net_bridge_port *p) 33127f0aec7aSNikolay Aleksandrov { 33137f0aec7aSNikolay Aleksandrov if (hlist_unhashed(&p->rlist)) 33147f0aec7aSNikolay Aleksandrov return; 33157f0aec7aSNikolay Aleksandrov hlist_del_init_rcu(&p->rlist); 33167f0aec7aSNikolay Aleksandrov br_rtr_notify(p->br->dev, p, RTM_DELMDB); 33176d549648SNogah Frankel br_port_mc_router_state_change(p, false); 3318f12e7d95SNogah Frankel 3319f12e7d95SNogah Frankel /* don't allow timer refresh */ 3320f12e7d95SNogah Frankel if (p->multicast_router == MDB_RTR_TYPE_TEMP) 3321f12e7d95SNogah Frankel p->multicast_router = MDB_RTR_TYPE_TEMP_QUERY; 33227f0aec7aSNikolay Aleksandrov } 33237f0aec7aSNikolay Aleksandrov 33240909e117SHerbert Xu int br_multicast_set_port_router(struct net_bridge_port *p, unsigned long val) 33250909e117SHerbert Xu { 33260909e117SHerbert Xu struct net_bridge *br = p->br; 3327a55d8246SNikolay Aleksandrov unsigned long now = jiffies; 33286ae4ae8eSLinus Lüssing int err = -EINVAL; 33290909e117SHerbert Xu 33300909e117SHerbert Xu spin_lock(&br->multicast_lock); 33314950cfd1SNikolay Aleksandrov if (p->multicast_router == val) { 3332a55d8246SNikolay Aleksandrov /* Refresh the temp router port timer */ 3333a55d8246SNikolay Aleksandrov if (p->multicast_router == MDB_RTR_TYPE_TEMP) 3334a55d8246SNikolay Aleksandrov mod_timer(&p->multicast_router_timer, 3335a55d8246SNikolay Aleksandrov now + br->multicast_querier_interval); 33364950cfd1SNikolay Aleksandrov err = 0; 33374950cfd1SNikolay Aleksandrov goto unlock; 33384950cfd1SNikolay Aleksandrov } 33390909e117SHerbert Xu switch (val) { 33407f0aec7aSNikolay Aleksandrov case MDB_RTR_TYPE_DISABLED: 33417f0aec7aSNikolay Aleksandrov p->multicast_router = MDB_RTR_TYPE_DISABLED; 33427f0aec7aSNikolay Aleksandrov __del_port_router(p); 33430909e117SHerbert Xu del_timer(&p->multicast_router_timer); 33440909e117SHerbert Xu break; 33457f0aec7aSNikolay Aleksandrov case MDB_RTR_TYPE_TEMP_QUERY: 33467f0aec7aSNikolay Aleksandrov p->multicast_router = MDB_RTR_TYPE_TEMP_QUERY; 33477f0aec7aSNikolay Aleksandrov __del_port_router(p); 33487f0aec7aSNikolay Aleksandrov break; 33497f0aec7aSNikolay Aleksandrov case MDB_RTR_TYPE_PERM: 33507f0aec7aSNikolay Aleksandrov p->multicast_router = MDB_RTR_TYPE_PERM; 33517f0aec7aSNikolay Aleksandrov del_timer(&p->multicast_router_timer); 33520909e117SHerbert Xu br_multicast_add_router(br, p); 33530909e117SHerbert Xu break; 3354a55d8246SNikolay Aleksandrov case MDB_RTR_TYPE_TEMP: 3355a55d8246SNikolay Aleksandrov p->multicast_router = MDB_RTR_TYPE_TEMP; 3356a55d8246SNikolay Aleksandrov br_multicast_mark_router(br, p); 3357a55d8246SNikolay Aleksandrov break; 33587f0aec7aSNikolay Aleksandrov default: 33597f0aec7aSNikolay Aleksandrov goto unlock; 33600909e117SHerbert Xu } 33617f0aec7aSNikolay Aleksandrov err = 0; 33627f0aec7aSNikolay Aleksandrov unlock: 33630909e117SHerbert Xu spin_unlock(&br->multicast_lock); 33640909e117SHerbert Xu 33650909e117SHerbert Xu return err; 33660909e117SHerbert Xu } 3367561f1103SHerbert Xu 3368cc0fdd80SLinus Lüssing static void br_multicast_start_querier(struct net_bridge *br, 336990010b36SLinus Lüssing struct bridge_mcast_own_query *query) 3370561f1103SHerbert Xu { 3371561f1103SHerbert Xu struct net_bridge_port *port; 337274857216SHerbert Xu 3373cc0fdd80SLinus Lüssing __br_multicast_open(br, query); 337474857216SHerbert Xu 3375c5b493ceSNikolay Aleksandrov rcu_read_lock(); 3376c5b493ceSNikolay Aleksandrov list_for_each_entry_rcu(port, &br->port_list, list) { 337774857216SHerbert Xu if (port->state == BR_STATE_DISABLED || 337874857216SHerbert Xu port->state == BR_STATE_BLOCKING) 337974857216SHerbert Xu continue; 338074857216SHerbert Xu 338190010b36SLinus Lüssing if (query == &br->ip4_own_query) 338290010b36SLinus Lüssing br_multicast_enable(&port->ip4_own_query); 3383cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 3384cc0fdd80SLinus Lüssing else 338590010b36SLinus Lüssing br_multicast_enable(&port->ip6_own_query); 3386cc0fdd80SLinus Lüssing #endif 338774857216SHerbert Xu } 3388c5b493ceSNikolay Aleksandrov rcu_read_unlock(); 338974857216SHerbert Xu } 339074857216SHerbert Xu 339174857216SHerbert Xu int br_multicast_toggle(struct net_bridge *br, unsigned long val) 339274857216SHerbert Xu { 33937cb3f921SNikolay Aleksandrov struct net_bridge_port *port; 3394561f1103SHerbert Xu 3395ef5e0d82SAndrey Vagin spin_lock_bh(&br->multicast_lock); 339613cefad2SNikolay Aleksandrov if (!!br_opt_get(br, BROPT_MULTICAST_ENABLED) == !!val) 3397561f1103SHerbert Xu goto unlock; 3398561f1103SHerbert Xu 339913cefad2SNikolay Aleksandrov br_mc_disabled_update(br->dev, val); 340013cefad2SNikolay Aleksandrov br_opt_toggle(br, BROPT_MULTICAST_ENABLED, !!val); 34014effd28cSLinus Lüssing if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) { 34024effd28cSLinus Lüssing br_multicast_leave_snoopers(br); 3403561f1103SHerbert Xu goto unlock; 34044effd28cSLinus Lüssing } 3405561f1103SHerbert Xu 34063a7fda06SHerbert Xu if (!netif_running(br->dev)) 34073a7fda06SHerbert Xu goto unlock; 34083a7fda06SHerbert Xu 34097cb3f921SNikolay Aleksandrov br_multicast_open(br); 34107cb3f921SNikolay Aleksandrov list_for_each_entry(port, &br->port_list, list) 34117cb3f921SNikolay Aleksandrov __br_multicast_enable_port(port); 3412561f1103SHerbert Xu 3413561f1103SHerbert Xu unlock: 3414ef5e0d82SAndrey Vagin spin_unlock_bh(&br->multicast_lock); 3415561f1103SHerbert Xu 3416a26d94bfSYueHaibing return 0; 3417561f1103SHerbert Xu } 3418b195167fSHerbert Xu 34199341b988SIdo Schimmel bool br_multicast_enabled(const struct net_device *dev) 34209341b988SIdo Schimmel { 34219341b988SIdo Schimmel struct net_bridge *br = netdev_priv(dev); 34229341b988SIdo Schimmel 342313cefad2SNikolay Aleksandrov return !!br_opt_get(br, BROPT_MULTICAST_ENABLED); 34249341b988SIdo Schimmel } 34259341b988SIdo Schimmel EXPORT_SYMBOL_GPL(br_multicast_enabled); 34269341b988SIdo Schimmel 34270912bda4SYotam Gigi bool br_multicast_router(const struct net_device *dev) 34280912bda4SYotam Gigi { 34290912bda4SYotam Gigi struct net_bridge *br = netdev_priv(dev); 34300912bda4SYotam Gigi bool is_router; 34310912bda4SYotam Gigi 34320912bda4SYotam Gigi spin_lock_bh(&br->multicast_lock); 34330912bda4SYotam Gigi is_router = br_multicast_is_router(br); 34340912bda4SYotam Gigi spin_unlock_bh(&br->multicast_lock); 34350912bda4SYotam Gigi return is_router; 34360912bda4SYotam Gigi } 34370912bda4SYotam Gigi EXPORT_SYMBOL_GPL(br_multicast_router); 34380912bda4SYotam Gigi 3439c5c23260SHerbert Xu int br_multicast_set_querier(struct net_bridge *br, unsigned long val) 3440c5c23260SHerbert Xu { 3441b00589afSLinus Lüssing unsigned long max_delay; 3442b00589afSLinus Lüssing 3443c5c23260SHerbert Xu val = !!val; 3444c5c23260SHerbert Xu 3445c5c23260SHerbert Xu spin_lock_bh(&br->multicast_lock); 3446675779adSNikolay Aleksandrov if (br_opt_get(br, BROPT_MULTICAST_QUERIER) == val) 3447c5c23260SHerbert Xu goto unlock; 3448c5c23260SHerbert Xu 3449675779adSNikolay Aleksandrov br_opt_toggle(br, BROPT_MULTICAST_QUERIER, !!val); 3450b00589afSLinus Lüssing if (!val) 3451b00589afSLinus Lüssing goto unlock; 3452b00589afSLinus Lüssing 3453b00589afSLinus Lüssing max_delay = br->multicast_query_response_interval; 3454b00589afSLinus Lüssing 345590010b36SLinus Lüssing if (!timer_pending(&br->ip4_other_query.timer)) 345690010b36SLinus Lüssing br->ip4_other_query.delay_time = jiffies + max_delay; 3457cc0fdd80SLinus Lüssing 345890010b36SLinus Lüssing br_multicast_start_querier(br, &br->ip4_own_query); 3459cc0fdd80SLinus Lüssing 3460cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 346190010b36SLinus Lüssing if (!timer_pending(&br->ip6_other_query.timer)) 346290010b36SLinus Lüssing br->ip6_other_query.delay_time = jiffies + max_delay; 3463cc0fdd80SLinus Lüssing 346490010b36SLinus Lüssing br_multicast_start_querier(br, &br->ip6_own_query); 3465cc0fdd80SLinus Lüssing #endif 3466c5c23260SHerbert Xu 3467c5c23260SHerbert Xu unlock: 3468c5c23260SHerbert Xu spin_unlock_bh(&br->multicast_lock); 3469c5c23260SHerbert Xu 3470c5c23260SHerbert Xu return 0; 3471c5c23260SHerbert Xu } 3472c5c23260SHerbert Xu 34735e923585SNikolay Aleksandrov int br_multicast_set_igmp_version(struct net_bridge *br, unsigned long val) 34745e923585SNikolay Aleksandrov { 34755e923585SNikolay Aleksandrov /* Currently we support only version 2 and 3 */ 34765e923585SNikolay Aleksandrov switch (val) { 34775e923585SNikolay Aleksandrov case 2: 34785e923585SNikolay Aleksandrov case 3: 34795e923585SNikolay Aleksandrov break; 34805e923585SNikolay Aleksandrov default: 34815e923585SNikolay Aleksandrov return -EINVAL; 34825e923585SNikolay Aleksandrov } 34835e923585SNikolay Aleksandrov 34845e923585SNikolay Aleksandrov spin_lock_bh(&br->multicast_lock); 34855e923585SNikolay Aleksandrov br->multicast_igmp_version = val; 34865e923585SNikolay Aleksandrov spin_unlock_bh(&br->multicast_lock); 34875e923585SNikolay Aleksandrov 34885e923585SNikolay Aleksandrov return 0; 34895e923585SNikolay Aleksandrov } 34905e923585SNikolay Aleksandrov 3491aa2ae3e7SNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6) 3492aa2ae3e7SNikolay Aleksandrov int br_multicast_set_mld_version(struct net_bridge *br, unsigned long val) 3493aa2ae3e7SNikolay Aleksandrov { 3494aa2ae3e7SNikolay Aleksandrov /* Currently we support version 1 and 2 */ 3495aa2ae3e7SNikolay Aleksandrov switch (val) { 3496aa2ae3e7SNikolay Aleksandrov case 1: 3497aa2ae3e7SNikolay Aleksandrov case 2: 3498aa2ae3e7SNikolay Aleksandrov break; 3499aa2ae3e7SNikolay Aleksandrov default: 3500aa2ae3e7SNikolay Aleksandrov return -EINVAL; 3501aa2ae3e7SNikolay Aleksandrov } 3502aa2ae3e7SNikolay Aleksandrov 3503aa2ae3e7SNikolay Aleksandrov spin_lock_bh(&br->multicast_lock); 3504aa2ae3e7SNikolay Aleksandrov br->multicast_mld_version = val; 3505aa2ae3e7SNikolay Aleksandrov spin_unlock_bh(&br->multicast_lock); 3506aa2ae3e7SNikolay Aleksandrov 3507aa2ae3e7SNikolay Aleksandrov return 0; 3508aa2ae3e7SNikolay Aleksandrov } 3509aa2ae3e7SNikolay Aleksandrov #endif 3510aa2ae3e7SNikolay Aleksandrov 351107f8ac4aSLinus Lüssing /** 351207f8ac4aSLinus Lüssing * br_multicast_list_adjacent - Returns snooped multicast addresses 351307f8ac4aSLinus Lüssing * @dev: The bridge port adjacent to which to retrieve addresses 351407f8ac4aSLinus Lüssing * @br_ip_list: The list to store found, snooped multicast IP addresses in 351507f8ac4aSLinus Lüssing * 351607f8ac4aSLinus Lüssing * Creates a list of IP addresses (struct br_ip_list) sensed by the multicast 351707f8ac4aSLinus Lüssing * snooping feature on all bridge ports of dev's bridge device, excluding 351807f8ac4aSLinus Lüssing * the addresses from dev itself. 351907f8ac4aSLinus Lüssing * 352007f8ac4aSLinus Lüssing * Returns the number of items added to br_ip_list. 352107f8ac4aSLinus Lüssing * 352207f8ac4aSLinus Lüssing * Notes: 352307f8ac4aSLinus Lüssing * - br_ip_list needs to be initialized by caller 352407f8ac4aSLinus Lüssing * - br_ip_list might contain duplicates in the end 352507f8ac4aSLinus Lüssing * (needs to be taken care of by caller) 352607f8ac4aSLinus Lüssing * - br_ip_list needs to be freed by caller 352707f8ac4aSLinus Lüssing */ 352807f8ac4aSLinus Lüssing int br_multicast_list_adjacent(struct net_device *dev, 352907f8ac4aSLinus Lüssing struct list_head *br_ip_list) 353007f8ac4aSLinus Lüssing { 353107f8ac4aSLinus Lüssing struct net_bridge *br; 353207f8ac4aSLinus Lüssing struct net_bridge_port *port; 353307f8ac4aSLinus Lüssing struct net_bridge_port_group *group; 353407f8ac4aSLinus Lüssing struct br_ip_list *entry; 353507f8ac4aSLinus Lüssing int count = 0; 353607f8ac4aSLinus Lüssing 353707f8ac4aSLinus Lüssing rcu_read_lock(); 353835f861e3SJulian Wiedmann if (!br_ip_list || !netif_is_bridge_port(dev)) 353907f8ac4aSLinus Lüssing goto unlock; 354007f8ac4aSLinus Lüssing 354107f8ac4aSLinus Lüssing port = br_port_get_rcu(dev); 354207f8ac4aSLinus Lüssing if (!port || !port->br) 354307f8ac4aSLinus Lüssing goto unlock; 354407f8ac4aSLinus Lüssing 354507f8ac4aSLinus Lüssing br = port->br; 354607f8ac4aSLinus Lüssing 354707f8ac4aSLinus Lüssing list_for_each_entry_rcu(port, &br->port_list, list) { 354807f8ac4aSLinus Lüssing if (!port->dev || port->dev == dev) 354907f8ac4aSLinus Lüssing continue; 355007f8ac4aSLinus Lüssing 355107f8ac4aSLinus Lüssing hlist_for_each_entry_rcu(group, &port->mglist, mglist) { 355207f8ac4aSLinus Lüssing entry = kmalloc(sizeof(*entry), GFP_ATOMIC); 355307f8ac4aSLinus Lüssing if (!entry) 355407f8ac4aSLinus Lüssing goto unlock; 355507f8ac4aSLinus Lüssing 3556085b53c8SNikolay Aleksandrov entry->addr = group->key.addr; 355707f8ac4aSLinus Lüssing list_add(&entry->list, br_ip_list); 355807f8ac4aSLinus Lüssing count++; 355907f8ac4aSLinus Lüssing } 356007f8ac4aSLinus Lüssing } 356107f8ac4aSLinus Lüssing 356207f8ac4aSLinus Lüssing unlock: 356307f8ac4aSLinus Lüssing rcu_read_unlock(); 356407f8ac4aSLinus Lüssing return count; 356507f8ac4aSLinus Lüssing } 356607f8ac4aSLinus Lüssing EXPORT_SYMBOL_GPL(br_multicast_list_adjacent); 35672cd41431SLinus Lüssing 35682cd41431SLinus Lüssing /** 3569c34963e2SLinus Lüssing * br_multicast_has_querier_anywhere - Checks for a querier on a bridge 3570c34963e2SLinus Lüssing * @dev: The bridge port providing the bridge on which to check for a querier 3571c34963e2SLinus Lüssing * @proto: The protocol family to check for: IGMP -> ETH_P_IP, MLD -> ETH_P_IPV6 3572c34963e2SLinus Lüssing * 3573c34963e2SLinus Lüssing * Checks whether the given interface has a bridge on top and if so returns 3574c34963e2SLinus Lüssing * true if a valid querier exists anywhere on the bridged link layer. 3575c34963e2SLinus Lüssing * Otherwise returns false. 3576c34963e2SLinus Lüssing */ 3577c34963e2SLinus Lüssing bool br_multicast_has_querier_anywhere(struct net_device *dev, int proto) 3578c34963e2SLinus Lüssing { 3579c34963e2SLinus Lüssing struct net_bridge *br; 3580c34963e2SLinus Lüssing struct net_bridge_port *port; 3581c34963e2SLinus Lüssing struct ethhdr eth; 3582c34963e2SLinus Lüssing bool ret = false; 3583c34963e2SLinus Lüssing 3584c34963e2SLinus Lüssing rcu_read_lock(); 358535f861e3SJulian Wiedmann if (!netif_is_bridge_port(dev)) 3586c34963e2SLinus Lüssing goto unlock; 3587c34963e2SLinus Lüssing 3588c34963e2SLinus Lüssing port = br_port_get_rcu(dev); 3589c34963e2SLinus Lüssing if (!port || !port->br) 3590c34963e2SLinus Lüssing goto unlock; 3591c34963e2SLinus Lüssing 3592c34963e2SLinus Lüssing br = port->br; 3593c34963e2SLinus Lüssing 3594c34963e2SLinus Lüssing memset(ð, 0, sizeof(eth)); 3595c34963e2SLinus Lüssing eth.h_proto = htons(proto); 3596c34963e2SLinus Lüssing 3597c34963e2SLinus Lüssing ret = br_multicast_querier_exists(br, ð); 3598c34963e2SLinus Lüssing 3599c34963e2SLinus Lüssing unlock: 3600c34963e2SLinus Lüssing rcu_read_unlock(); 3601c34963e2SLinus Lüssing return ret; 3602c34963e2SLinus Lüssing } 3603c34963e2SLinus Lüssing EXPORT_SYMBOL_GPL(br_multicast_has_querier_anywhere); 3604c34963e2SLinus Lüssing 3605c34963e2SLinus Lüssing /** 36062cd41431SLinus Lüssing * br_multicast_has_querier_adjacent - Checks for a querier behind a bridge port 36072cd41431SLinus Lüssing * @dev: The bridge port adjacent to which to check for a querier 36082cd41431SLinus Lüssing * @proto: The protocol family to check for: IGMP -> ETH_P_IP, MLD -> ETH_P_IPV6 36092cd41431SLinus Lüssing * 36102cd41431SLinus Lüssing * Checks whether the given interface has a bridge on top and if so returns 36112cd41431SLinus Lüssing * true if a selected querier is behind one of the other ports of this 36122cd41431SLinus Lüssing * bridge. Otherwise returns false. 36132cd41431SLinus Lüssing */ 36142cd41431SLinus Lüssing bool br_multicast_has_querier_adjacent(struct net_device *dev, int proto) 36152cd41431SLinus Lüssing { 36162cd41431SLinus Lüssing struct net_bridge *br; 36172cd41431SLinus Lüssing struct net_bridge_port *port; 36182cd41431SLinus Lüssing bool ret = false; 36192cd41431SLinus Lüssing 36202cd41431SLinus Lüssing rcu_read_lock(); 362135f861e3SJulian Wiedmann if (!netif_is_bridge_port(dev)) 36222cd41431SLinus Lüssing goto unlock; 36232cd41431SLinus Lüssing 36242cd41431SLinus Lüssing port = br_port_get_rcu(dev); 36252cd41431SLinus Lüssing if (!port || !port->br) 36262cd41431SLinus Lüssing goto unlock; 36272cd41431SLinus Lüssing 36282cd41431SLinus Lüssing br = port->br; 36292cd41431SLinus Lüssing 36302cd41431SLinus Lüssing switch (proto) { 36312cd41431SLinus Lüssing case ETH_P_IP: 36322cd41431SLinus Lüssing if (!timer_pending(&br->ip4_other_query.timer) || 36332cd41431SLinus Lüssing rcu_dereference(br->ip4_querier.port) == port) 36342cd41431SLinus Lüssing goto unlock; 36352cd41431SLinus Lüssing break; 36363993c4e1SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 36372cd41431SLinus Lüssing case ETH_P_IPV6: 36382cd41431SLinus Lüssing if (!timer_pending(&br->ip6_other_query.timer) || 36392cd41431SLinus Lüssing rcu_dereference(br->ip6_querier.port) == port) 36402cd41431SLinus Lüssing goto unlock; 36412cd41431SLinus Lüssing break; 36423993c4e1SLinus Lüssing #endif 36432cd41431SLinus Lüssing default: 36442cd41431SLinus Lüssing goto unlock; 36452cd41431SLinus Lüssing } 36462cd41431SLinus Lüssing 36472cd41431SLinus Lüssing ret = true; 36482cd41431SLinus Lüssing unlock: 36492cd41431SLinus Lüssing rcu_read_unlock(); 36502cd41431SLinus Lüssing return ret; 36512cd41431SLinus Lüssing } 36522cd41431SLinus Lüssing EXPORT_SYMBOL_GPL(br_multicast_has_querier_adjacent); 36531080ab95SNikolay Aleksandrov 36541080ab95SNikolay Aleksandrov static void br_mcast_stats_add(struct bridge_mcast_stats __percpu *stats, 3655a65056ecSNikolay Aleksandrov const struct sk_buff *skb, u8 type, u8 dir) 36561080ab95SNikolay Aleksandrov { 36571080ab95SNikolay Aleksandrov struct bridge_mcast_stats *pstats = this_cpu_ptr(stats); 3658a65056ecSNikolay Aleksandrov __be16 proto = skb->protocol; 3659a65056ecSNikolay Aleksandrov unsigned int t_len; 36601080ab95SNikolay Aleksandrov 36611080ab95SNikolay Aleksandrov u64_stats_update_begin(&pstats->syncp); 36621080ab95SNikolay Aleksandrov switch (proto) { 36631080ab95SNikolay Aleksandrov case htons(ETH_P_IP): 3664a65056ecSNikolay Aleksandrov t_len = ntohs(ip_hdr(skb)->tot_len) - ip_hdrlen(skb); 36651080ab95SNikolay Aleksandrov switch (type) { 36661080ab95SNikolay Aleksandrov case IGMP_HOST_MEMBERSHIP_REPORT: 36671080ab95SNikolay Aleksandrov pstats->mstats.igmp_v1reports[dir]++; 36681080ab95SNikolay Aleksandrov break; 36691080ab95SNikolay Aleksandrov case IGMPV2_HOST_MEMBERSHIP_REPORT: 36701080ab95SNikolay Aleksandrov pstats->mstats.igmp_v2reports[dir]++; 36711080ab95SNikolay Aleksandrov break; 36721080ab95SNikolay Aleksandrov case IGMPV3_HOST_MEMBERSHIP_REPORT: 36731080ab95SNikolay Aleksandrov pstats->mstats.igmp_v3reports[dir]++; 36741080ab95SNikolay Aleksandrov break; 36751080ab95SNikolay Aleksandrov case IGMP_HOST_MEMBERSHIP_QUERY: 3676a65056ecSNikolay Aleksandrov if (t_len != sizeof(struct igmphdr)) { 3677a65056ecSNikolay Aleksandrov pstats->mstats.igmp_v3queries[dir]++; 3678a65056ecSNikolay Aleksandrov } else { 3679a65056ecSNikolay Aleksandrov unsigned int offset = skb_transport_offset(skb); 3680a65056ecSNikolay Aleksandrov struct igmphdr *ih, _ihdr; 3681a65056ecSNikolay Aleksandrov 3682a65056ecSNikolay Aleksandrov ih = skb_header_pointer(skb, offset, 3683a65056ecSNikolay Aleksandrov sizeof(_ihdr), &_ihdr); 3684a65056ecSNikolay Aleksandrov if (!ih) 3685a65056ecSNikolay Aleksandrov break; 3686a65056ecSNikolay Aleksandrov if (!ih->code) 3687a65056ecSNikolay Aleksandrov pstats->mstats.igmp_v1queries[dir]++; 3688a65056ecSNikolay Aleksandrov else 3689a65056ecSNikolay Aleksandrov pstats->mstats.igmp_v2queries[dir]++; 3690a65056ecSNikolay Aleksandrov } 36911080ab95SNikolay Aleksandrov break; 36921080ab95SNikolay Aleksandrov case IGMP_HOST_LEAVE_MESSAGE: 36931080ab95SNikolay Aleksandrov pstats->mstats.igmp_leaves[dir]++; 36941080ab95SNikolay Aleksandrov break; 36951080ab95SNikolay Aleksandrov } 36961080ab95SNikolay Aleksandrov break; 36971080ab95SNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6) 36981080ab95SNikolay Aleksandrov case htons(ETH_P_IPV6): 3699a65056ecSNikolay Aleksandrov t_len = ntohs(ipv6_hdr(skb)->payload_len) + 3700a65056ecSNikolay Aleksandrov sizeof(struct ipv6hdr); 3701a65056ecSNikolay Aleksandrov t_len -= skb_network_header_len(skb); 37021080ab95SNikolay Aleksandrov switch (type) { 37031080ab95SNikolay Aleksandrov case ICMPV6_MGM_REPORT: 37041080ab95SNikolay Aleksandrov pstats->mstats.mld_v1reports[dir]++; 37051080ab95SNikolay Aleksandrov break; 37061080ab95SNikolay Aleksandrov case ICMPV6_MLD2_REPORT: 37071080ab95SNikolay Aleksandrov pstats->mstats.mld_v2reports[dir]++; 37081080ab95SNikolay Aleksandrov break; 37091080ab95SNikolay Aleksandrov case ICMPV6_MGM_QUERY: 3710a65056ecSNikolay Aleksandrov if (t_len != sizeof(struct mld_msg)) 3711a65056ecSNikolay Aleksandrov pstats->mstats.mld_v2queries[dir]++; 3712a65056ecSNikolay Aleksandrov else 3713a65056ecSNikolay Aleksandrov pstats->mstats.mld_v1queries[dir]++; 37141080ab95SNikolay Aleksandrov break; 37151080ab95SNikolay Aleksandrov case ICMPV6_MGM_REDUCTION: 37161080ab95SNikolay Aleksandrov pstats->mstats.mld_leaves[dir]++; 37171080ab95SNikolay Aleksandrov break; 37181080ab95SNikolay Aleksandrov } 37191080ab95SNikolay Aleksandrov break; 37201080ab95SNikolay Aleksandrov #endif /* CONFIG_IPV6 */ 37211080ab95SNikolay Aleksandrov } 37221080ab95SNikolay Aleksandrov u64_stats_update_end(&pstats->syncp); 37231080ab95SNikolay Aleksandrov } 37241080ab95SNikolay Aleksandrov 37251080ab95SNikolay Aleksandrov void br_multicast_count(struct net_bridge *br, const struct net_bridge_port *p, 3726a65056ecSNikolay Aleksandrov const struct sk_buff *skb, u8 type, u8 dir) 37271080ab95SNikolay Aleksandrov { 37281080ab95SNikolay Aleksandrov struct bridge_mcast_stats __percpu *stats; 37291080ab95SNikolay Aleksandrov 37301080ab95SNikolay Aleksandrov /* if multicast_disabled is true then igmp type can't be set */ 3731675779adSNikolay Aleksandrov if (!type || !br_opt_get(br, BROPT_MULTICAST_STATS_ENABLED)) 37321080ab95SNikolay Aleksandrov return; 37331080ab95SNikolay Aleksandrov 37341080ab95SNikolay Aleksandrov if (p) 37351080ab95SNikolay Aleksandrov stats = p->mcast_stats; 37361080ab95SNikolay Aleksandrov else 37371080ab95SNikolay Aleksandrov stats = br->mcast_stats; 37381080ab95SNikolay Aleksandrov if (WARN_ON(!stats)) 37391080ab95SNikolay Aleksandrov return; 37401080ab95SNikolay Aleksandrov 3741a65056ecSNikolay Aleksandrov br_mcast_stats_add(stats, skb, type, dir); 37421080ab95SNikolay Aleksandrov } 37431080ab95SNikolay Aleksandrov 37441080ab95SNikolay Aleksandrov int br_multicast_init_stats(struct net_bridge *br) 37451080ab95SNikolay Aleksandrov { 37461080ab95SNikolay Aleksandrov br->mcast_stats = netdev_alloc_pcpu_stats(struct bridge_mcast_stats); 37471080ab95SNikolay Aleksandrov if (!br->mcast_stats) 37481080ab95SNikolay Aleksandrov return -ENOMEM; 37491080ab95SNikolay Aleksandrov 37501080ab95SNikolay Aleksandrov return 0; 37511080ab95SNikolay Aleksandrov } 37521080ab95SNikolay Aleksandrov 3753b6fe0440SIdo Schimmel void br_multicast_uninit_stats(struct net_bridge *br) 3754b6fe0440SIdo Schimmel { 3755b6fe0440SIdo Schimmel free_percpu(br->mcast_stats); 3756b6fe0440SIdo Schimmel } 3757b6fe0440SIdo Schimmel 3758b3b6a84cSArnd Bergmann /* noinline for https://bugs.llvm.org/show_bug.cgi?id=45802#c9 */ 3759b3b6a84cSArnd Bergmann static noinline_for_stack void mcast_stats_add_dir(u64 *dst, u64 *src) 37601080ab95SNikolay Aleksandrov { 37611080ab95SNikolay Aleksandrov dst[BR_MCAST_DIR_RX] += src[BR_MCAST_DIR_RX]; 37621080ab95SNikolay Aleksandrov dst[BR_MCAST_DIR_TX] += src[BR_MCAST_DIR_TX]; 37631080ab95SNikolay Aleksandrov } 37641080ab95SNikolay Aleksandrov 37651080ab95SNikolay Aleksandrov void br_multicast_get_stats(const struct net_bridge *br, 37661080ab95SNikolay Aleksandrov const struct net_bridge_port *p, 37671080ab95SNikolay Aleksandrov struct br_mcast_stats *dest) 37681080ab95SNikolay Aleksandrov { 37691080ab95SNikolay Aleksandrov struct bridge_mcast_stats __percpu *stats; 37701080ab95SNikolay Aleksandrov struct br_mcast_stats tdst; 37711080ab95SNikolay Aleksandrov int i; 37721080ab95SNikolay Aleksandrov 37731080ab95SNikolay Aleksandrov memset(dest, 0, sizeof(*dest)); 37741080ab95SNikolay Aleksandrov if (p) 37751080ab95SNikolay Aleksandrov stats = p->mcast_stats; 37761080ab95SNikolay Aleksandrov else 37771080ab95SNikolay Aleksandrov stats = br->mcast_stats; 37781080ab95SNikolay Aleksandrov if (WARN_ON(!stats)) 37791080ab95SNikolay Aleksandrov return; 37801080ab95SNikolay Aleksandrov 37811080ab95SNikolay Aleksandrov memset(&tdst, 0, sizeof(tdst)); 37821080ab95SNikolay Aleksandrov for_each_possible_cpu(i) { 37831080ab95SNikolay Aleksandrov struct bridge_mcast_stats *cpu_stats = per_cpu_ptr(stats, i); 37841080ab95SNikolay Aleksandrov struct br_mcast_stats temp; 37851080ab95SNikolay Aleksandrov unsigned int start; 37861080ab95SNikolay Aleksandrov 37871080ab95SNikolay Aleksandrov do { 37881080ab95SNikolay Aleksandrov start = u64_stats_fetch_begin_irq(&cpu_stats->syncp); 37891080ab95SNikolay Aleksandrov memcpy(&temp, &cpu_stats->mstats, sizeof(temp)); 37901080ab95SNikolay Aleksandrov } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start)); 37911080ab95SNikolay Aleksandrov 3792a65056ecSNikolay Aleksandrov mcast_stats_add_dir(tdst.igmp_v1queries, temp.igmp_v1queries); 3793a65056ecSNikolay Aleksandrov mcast_stats_add_dir(tdst.igmp_v2queries, temp.igmp_v2queries); 3794a65056ecSNikolay Aleksandrov mcast_stats_add_dir(tdst.igmp_v3queries, temp.igmp_v3queries); 37951080ab95SNikolay Aleksandrov mcast_stats_add_dir(tdst.igmp_leaves, temp.igmp_leaves); 37961080ab95SNikolay Aleksandrov mcast_stats_add_dir(tdst.igmp_v1reports, temp.igmp_v1reports); 37971080ab95SNikolay Aleksandrov mcast_stats_add_dir(tdst.igmp_v2reports, temp.igmp_v2reports); 37981080ab95SNikolay Aleksandrov mcast_stats_add_dir(tdst.igmp_v3reports, temp.igmp_v3reports); 37991080ab95SNikolay Aleksandrov tdst.igmp_parse_errors += temp.igmp_parse_errors; 38001080ab95SNikolay Aleksandrov 3801a65056ecSNikolay Aleksandrov mcast_stats_add_dir(tdst.mld_v1queries, temp.mld_v1queries); 3802a65056ecSNikolay Aleksandrov mcast_stats_add_dir(tdst.mld_v2queries, temp.mld_v2queries); 38031080ab95SNikolay Aleksandrov mcast_stats_add_dir(tdst.mld_leaves, temp.mld_leaves); 38041080ab95SNikolay Aleksandrov mcast_stats_add_dir(tdst.mld_v1reports, temp.mld_v1reports); 38051080ab95SNikolay Aleksandrov mcast_stats_add_dir(tdst.mld_v2reports, temp.mld_v2reports); 38061080ab95SNikolay Aleksandrov tdst.mld_parse_errors += temp.mld_parse_errors; 38071080ab95SNikolay Aleksandrov } 38081080ab95SNikolay Aleksandrov memcpy(dest, &tdst, sizeof(*dest)); 38091080ab95SNikolay Aleksandrov } 381019e3a9c9SNikolay Aleksandrov 381119e3a9c9SNikolay Aleksandrov int br_mdb_hash_init(struct net_bridge *br) 381219e3a9c9SNikolay Aleksandrov { 3813085b53c8SNikolay Aleksandrov int err; 3814085b53c8SNikolay Aleksandrov 3815085b53c8SNikolay Aleksandrov err = rhashtable_init(&br->sg_port_tbl, &br_sg_port_rht_params); 3816085b53c8SNikolay Aleksandrov if (err) 3817085b53c8SNikolay Aleksandrov return err; 3818085b53c8SNikolay Aleksandrov 3819085b53c8SNikolay Aleksandrov err = rhashtable_init(&br->mdb_hash_tbl, &br_mdb_rht_params); 3820085b53c8SNikolay Aleksandrov if (err) { 3821085b53c8SNikolay Aleksandrov rhashtable_destroy(&br->sg_port_tbl); 3822085b53c8SNikolay Aleksandrov return err; 3823085b53c8SNikolay Aleksandrov } 3824085b53c8SNikolay Aleksandrov 3825085b53c8SNikolay Aleksandrov return 0; 382619e3a9c9SNikolay Aleksandrov } 382719e3a9c9SNikolay Aleksandrov 382819e3a9c9SNikolay Aleksandrov void br_mdb_hash_fini(struct net_bridge *br) 382919e3a9c9SNikolay Aleksandrov { 3830085b53c8SNikolay Aleksandrov rhashtable_destroy(&br->sg_port_tbl); 383119e3a9c9SNikolay Aleksandrov rhashtable_destroy(&br->mdb_hash_tbl); 383219e3a9c9SNikolay Aleksandrov } 3833