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" 368f07b831SNikolay Aleksandrov #include "br_private_mcast_eht.h" 37eb1d1641SHerbert Xu 3819e3a9c9SNikolay Aleksandrov static const struct rhashtable_params br_mdb_rht_params = { 3919e3a9c9SNikolay Aleksandrov .head_offset = offsetof(struct net_bridge_mdb_entry, rhnode), 4019e3a9c9SNikolay Aleksandrov .key_offset = offsetof(struct net_bridge_mdb_entry, addr), 4119e3a9c9SNikolay Aleksandrov .key_len = sizeof(struct br_ip), 4219e3a9c9SNikolay Aleksandrov .automatic_shrinking = true, 4319e3a9c9SNikolay Aleksandrov }; 4419e3a9c9SNikolay Aleksandrov 45085b53c8SNikolay Aleksandrov static const struct rhashtable_params br_sg_port_rht_params = { 46085b53c8SNikolay Aleksandrov .head_offset = offsetof(struct net_bridge_port_group, rhnode), 47085b53c8SNikolay Aleksandrov .key_offset = offsetof(struct net_bridge_port_group, key), 48085b53c8SNikolay Aleksandrov .key_len = sizeof(struct net_bridge_port_group_sg_key), 49085b53c8SNikolay Aleksandrov .automatic_shrinking = true, 50085b53c8SNikolay Aleksandrov }; 51085b53c8SNikolay Aleksandrov 52adc47037SNikolay Aleksandrov static void br_multicast_start_querier(struct net_bridge_mcast *brmctx, 5390010b36SLinus Lüssing struct bridge_mcast_own_query *query); 54adc47037SNikolay Aleksandrov static void br_ip4_multicast_add_router(struct net_bridge_mcast *brmctx, 55adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx); 56adc47037SNikolay Aleksandrov static void br_ip4_multicast_leave_group(struct net_bridge_mcast *brmctx, 57adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 58bc8c20acSSatish Ashok __be32 group, 596db6f0eaSFelix Fietkau __u16 vid, 606db6f0eaSFelix Fietkau const unsigned char *src); 6142c11ccfSNikolay Aleksandrov static void br_multicast_port_group_rexmit(struct timer_list *t); 626db6f0eaSFelix Fietkau 63ed2d3597SLinus Lüssing static void 64adc47037SNikolay Aleksandrov br_multicast_rport_del_notify(struct net_bridge_mcast_port *pmctx, bool deleted); 65adc47037SNikolay Aleksandrov static void br_ip6_multicast_add_router(struct net_bridge_mcast *brmctx, 66adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx); 6730515832SMatteo Croce #if IS_ENABLED(CONFIG_IPV6) 68adc47037SNikolay Aleksandrov static void br_ip6_multicast_leave_group(struct net_bridge_mcast *brmctx, 69adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 70bc8c20acSSatish Ashok const struct in6_addr *group, 716db6f0eaSFelix Fietkau __u16 vid, const unsigned char *src); 72bc8c20acSSatish Ashok #endif 73b0812368SNikolay Aleksandrov static struct net_bridge_port_group * 74adc47037SNikolay Aleksandrov __br_multicast_add_group(struct net_bridge_mcast *brmctx, 75adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 76b0812368SNikolay Aleksandrov struct br_ip *group, 77b0812368SNikolay Aleksandrov const unsigned char *src, 78b0812368SNikolay Aleksandrov u8 filter_mode, 799116ffbfSNikolay Aleksandrov bool igmpv2_mldv1, 809116ffbfSNikolay Aleksandrov bool blocked); 818266a049SNikolay Aleksandrov static void br_multicast_find_del_pg(struct net_bridge *br, 828266a049SNikolay Aleksandrov struct net_bridge_port_group *pg); 83613d61dbSNikolay Aleksandrov static void __br_multicast_stop(struct net_bridge_mcast *brmctx); 84c83b8fabSHerbert Xu 85085b53c8SNikolay Aleksandrov static struct net_bridge_port_group * 86085b53c8SNikolay Aleksandrov br_sg_port_find(struct net_bridge *br, 87085b53c8SNikolay Aleksandrov struct net_bridge_port_group_sg_key *sg_p) 88085b53c8SNikolay Aleksandrov { 89085b53c8SNikolay Aleksandrov lockdep_assert_held_once(&br->multicast_lock); 90085b53c8SNikolay Aleksandrov 91085b53c8SNikolay Aleksandrov return rhashtable_lookup_fast(&br->sg_port_tbl, sg_p, 92085b53c8SNikolay Aleksandrov br_sg_port_rht_params); 93085b53c8SNikolay Aleksandrov } 94085b53c8SNikolay Aleksandrov 9519e3a9c9SNikolay Aleksandrov static struct net_bridge_mdb_entry *br_mdb_ip_get_rcu(struct net_bridge *br, 96cfd56754SCong Wang struct br_ip *dst) 977f285fa7SHerbert Xu { 9819e3a9c9SNikolay Aleksandrov return rhashtable_lookup(&br->mdb_hash_tbl, dst, br_mdb_rht_params); 997f285fa7SHerbert Xu } 1007f285fa7SHerbert Xu 10119e3a9c9SNikolay Aleksandrov struct net_bridge_mdb_entry *br_mdb_ip_get(struct net_bridge *br, 10219e3a9c9SNikolay Aleksandrov struct br_ip *dst) 10319e3a9c9SNikolay Aleksandrov { 10419e3a9c9SNikolay Aleksandrov struct net_bridge_mdb_entry *ent; 10519e3a9c9SNikolay Aleksandrov 10619e3a9c9SNikolay Aleksandrov lockdep_assert_held_once(&br->multicast_lock); 10719e3a9c9SNikolay Aleksandrov 10819e3a9c9SNikolay Aleksandrov rcu_read_lock(); 10919e3a9c9SNikolay Aleksandrov ent = rhashtable_lookup(&br->mdb_hash_tbl, dst, br_mdb_rht_params); 11019e3a9c9SNikolay Aleksandrov rcu_read_unlock(); 11119e3a9c9SNikolay Aleksandrov 11219e3a9c9SNikolay Aleksandrov return ent; 11319e3a9c9SNikolay Aleksandrov } 11419e3a9c9SNikolay Aleksandrov 11519e3a9c9SNikolay Aleksandrov static struct net_bridge_mdb_entry *br_mdb_ip4_get(struct net_bridge *br, 11619e3a9c9SNikolay Aleksandrov __be32 dst, __u16 vid) 117eb1d1641SHerbert Xu { 1188ef2a9a5SYOSHIFUJI Hideaki struct br_ip br_dst; 1190821ec55SHerbert Xu 12019e3a9c9SNikolay Aleksandrov memset(&br_dst, 0, sizeof(br_dst)); 121eab3227bSNikolay Aleksandrov br_dst.dst.ip4 = dst; 1228ef2a9a5SYOSHIFUJI Hideaki br_dst.proto = htons(ETH_P_IP); 123b0e9a30dSVlad Yasevich br_dst.vid = vid; 1248ef2a9a5SYOSHIFUJI Hideaki 12519e3a9c9SNikolay Aleksandrov return br_mdb_ip_get(br, &br_dst); 1268ef2a9a5SYOSHIFUJI Hideaki } 1278ef2a9a5SYOSHIFUJI Hideaki 128dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 12919e3a9c9SNikolay Aleksandrov static struct net_bridge_mdb_entry *br_mdb_ip6_get(struct net_bridge *br, 13019e3a9c9SNikolay Aleksandrov const struct in6_addr *dst, 131b0e9a30dSVlad Yasevich __u16 vid) 13208b202b6SYOSHIFUJI Hideaki { 13308b202b6SYOSHIFUJI Hideaki struct br_ip br_dst; 13408b202b6SYOSHIFUJI Hideaki 13519e3a9c9SNikolay Aleksandrov memset(&br_dst, 0, sizeof(br_dst)); 136eab3227bSNikolay Aleksandrov br_dst.dst.ip6 = *dst; 13708b202b6SYOSHIFUJI Hideaki br_dst.proto = htons(ETH_P_IPV6); 138b0e9a30dSVlad Yasevich br_dst.vid = vid; 13908b202b6SYOSHIFUJI Hideaki 14019e3a9c9SNikolay Aleksandrov return br_mdb_ip_get(br, &br_dst); 14108b202b6SYOSHIFUJI Hideaki } 14208b202b6SYOSHIFUJI Hideaki #endif 14308b202b6SYOSHIFUJI Hideaki 144adc47037SNikolay Aleksandrov struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge_mcast *brmctx, 145fbca58a2SCong Wang struct sk_buff *skb, u16 vid) 146eb1d1641SHerbert Xu { 147adc47037SNikolay Aleksandrov struct net_bridge *br = brmctx->br; 1488ef2a9a5SYOSHIFUJI Hideaki struct br_ip ip; 1498ef2a9a5SYOSHIFUJI Hideaki 15013cefad2SNikolay Aleksandrov if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) 151eb1d1641SHerbert Xu return NULL; 152eb1d1641SHerbert Xu 1538ef2a9a5SYOSHIFUJI Hideaki if (BR_INPUT_SKB_CB(skb)->igmp) 1548ef2a9a5SYOSHIFUJI Hideaki return NULL; 1558ef2a9a5SYOSHIFUJI Hideaki 15619e3a9c9SNikolay Aleksandrov memset(&ip, 0, sizeof(ip)); 1578ef2a9a5SYOSHIFUJI Hideaki ip.proto = skb->protocol; 158fbca58a2SCong Wang ip.vid = vid; 1598ef2a9a5SYOSHIFUJI Hideaki 160eb1d1641SHerbert Xu switch (skb->protocol) { 161eb1d1641SHerbert Xu case htons(ETH_P_IP): 162eab3227bSNikolay Aleksandrov ip.dst.ip4 = ip_hdr(skb)->daddr; 163adc47037SNikolay Aleksandrov if (brmctx->multicast_igmp_version == 3) { 1647d07a68cSNikolay Aleksandrov struct net_bridge_mdb_entry *mdb; 1657d07a68cSNikolay Aleksandrov 1667d07a68cSNikolay Aleksandrov ip.src.ip4 = ip_hdr(skb)->saddr; 1677d07a68cSNikolay Aleksandrov mdb = br_mdb_ip_get_rcu(br, &ip); 1687d07a68cSNikolay Aleksandrov if (mdb) 1697d07a68cSNikolay Aleksandrov return mdb; 1707d07a68cSNikolay Aleksandrov ip.src.ip4 = 0; 1717d07a68cSNikolay Aleksandrov } 172eb1d1641SHerbert Xu break; 173dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 17408b202b6SYOSHIFUJI Hideaki case htons(ETH_P_IPV6): 175eab3227bSNikolay Aleksandrov ip.dst.ip6 = ipv6_hdr(skb)->daddr; 176adc47037SNikolay Aleksandrov if (brmctx->multicast_mld_version == 2) { 1777d07a68cSNikolay Aleksandrov struct net_bridge_mdb_entry *mdb; 1787d07a68cSNikolay Aleksandrov 1797d07a68cSNikolay Aleksandrov ip.src.ip6 = ipv6_hdr(skb)->saddr; 1807d07a68cSNikolay Aleksandrov mdb = br_mdb_ip_get_rcu(br, &ip); 1817d07a68cSNikolay Aleksandrov if (mdb) 1827d07a68cSNikolay Aleksandrov return mdb; 1837d07a68cSNikolay Aleksandrov memset(&ip.src.ip6, 0, sizeof(ip.src.ip6)); 1847d07a68cSNikolay Aleksandrov } 18508b202b6SYOSHIFUJI Hideaki break; 18608b202b6SYOSHIFUJI Hideaki #endif 1878ef2a9a5SYOSHIFUJI Hideaki default: 188955062b0SNikolay Aleksandrov ip.proto = 0; 189955062b0SNikolay Aleksandrov ether_addr_copy(ip.dst.mac_addr, eth_hdr(skb)->h_dest); 190eb1d1641SHerbert Xu } 191eb1d1641SHerbert Xu 19219e3a9c9SNikolay Aleksandrov return br_mdb_ip_get_rcu(br, &ip); 193eb1d1641SHerbert Xu } 194eb1d1641SHerbert Xu 19574edfd48SNikolay Aleksandrov /* IMPORTANT: this function must be used only when the contexts cannot be 19674edfd48SNikolay Aleksandrov * passed down (e.g. timer) and must be used for read-only purposes because 19774edfd48SNikolay Aleksandrov * the vlan snooping option can change, so it can return any context 19874edfd48SNikolay Aleksandrov * (non-vlan or vlan). Its initial intended purpose is to read timer values 19974edfd48SNikolay Aleksandrov * from the *current* context based on the option. At worst that could lead 20074edfd48SNikolay Aleksandrov * to inconsistent timers when the contexts are changed, i.e. src timer 20174edfd48SNikolay Aleksandrov * which needs to re-arm with a specific delay taken from the old context 20274edfd48SNikolay Aleksandrov */ 20374edfd48SNikolay Aleksandrov static struct net_bridge_mcast_port * 20474edfd48SNikolay Aleksandrov br_multicast_pg_to_port_ctx(const struct net_bridge_port_group *pg) 20574edfd48SNikolay Aleksandrov { 20674edfd48SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx = &pg->key.port->multicast_ctx; 20774edfd48SNikolay Aleksandrov struct net_bridge_vlan *vlan; 20874edfd48SNikolay Aleksandrov 20974edfd48SNikolay Aleksandrov lockdep_assert_held_once(&pg->key.port->br->multicast_lock); 21074edfd48SNikolay Aleksandrov 21174edfd48SNikolay Aleksandrov /* if vlan snooping is disabled use the port's multicast context */ 21274edfd48SNikolay Aleksandrov if (!pg->key.addr.vid || 21374edfd48SNikolay Aleksandrov !br_opt_get(pg->key.port->br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) 21474edfd48SNikolay Aleksandrov goto out; 21574edfd48SNikolay Aleksandrov 21674edfd48SNikolay Aleksandrov /* locking is tricky here, due to different rules for multicast and 21774edfd48SNikolay Aleksandrov * vlans we need to take rcu to find the vlan and make sure it has 21874edfd48SNikolay Aleksandrov * the BR_VLFLAG_MCAST_ENABLED flag set, it can only change under 21974edfd48SNikolay Aleksandrov * multicast_lock which must be already held here, so the vlan's pmctx 22074edfd48SNikolay Aleksandrov * can safely be used on return 22174edfd48SNikolay Aleksandrov */ 22274edfd48SNikolay Aleksandrov rcu_read_lock(); 22374edfd48SNikolay Aleksandrov vlan = br_vlan_find(nbp_vlan_group(pg->key.port), pg->key.addr.vid); 22474edfd48SNikolay Aleksandrov if (vlan && !br_multicast_port_ctx_vlan_disabled(&vlan->port_mcast_ctx)) 22574edfd48SNikolay Aleksandrov pmctx = &vlan->port_mcast_ctx; 22674edfd48SNikolay Aleksandrov else 22774edfd48SNikolay Aleksandrov pmctx = NULL; 22874edfd48SNikolay Aleksandrov rcu_read_unlock(); 22974edfd48SNikolay Aleksandrov out: 23074edfd48SNikolay Aleksandrov return pmctx; 23174edfd48SNikolay Aleksandrov } 23274edfd48SNikolay Aleksandrov 233b0812368SNikolay Aleksandrov static bool br_port_group_equal(struct net_bridge_port_group *p, 234b0812368SNikolay Aleksandrov struct net_bridge_port *port, 235b0812368SNikolay Aleksandrov const unsigned char *src) 236b0812368SNikolay Aleksandrov { 237b0812368SNikolay Aleksandrov if (p->key.port != port) 238b0812368SNikolay Aleksandrov return false; 239b0812368SNikolay Aleksandrov 240b0812368SNikolay Aleksandrov if (!(port->flags & BR_MULTICAST_TO_UNICAST)) 241b0812368SNikolay Aleksandrov return true; 242b0812368SNikolay Aleksandrov 243b0812368SNikolay Aleksandrov return ether_addr_equal(src, p->eth_addr); 244b0812368SNikolay Aleksandrov } 245b0812368SNikolay Aleksandrov 246adc47037SNikolay Aleksandrov static void __fwd_add_star_excl(struct net_bridge_mcast_port *pmctx, 247adc47037SNikolay Aleksandrov struct net_bridge_port_group *pg, 2488266a049SNikolay Aleksandrov struct br_ip *sg_ip) 2498266a049SNikolay Aleksandrov { 2508266a049SNikolay Aleksandrov struct net_bridge_port_group_sg_key sg_key; 2518266a049SNikolay Aleksandrov struct net_bridge_port_group *src_pg; 252adc47037SNikolay Aleksandrov struct net_bridge_mcast *brmctx; 2538266a049SNikolay Aleksandrov 2548266a049SNikolay Aleksandrov memset(&sg_key, 0, sizeof(sg_key)); 2557b54aaafSNikolay Aleksandrov brmctx = br_multicast_port_ctx_get_global(pmctx); 2568266a049SNikolay Aleksandrov sg_key.port = pg->key.port; 2578266a049SNikolay Aleksandrov sg_key.addr = *sg_ip; 258adc47037SNikolay Aleksandrov if (br_sg_port_find(brmctx->br, &sg_key)) 2598266a049SNikolay Aleksandrov return; 2608266a049SNikolay Aleksandrov 261adc47037SNikolay Aleksandrov src_pg = __br_multicast_add_group(brmctx, pmctx, 262adc47037SNikolay Aleksandrov sg_ip, pg->eth_addr, 2639116ffbfSNikolay Aleksandrov MCAST_INCLUDE, false, false); 2648266a049SNikolay Aleksandrov if (IS_ERR_OR_NULL(src_pg) || 2658266a049SNikolay Aleksandrov src_pg->rt_protocol != RTPROT_KERNEL) 2668266a049SNikolay Aleksandrov return; 2678266a049SNikolay Aleksandrov 2688266a049SNikolay Aleksandrov src_pg->flags |= MDB_PG_FLAGS_STAR_EXCL; 2698266a049SNikolay Aleksandrov } 2708266a049SNikolay Aleksandrov 2718266a049SNikolay Aleksandrov static void __fwd_del_star_excl(struct net_bridge_port_group *pg, 2728266a049SNikolay Aleksandrov struct br_ip *sg_ip) 2738266a049SNikolay Aleksandrov { 2748266a049SNikolay Aleksandrov struct net_bridge_port_group_sg_key sg_key; 2758266a049SNikolay Aleksandrov struct net_bridge *br = pg->key.port->br; 2768266a049SNikolay Aleksandrov struct net_bridge_port_group *src_pg; 2778266a049SNikolay Aleksandrov 2788266a049SNikolay Aleksandrov memset(&sg_key, 0, sizeof(sg_key)); 2798266a049SNikolay Aleksandrov sg_key.port = pg->key.port; 2808266a049SNikolay Aleksandrov sg_key.addr = *sg_ip; 2818266a049SNikolay Aleksandrov src_pg = br_sg_port_find(br, &sg_key); 2828266a049SNikolay Aleksandrov if (!src_pg || !(src_pg->flags & MDB_PG_FLAGS_STAR_EXCL) || 2838266a049SNikolay Aleksandrov src_pg->rt_protocol != RTPROT_KERNEL) 2848266a049SNikolay Aleksandrov return; 2858266a049SNikolay Aleksandrov 2868266a049SNikolay Aleksandrov br_multicast_find_del_pg(br, src_pg); 2878266a049SNikolay Aleksandrov } 2888266a049SNikolay Aleksandrov 2898266a049SNikolay Aleksandrov /* When a port group transitions to (or is added as) EXCLUDE we need to add it 2908266a049SNikolay Aleksandrov * to all other ports' S,G entries which are not blocked by the current group 2918266a049SNikolay Aleksandrov * for proper replication, the assumption is that any S,G blocked entries 2928266a049SNikolay Aleksandrov * are already added so the S,G,port lookup should skip them. 2938266a049SNikolay Aleksandrov * When a port group transitions from EXCLUDE -> INCLUDE mode or is being 2948266a049SNikolay Aleksandrov * deleted we need to remove it from all ports' S,G entries where it was 2958266a049SNikolay Aleksandrov * automatically installed before (i.e. where it's MDB_PG_FLAGS_STAR_EXCL). 2968266a049SNikolay Aleksandrov */ 2978266a049SNikolay Aleksandrov void br_multicast_star_g_handle_mode(struct net_bridge_port_group *pg, 2988266a049SNikolay Aleksandrov u8 filter_mode) 2998266a049SNikolay Aleksandrov { 3008266a049SNikolay Aleksandrov struct net_bridge *br = pg->key.port->br; 3018266a049SNikolay Aleksandrov struct net_bridge_port_group *pg_lst; 302adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx; 3038266a049SNikolay Aleksandrov struct net_bridge_mdb_entry *mp; 3048266a049SNikolay Aleksandrov struct br_ip sg_ip; 3058266a049SNikolay Aleksandrov 3068266a049SNikolay Aleksandrov if (WARN_ON(!br_multicast_is_star_g(&pg->key.addr))) 3078266a049SNikolay Aleksandrov return; 3088266a049SNikolay Aleksandrov 3098266a049SNikolay Aleksandrov mp = br_mdb_ip_get(br, &pg->key.addr); 3108266a049SNikolay Aleksandrov if (!mp) 3118266a049SNikolay Aleksandrov return; 312*eb1593a0SNikolay Aleksandrov pmctx = br_multicast_pg_to_port_ctx(pg); 313*eb1593a0SNikolay Aleksandrov if (!pmctx) 314*eb1593a0SNikolay Aleksandrov return; 3158266a049SNikolay Aleksandrov 3168266a049SNikolay Aleksandrov memset(&sg_ip, 0, sizeof(sg_ip)); 3178266a049SNikolay Aleksandrov sg_ip = pg->key.addr; 3187b54aaafSNikolay Aleksandrov 3198266a049SNikolay Aleksandrov for (pg_lst = mlock_dereference(mp->ports, br); 3208266a049SNikolay Aleksandrov pg_lst; 3218266a049SNikolay Aleksandrov pg_lst = mlock_dereference(pg_lst->next, br)) { 3228266a049SNikolay Aleksandrov struct net_bridge_group_src *src_ent; 3238266a049SNikolay Aleksandrov 3248266a049SNikolay Aleksandrov if (pg_lst == pg) 3258266a049SNikolay Aleksandrov continue; 3268266a049SNikolay Aleksandrov hlist_for_each_entry(src_ent, &pg_lst->src_list, node) { 3278266a049SNikolay Aleksandrov if (!(src_ent->flags & BR_SGRP_F_INSTALLED)) 3288266a049SNikolay Aleksandrov continue; 3298266a049SNikolay Aleksandrov sg_ip.src = src_ent->addr.src; 3308266a049SNikolay Aleksandrov switch (filter_mode) { 3318266a049SNikolay Aleksandrov case MCAST_INCLUDE: 3328266a049SNikolay Aleksandrov __fwd_del_star_excl(pg, &sg_ip); 3338266a049SNikolay Aleksandrov break; 3348266a049SNikolay Aleksandrov case MCAST_EXCLUDE: 335adc47037SNikolay Aleksandrov __fwd_add_star_excl(pmctx, pg, &sg_ip); 3368266a049SNikolay Aleksandrov break; 3378266a049SNikolay Aleksandrov } 3388266a049SNikolay Aleksandrov } 3398266a049SNikolay Aleksandrov } 3408266a049SNikolay Aleksandrov } 3418266a049SNikolay Aleksandrov 342094b82fdSNikolay Aleksandrov /* called when adding a new S,G with host_joined == false by default */ 343094b82fdSNikolay Aleksandrov static void br_multicast_sg_host_state(struct net_bridge_mdb_entry *star_mp, 344094b82fdSNikolay Aleksandrov struct net_bridge_port_group *sg) 345094b82fdSNikolay Aleksandrov { 346094b82fdSNikolay Aleksandrov struct net_bridge_mdb_entry *sg_mp; 347094b82fdSNikolay Aleksandrov 348094b82fdSNikolay Aleksandrov if (WARN_ON(!br_multicast_is_star_g(&star_mp->addr))) 349094b82fdSNikolay Aleksandrov return; 350094b82fdSNikolay Aleksandrov if (!star_mp->host_joined) 351094b82fdSNikolay Aleksandrov return; 352094b82fdSNikolay Aleksandrov 353094b82fdSNikolay Aleksandrov sg_mp = br_mdb_ip_get(star_mp->br, &sg->key.addr); 354094b82fdSNikolay Aleksandrov if (!sg_mp) 355094b82fdSNikolay Aleksandrov return; 356094b82fdSNikolay Aleksandrov sg_mp->host_joined = true; 357094b82fdSNikolay Aleksandrov } 358094b82fdSNikolay Aleksandrov 359094b82fdSNikolay Aleksandrov /* set the host_joined state of all of *,G's S,G entries */ 360094b82fdSNikolay Aleksandrov static void br_multicast_star_g_host_state(struct net_bridge_mdb_entry *star_mp) 361094b82fdSNikolay Aleksandrov { 362094b82fdSNikolay Aleksandrov struct net_bridge *br = star_mp->br; 363094b82fdSNikolay Aleksandrov struct net_bridge_mdb_entry *sg_mp; 364094b82fdSNikolay Aleksandrov struct net_bridge_port_group *pg; 365094b82fdSNikolay Aleksandrov struct br_ip sg_ip; 366094b82fdSNikolay Aleksandrov 367094b82fdSNikolay Aleksandrov if (WARN_ON(!br_multicast_is_star_g(&star_mp->addr))) 368094b82fdSNikolay Aleksandrov return; 369094b82fdSNikolay Aleksandrov 370094b82fdSNikolay Aleksandrov memset(&sg_ip, 0, sizeof(sg_ip)); 371094b82fdSNikolay Aleksandrov sg_ip = star_mp->addr; 372094b82fdSNikolay Aleksandrov for (pg = mlock_dereference(star_mp->ports, br); 373094b82fdSNikolay Aleksandrov pg; 374094b82fdSNikolay Aleksandrov pg = mlock_dereference(pg->next, br)) { 375094b82fdSNikolay Aleksandrov struct net_bridge_group_src *src_ent; 376094b82fdSNikolay Aleksandrov 377094b82fdSNikolay Aleksandrov hlist_for_each_entry(src_ent, &pg->src_list, node) { 378094b82fdSNikolay Aleksandrov if (!(src_ent->flags & BR_SGRP_F_INSTALLED)) 379094b82fdSNikolay Aleksandrov continue; 380094b82fdSNikolay Aleksandrov sg_ip.src = src_ent->addr.src; 381094b82fdSNikolay Aleksandrov sg_mp = br_mdb_ip_get(br, &sg_ip); 382094b82fdSNikolay Aleksandrov if (!sg_mp) 383094b82fdSNikolay Aleksandrov continue; 384094b82fdSNikolay Aleksandrov sg_mp->host_joined = star_mp->host_joined; 385094b82fdSNikolay Aleksandrov } 386094b82fdSNikolay Aleksandrov } 387094b82fdSNikolay Aleksandrov } 388094b82fdSNikolay Aleksandrov 3898266a049SNikolay Aleksandrov static void br_multicast_sg_del_exclude_ports(struct net_bridge_mdb_entry *sgmp) 3908266a049SNikolay Aleksandrov { 3918266a049SNikolay Aleksandrov struct net_bridge_port_group __rcu **pp; 3928266a049SNikolay Aleksandrov struct net_bridge_port_group *p; 3938266a049SNikolay Aleksandrov 3948266a049SNikolay Aleksandrov /* *,G exclude ports are only added to S,G entries */ 3958266a049SNikolay Aleksandrov if (WARN_ON(br_multicast_is_star_g(&sgmp->addr))) 3968266a049SNikolay Aleksandrov return; 3978266a049SNikolay Aleksandrov 3988266a049SNikolay Aleksandrov /* we need the STAR_EXCLUDE ports if there are non-STAR_EXCLUDE ports 3998266a049SNikolay Aleksandrov * we should ignore perm entries since they're managed by user-space 4008266a049SNikolay Aleksandrov */ 4018266a049SNikolay Aleksandrov for (pp = &sgmp->ports; 4028266a049SNikolay Aleksandrov (p = mlock_dereference(*pp, sgmp->br)) != NULL; 4038266a049SNikolay Aleksandrov pp = &p->next) 4048266a049SNikolay Aleksandrov if (!(p->flags & (MDB_PG_FLAGS_STAR_EXCL | 4058266a049SNikolay Aleksandrov MDB_PG_FLAGS_PERMANENT))) 4068266a049SNikolay Aleksandrov return; 4078266a049SNikolay Aleksandrov 408094b82fdSNikolay Aleksandrov /* currently the host can only have joined the *,G which means 409094b82fdSNikolay Aleksandrov * we treat it as EXCLUDE {}, so for an S,G it's considered a 410094b82fdSNikolay Aleksandrov * STAR_EXCLUDE entry and we can safely leave it 411094b82fdSNikolay Aleksandrov */ 412094b82fdSNikolay Aleksandrov sgmp->host_joined = false; 413094b82fdSNikolay Aleksandrov 4148266a049SNikolay Aleksandrov for (pp = &sgmp->ports; 4158266a049SNikolay Aleksandrov (p = mlock_dereference(*pp, sgmp->br)) != NULL;) { 4168266a049SNikolay Aleksandrov if (!(p->flags & MDB_PG_FLAGS_PERMANENT)) 4178266a049SNikolay Aleksandrov br_multicast_del_pg(sgmp, p, pp); 4188266a049SNikolay Aleksandrov else 4198266a049SNikolay Aleksandrov pp = &p->next; 4208266a049SNikolay Aleksandrov } 4218266a049SNikolay Aleksandrov } 4228266a049SNikolay Aleksandrov 4238266a049SNikolay Aleksandrov void br_multicast_sg_add_exclude_ports(struct net_bridge_mdb_entry *star_mp, 4248266a049SNikolay Aleksandrov struct net_bridge_port_group *sg) 4258266a049SNikolay Aleksandrov { 4268266a049SNikolay Aleksandrov struct net_bridge_port_group_sg_key sg_key; 4278266a049SNikolay Aleksandrov struct net_bridge *br = star_mp->br; 428adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx; 4298266a049SNikolay Aleksandrov struct net_bridge_port_group *pg; 430adc47037SNikolay Aleksandrov struct net_bridge_mcast *brmctx; 4318266a049SNikolay Aleksandrov 4328266a049SNikolay Aleksandrov if (WARN_ON(br_multicast_is_star_g(&sg->key.addr))) 4338266a049SNikolay Aleksandrov return; 4348266a049SNikolay Aleksandrov if (WARN_ON(!br_multicast_is_star_g(&star_mp->addr))) 4358266a049SNikolay Aleksandrov return; 4368266a049SNikolay Aleksandrov 437094b82fdSNikolay Aleksandrov br_multicast_sg_host_state(star_mp, sg); 4388266a049SNikolay Aleksandrov memset(&sg_key, 0, sizeof(sg_key)); 4398266a049SNikolay Aleksandrov sg_key.addr = sg->key.addr; 4408266a049SNikolay Aleksandrov /* we need to add all exclude ports to the S,G */ 4418266a049SNikolay Aleksandrov for (pg = mlock_dereference(star_mp->ports, br); 4428266a049SNikolay Aleksandrov pg; 4438266a049SNikolay Aleksandrov pg = mlock_dereference(pg->next, br)) { 4448266a049SNikolay Aleksandrov struct net_bridge_port_group *src_pg; 4458266a049SNikolay Aleksandrov 4468266a049SNikolay Aleksandrov if (pg == sg || pg->filter_mode == MCAST_INCLUDE) 4478266a049SNikolay Aleksandrov continue; 4488266a049SNikolay Aleksandrov 4498266a049SNikolay Aleksandrov sg_key.port = pg->key.port; 4508266a049SNikolay Aleksandrov if (br_sg_port_find(br, &sg_key)) 4518266a049SNikolay Aleksandrov continue; 4528266a049SNikolay Aleksandrov 453*eb1593a0SNikolay Aleksandrov pmctx = br_multicast_pg_to_port_ctx(pg); 454*eb1593a0SNikolay Aleksandrov if (!pmctx) 455*eb1593a0SNikolay Aleksandrov continue; 456*eb1593a0SNikolay Aleksandrov brmctx = br_multicast_port_ctx_get_global(pmctx); 457*eb1593a0SNikolay Aleksandrov 458adc47037SNikolay Aleksandrov src_pg = __br_multicast_add_group(brmctx, pmctx, 4598266a049SNikolay Aleksandrov &sg->key.addr, 4608266a049SNikolay Aleksandrov sg->eth_addr, 4619116ffbfSNikolay Aleksandrov MCAST_INCLUDE, false, false); 4628266a049SNikolay Aleksandrov if (IS_ERR_OR_NULL(src_pg) || 4638266a049SNikolay Aleksandrov src_pg->rt_protocol != RTPROT_KERNEL) 4648266a049SNikolay Aleksandrov continue; 4658266a049SNikolay Aleksandrov src_pg->flags |= MDB_PG_FLAGS_STAR_EXCL; 4668266a049SNikolay Aleksandrov } 4678266a049SNikolay Aleksandrov } 4688266a049SNikolay Aleksandrov 469b0812368SNikolay Aleksandrov static void br_multicast_fwd_src_add(struct net_bridge_group_src *src) 470b0812368SNikolay Aleksandrov { 4718266a049SNikolay Aleksandrov struct net_bridge_mdb_entry *star_mp; 472adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx; 473b0812368SNikolay Aleksandrov struct net_bridge_port_group *sg; 474adc47037SNikolay Aleksandrov struct net_bridge_mcast *brmctx; 475b0812368SNikolay Aleksandrov struct br_ip sg_ip; 476b0812368SNikolay Aleksandrov 477b0812368SNikolay Aleksandrov if (src->flags & BR_SGRP_F_INSTALLED) 478b0812368SNikolay Aleksandrov return; 479b0812368SNikolay Aleksandrov 480b0812368SNikolay Aleksandrov memset(&sg_ip, 0, sizeof(sg_ip)); 481*eb1593a0SNikolay Aleksandrov pmctx = br_multicast_pg_to_port_ctx(src->pg); 482*eb1593a0SNikolay Aleksandrov if (!pmctx) 483*eb1593a0SNikolay Aleksandrov return; 4847b54aaafSNikolay Aleksandrov brmctx = br_multicast_port_ctx_get_global(pmctx); 485b0812368SNikolay Aleksandrov sg_ip = src->pg->key.addr; 486b0812368SNikolay Aleksandrov sg_ip.src = src->addr.src; 487adc47037SNikolay Aleksandrov 488adc47037SNikolay Aleksandrov sg = __br_multicast_add_group(brmctx, pmctx, &sg_ip, 4899116ffbfSNikolay Aleksandrov src->pg->eth_addr, MCAST_INCLUDE, false, 4909116ffbfSNikolay Aleksandrov !timer_pending(&src->timer)); 491b0812368SNikolay Aleksandrov if (IS_ERR_OR_NULL(sg)) 492b0812368SNikolay Aleksandrov return; 493b0812368SNikolay Aleksandrov src->flags |= BR_SGRP_F_INSTALLED; 4948266a049SNikolay Aleksandrov sg->flags &= ~MDB_PG_FLAGS_STAR_EXCL; 495b0812368SNikolay Aleksandrov 496b0812368SNikolay Aleksandrov /* if it was added by user-space as perm we can skip next steps */ 497b0812368SNikolay Aleksandrov if (sg->rt_protocol != RTPROT_KERNEL && 498b0812368SNikolay Aleksandrov (sg->flags & MDB_PG_FLAGS_PERMANENT)) 499b0812368SNikolay Aleksandrov return; 500b0812368SNikolay Aleksandrov 501b0812368SNikolay Aleksandrov /* the kernel is now responsible for removing this S,G */ 502b0812368SNikolay Aleksandrov del_timer(&sg->timer); 5038266a049SNikolay Aleksandrov star_mp = br_mdb_ip_get(src->br, &src->pg->key.addr); 5048266a049SNikolay Aleksandrov if (!star_mp) 5058266a049SNikolay Aleksandrov return; 5068266a049SNikolay Aleksandrov 5078266a049SNikolay Aleksandrov br_multicast_sg_add_exclude_ports(star_mp, sg); 508b0812368SNikolay Aleksandrov } 509b0812368SNikolay Aleksandrov 510d5a10222SNikolay Aleksandrov static void br_multicast_fwd_src_remove(struct net_bridge_group_src *src, 511d5a10222SNikolay Aleksandrov bool fastleave) 512b0812368SNikolay Aleksandrov { 513b0812368SNikolay Aleksandrov struct net_bridge_port_group *p, *pg = src->pg; 514b0812368SNikolay Aleksandrov struct net_bridge_port_group __rcu **pp; 515b0812368SNikolay Aleksandrov struct net_bridge_mdb_entry *mp; 516b0812368SNikolay Aleksandrov struct br_ip sg_ip; 517b0812368SNikolay Aleksandrov 518b0812368SNikolay Aleksandrov memset(&sg_ip, 0, sizeof(sg_ip)); 519b0812368SNikolay Aleksandrov sg_ip = pg->key.addr; 520b0812368SNikolay Aleksandrov sg_ip.src = src->addr.src; 521b0812368SNikolay Aleksandrov 522b0812368SNikolay Aleksandrov mp = br_mdb_ip_get(src->br, &sg_ip); 523b0812368SNikolay Aleksandrov if (!mp) 524b0812368SNikolay Aleksandrov return; 525b0812368SNikolay Aleksandrov 526b0812368SNikolay Aleksandrov for (pp = &mp->ports; 527b0812368SNikolay Aleksandrov (p = mlock_dereference(*pp, src->br)) != NULL; 528b0812368SNikolay Aleksandrov pp = &p->next) { 529b0812368SNikolay Aleksandrov if (!br_port_group_equal(p, pg->key.port, pg->eth_addr)) 530b0812368SNikolay Aleksandrov continue; 531b0812368SNikolay Aleksandrov 532b0812368SNikolay Aleksandrov if (p->rt_protocol != RTPROT_KERNEL && 533b0812368SNikolay Aleksandrov (p->flags & MDB_PG_FLAGS_PERMANENT)) 534b0812368SNikolay Aleksandrov break; 535b0812368SNikolay Aleksandrov 536d5a10222SNikolay Aleksandrov if (fastleave) 537d5a10222SNikolay Aleksandrov p->flags |= MDB_PG_FLAGS_FAST_LEAVE; 538b0812368SNikolay Aleksandrov br_multicast_del_pg(mp, p, pp); 539b0812368SNikolay Aleksandrov break; 540b0812368SNikolay Aleksandrov } 541b0812368SNikolay Aleksandrov src->flags &= ~BR_SGRP_F_INSTALLED; 542b0812368SNikolay Aleksandrov } 543b0812368SNikolay Aleksandrov 5449116ffbfSNikolay Aleksandrov /* install S,G and based on src's timer enable or disable forwarding */ 545b0812368SNikolay Aleksandrov static void br_multicast_fwd_src_handle(struct net_bridge_group_src *src) 546b0812368SNikolay Aleksandrov { 5479116ffbfSNikolay Aleksandrov struct net_bridge_port_group_sg_key sg_key; 5489116ffbfSNikolay Aleksandrov struct net_bridge_port_group *sg; 5499116ffbfSNikolay Aleksandrov u8 old_flags; 5509116ffbfSNikolay Aleksandrov 551b0812368SNikolay Aleksandrov br_multicast_fwd_src_add(src); 5529116ffbfSNikolay Aleksandrov 5539116ffbfSNikolay Aleksandrov memset(&sg_key, 0, sizeof(sg_key)); 5549116ffbfSNikolay Aleksandrov sg_key.addr = src->pg->key.addr; 5559116ffbfSNikolay Aleksandrov sg_key.addr.src = src->addr.src; 5569116ffbfSNikolay Aleksandrov sg_key.port = src->pg->key.port; 5579116ffbfSNikolay Aleksandrov 5589116ffbfSNikolay Aleksandrov sg = br_sg_port_find(src->br, &sg_key); 5599116ffbfSNikolay Aleksandrov if (!sg || (sg->flags & MDB_PG_FLAGS_PERMANENT)) 5609116ffbfSNikolay Aleksandrov return; 5619116ffbfSNikolay Aleksandrov 5629116ffbfSNikolay Aleksandrov old_flags = sg->flags; 5639116ffbfSNikolay Aleksandrov if (timer_pending(&src->timer)) 5649116ffbfSNikolay Aleksandrov sg->flags &= ~MDB_PG_FLAGS_BLOCKED; 5659116ffbfSNikolay Aleksandrov else 5669116ffbfSNikolay Aleksandrov sg->flags |= MDB_PG_FLAGS_BLOCKED; 5679116ffbfSNikolay Aleksandrov 5689116ffbfSNikolay Aleksandrov if (old_flags != sg->flags) { 5699116ffbfSNikolay Aleksandrov struct net_bridge_mdb_entry *sg_mp; 5709116ffbfSNikolay Aleksandrov 5719116ffbfSNikolay Aleksandrov sg_mp = br_mdb_ip_get(src->br, &sg_key.addr); 5729116ffbfSNikolay Aleksandrov if (!sg_mp) 5739116ffbfSNikolay Aleksandrov return; 5749116ffbfSNikolay Aleksandrov br_mdb_notify(src->br->dev, sg_mp, sg, RTM_NEWMDB); 5759116ffbfSNikolay Aleksandrov } 576b0812368SNikolay Aleksandrov } 577b0812368SNikolay Aleksandrov 578e12cec65SNikolay Aleksandrov static void br_multicast_destroy_mdb_entry(struct net_bridge_mcast_gc *gc) 579e12cec65SNikolay Aleksandrov { 580e12cec65SNikolay Aleksandrov struct net_bridge_mdb_entry *mp; 581e12cec65SNikolay Aleksandrov 582e12cec65SNikolay Aleksandrov mp = container_of(gc, struct net_bridge_mdb_entry, mcast_gc); 583e12cec65SNikolay Aleksandrov WARN_ON(!hlist_unhashed(&mp->mdb_node)); 584e12cec65SNikolay Aleksandrov WARN_ON(mp->ports); 585e12cec65SNikolay Aleksandrov 586e12cec65SNikolay Aleksandrov del_timer_sync(&mp->timer); 587e12cec65SNikolay Aleksandrov kfree_rcu(mp, rcu); 588e12cec65SNikolay Aleksandrov } 589e12cec65SNikolay Aleksandrov 590e12cec65SNikolay Aleksandrov static void br_multicast_del_mdb_entry(struct net_bridge_mdb_entry *mp) 591e12cec65SNikolay Aleksandrov { 592e12cec65SNikolay Aleksandrov struct net_bridge *br = mp->br; 593e12cec65SNikolay Aleksandrov 594e12cec65SNikolay Aleksandrov rhashtable_remove_fast(&br->mdb_hash_tbl, &mp->rhnode, 595e12cec65SNikolay Aleksandrov br_mdb_rht_params); 596e12cec65SNikolay Aleksandrov hlist_del_init_rcu(&mp->mdb_node); 597e12cec65SNikolay Aleksandrov hlist_add_head(&mp->mcast_gc.gc_node, &br->mcast_gc_list); 598e12cec65SNikolay Aleksandrov queue_work(system_long_wq, &br->mcast_gc_work); 599e12cec65SNikolay Aleksandrov } 600e12cec65SNikolay Aleksandrov 60188c1f37fSAllen Pais static void br_multicast_group_expired(struct timer_list *t) 602eb1d1641SHerbert Xu { 60388c1f37fSAllen Pais struct net_bridge_mdb_entry *mp = from_timer(mp, t, timer); 604eb1d1641SHerbert Xu struct net_bridge *br = mp->br; 605eb1d1641SHerbert Xu 606eb1d1641SHerbert Xu spin_lock(&br->multicast_lock); 607b0812368SNikolay Aleksandrov if (hlist_unhashed(&mp->mdb_node) || !netif_running(br->dev) || 608b0812368SNikolay Aleksandrov timer_pending(&mp->timer)) 609eb1d1641SHerbert Xu goto out; 610eb1d1641SHerbert Xu 6111bc844eeSNikolay Aleksandrov br_multicast_host_leave(mp, true); 612eb1d1641SHerbert Xu 613eb1d1641SHerbert Xu if (mp->ports) 614eb1d1641SHerbert Xu goto out; 615e12cec65SNikolay Aleksandrov br_multicast_del_mdb_entry(mp); 616eb1d1641SHerbert Xu out: 617eb1d1641SHerbert Xu spin_unlock(&br->multicast_lock); 618eb1d1641SHerbert Xu } 619eb1d1641SHerbert Xu 620e12cec65SNikolay Aleksandrov static void br_multicast_destroy_group_src(struct net_bridge_mcast_gc *gc) 621e12cec65SNikolay Aleksandrov { 622e12cec65SNikolay Aleksandrov struct net_bridge_group_src *src; 623e12cec65SNikolay Aleksandrov 624e12cec65SNikolay Aleksandrov src = container_of(gc, struct net_bridge_group_src, mcast_gc); 625e12cec65SNikolay Aleksandrov WARN_ON(!hlist_unhashed(&src->node)); 626e12cec65SNikolay Aleksandrov 627e12cec65SNikolay Aleksandrov del_timer_sync(&src->timer); 628e12cec65SNikolay Aleksandrov kfree_rcu(src, rcu); 629e12cec65SNikolay Aleksandrov } 630e12cec65SNikolay Aleksandrov 631d5a10222SNikolay Aleksandrov void br_multicast_del_group_src(struct net_bridge_group_src *src, 632d5a10222SNikolay Aleksandrov bool fastleave) 6338b671779SNikolay Aleksandrov { 634085b53c8SNikolay Aleksandrov struct net_bridge *br = src->pg->key.port->br; 6358b671779SNikolay Aleksandrov 636d5a10222SNikolay Aleksandrov br_multicast_fwd_src_remove(src, fastleave); 6378b671779SNikolay Aleksandrov hlist_del_init_rcu(&src->node); 6388b671779SNikolay Aleksandrov src->pg->src_ents--; 639e12cec65SNikolay Aleksandrov hlist_add_head(&src->mcast_gc.gc_node, &br->mcast_gc_list); 640e12cec65SNikolay Aleksandrov queue_work(system_long_wq, &br->mcast_gc_work); 641e12cec65SNikolay Aleksandrov } 642e12cec65SNikolay Aleksandrov 643e12cec65SNikolay Aleksandrov static void br_multicast_destroy_port_group(struct net_bridge_mcast_gc *gc) 644e12cec65SNikolay Aleksandrov { 645e12cec65SNikolay Aleksandrov struct net_bridge_port_group *pg; 646e12cec65SNikolay Aleksandrov 647e12cec65SNikolay Aleksandrov pg = container_of(gc, struct net_bridge_port_group, mcast_gc); 648e12cec65SNikolay Aleksandrov WARN_ON(!hlist_unhashed(&pg->mglist)); 649e12cec65SNikolay Aleksandrov WARN_ON(!hlist_empty(&pg->src_list)); 650e12cec65SNikolay Aleksandrov 651e12cec65SNikolay Aleksandrov del_timer_sync(&pg->rexmit_timer); 652e12cec65SNikolay Aleksandrov del_timer_sync(&pg->timer); 653e12cec65SNikolay Aleksandrov kfree_rcu(pg, rcu); 6548b671779SNikolay Aleksandrov } 6558b671779SNikolay Aleksandrov 656681590bdSNikolay Aleksandrov void br_multicast_del_pg(struct net_bridge_mdb_entry *mp, 657681590bdSNikolay Aleksandrov struct net_bridge_port_group *pg, 658681590bdSNikolay Aleksandrov struct net_bridge_port_group __rcu **pp) 659681590bdSNikolay Aleksandrov { 660085b53c8SNikolay Aleksandrov struct net_bridge *br = pg->key.port->br; 6618b671779SNikolay Aleksandrov struct net_bridge_group_src *ent; 6628b671779SNikolay Aleksandrov struct hlist_node *tmp; 663681590bdSNikolay Aleksandrov 664681590bdSNikolay Aleksandrov rcu_assign_pointer(*pp, pg->next); 665681590bdSNikolay Aleksandrov hlist_del_init(&pg->mglist); 666baa74d39SNikolay Aleksandrov br_multicast_eht_clean_sets(pg); 6678b671779SNikolay Aleksandrov hlist_for_each_entry_safe(ent, tmp, &pg->src_list, node) 668d5a10222SNikolay Aleksandrov br_multicast_del_group_src(ent, false); 66981f19838SNikolay Aleksandrov br_mdb_notify(br->dev, mp, pg, RTM_DELMDB); 67074705582SNikolay Aleksandrov if (!br_multicast_is_star_g(&mp->addr)) { 67174705582SNikolay Aleksandrov rhashtable_remove_fast(&br->sg_port_tbl, &pg->rhnode, 67274705582SNikolay Aleksandrov br_sg_port_rht_params); 6738266a049SNikolay Aleksandrov br_multicast_sg_del_exclude_ports(mp); 67474705582SNikolay Aleksandrov } else { 6758266a049SNikolay Aleksandrov br_multicast_star_g_handle_mode(pg, MCAST_INCLUDE); 67674705582SNikolay Aleksandrov } 677e12cec65SNikolay Aleksandrov hlist_add_head(&pg->mcast_gc.gc_node, &br->mcast_gc_list); 678e12cec65SNikolay Aleksandrov queue_work(system_long_wq, &br->mcast_gc_work); 679681590bdSNikolay Aleksandrov 680681590bdSNikolay Aleksandrov if (!mp->ports && !mp->host_joined && netif_running(br->dev)) 681681590bdSNikolay Aleksandrov mod_timer(&mp->timer, jiffies); 682681590bdSNikolay Aleksandrov } 683681590bdSNikolay Aleksandrov 684681590bdSNikolay Aleksandrov static void br_multicast_find_del_pg(struct net_bridge *br, 685eb1d1641SHerbert Xu struct net_bridge_port_group *pg) 686eb1d1641SHerbert Xu { 6878b671779SNikolay Aleksandrov struct net_bridge_port_group __rcu **pp; 688eb1d1641SHerbert Xu struct net_bridge_mdb_entry *mp; 689eb1d1641SHerbert Xu struct net_bridge_port_group *p; 690e8051688SEric Dumazet 691085b53c8SNikolay Aleksandrov mp = br_mdb_ip_get(br, &pg->key.addr); 692eb1d1641SHerbert Xu if (WARN_ON(!mp)) 693eb1d1641SHerbert Xu return; 694eb1d1641SHerbert Xu 695e8051688SEric Dumazet for (pp = &mp->ports; 696e8051688SEric Dumazet (p = mlock_dereference(*pp, br)) != NULL; 697e8051688SEric Dumazet pp = &p->next) { 698eb1d1641SHerbert Xu if (p != pg) 699eb1d1641SHerbert Xu continue; 700eb1d1641SHerbert Xu 701681590bdSNikolay Aleksandrov br_multicast_del_pg(mp, pg, pp); 702eb1d1641SHerbert Xu return; 703eb1d1641SHerbert Xu } 704eb1d1641SHerbert Xu 705eb1d1641SHerbert Xu WARN_ON(1); 706eb1d1641SHerbert Xu } 707eb1d1641SHerbert Xu 70888c1f37fSAllen Pais static void br_multicast_port_group_expired(struct timer_list *t) 709eb1d1641SHerbert Xu { 71088c1f37fSAllen Pais struct net_bridge_port_group *pg = from_timer(pg, t, timer); 711d6c33d67SNikolay Aleksandrov struct net_bridge_group_src *src_ent; 712085b53c8SNikolay Aleksandrov struct net_bridge *br = pg->key.port->br; 713d6c33d67SNikolay Aleksandrov struct hlist_node *tmp; 714d6c33d67SNikolay Aleksandrov bool changed; 715eb1d1641SHerbert Xu 716eb1d1641SHerbert Xu spin_lock(&br->multicast_lock); 717eb1d1641SHerbert Xu if (!netif_running(br->dev) || timer_pending(&pg->timer) || 7189d06b6d8SElad Raz hlist_unhashed(&pg->mglist) || pg->flags & MDB_PG_FLAGS_PERMANENT) 719eb1d1641SHerbert Xu goto out; 720eb1d1641SHerbert Xu 721d6c33d67SNikolay Aleksandrov changed = !!(pg->filter_mode == MCAST_EXCLUDE); 722d6c33d67SNikolay Aleksandrov pg->filter_mode = MCAST_INCLUDE; 723d6c33d67SNikolay Aleksandrov hlist_for_each_entry_safe(src_ent, tmp, &pg->src_list, node) { 724d6c33d67SNikolay Aleksandrov if (!timer_pending(&src_ent->timer)) { 725d5a10222SNikolay Aleksandrov br_multicast_del_group_src(src_ent, false); 726d6c33d67SNikolay Aleksandrov changed = true; 727d6c33d67SNikolay Aleksandrov } 728d6c33d67SNikolay Aleksandrov } 729eb1d1641SHerbert Xu 730d6c33d67SNikolay Aleksandrov if (hlist_empty(&pg->src_list)) { 731d6c33d67SNikolay Aleksandrov br_multicast_find_del_pg(br, pg); 732d6c33d67SNikolay Aleksandrov } else if (changed) { 733085b53c8SNikolay Aleksandrov struct net_bridge_mdb_entry *mp = br_mdb_ip_get(br, &pg->key.addr); 734d6c33d67SNikolay Aleksandrov 7358266a049SNikolay Aleksandrov if (changed && br_multicast_is_star_g(&pg->key.addr)) 7368266a049SNikolay Aleksandrov br_multicast_star_g_handle_mode(pg, MCAST_INCLUDE); 7378266a049SNikolay Aleksandrov 738d6c33d67SNikolay Aleksandrov if (WARN_ON(!mp)) 739d6c33d67SNikolay Aleksandrov goto out; 740d6c33d67SNikolay Aleksandrov br_mdb_notify(br->dev, mp, pg, RTM_NEWMDB); 741d6c33d67SNikolay Aleksandrov } 742eb1d1641SHerbert Xu out: 743eb1d1641SHerbert Xu spin_unlock(&br->multicast_lock); 744eb1d1641SHerbert Xu } 745eb1d1641SHerbert Xu 746e12cec65SNikolay Aleksandrov static void br_multicast_gc(struct hlist_head *head) 747e12cec65SNikolay Aleksandrov { 748e12cec65SNikolay Aleksandrov struct net_bridge_mcast_gc *gcent; 749e12cec65SNikolay Aleksandrov struct hlist_node *tmp; 750e12cec65SNikolay Aleksandrov 751e12cec65SNikolay Aleksandrov hlist_for_each_entry_safe(gcent, tmp, head, gc_node) { 752e12cec65SNikolay Aleksandrov hlist_del_init(&gcent->gc_node); 753e12cec65SNikolay Aleksandrov gcent->destroy(gcent); 754e12cec65SNikolay Aleksandrov } 755e12cec65SNikolay Aleksandrov } 756e12cec65SNikolay Aleksandrov 757adc47037SNikolay Aleksandrov static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge_mcast *brmctx, 758438ef2d0SNikolay Aleksandrov struct net_bridge_port_group *pg, 759438ef2d0SNikolay Aleksandrov __be32 ip_dst, __be32 group, 760438ef2d0SNikolay Aleksandrov bool with_srcs, bool over_lmqt, 76142c11ccfSNikolay Aleksandrov u8 sflag, u8 *igmp_type, 76242c11ccfSNikolay Aleksandrov bool *need_rexmit) 763eb1d1641SHerbert Xu { 764085b53c8SNikolay Aleksandrov struct net_bridge_port *p = pg ? pg->key.port : NULL; 765438ef2d0SNikolay Aleksandrov struct net_bridge_group_src *ent; 766438ef2d0SNikolay Aleksandrov size_t pkt_size, igmp_hdr_size; 767438ef2d0SNikolay Aleksandrov unsigned long now = jiffies; 7685e923585SNikolay Aleksandrov struct igmpv3_query *ihv3; 769438ef2d0SNikolay Aleksandrov void *csum_start = NULL; 770438ef2d0SNikolay Aleksandrov __sum16 *csum = NULL; 771eb1d1641SHerbert Xu struct sk_buff *skb; 772eb1d1641SHerbert Xu struct igmphdr *ih; 773eb1d1641SHerbert Xu struct ethhdr *eth; 774438ef2d0SNikolay Aleksandrov unsigned long lmqt; 775eb1d1641SHerbert Xu struct iphdr *iph; 776438ef2d0SNikolay Aleksandrov u16 lmqt_srcs = 0; 777eb1d1641SHerbert Xu 7785e923585SNikolay Aleksandrov igmp_hdr_size = sizeof(*ih); 779d3d065c0SNikolay Aleksandrov if (brmctx->multicast_igmp_version == 3) { 7805e923585SNikolay Aleksandrov igmp_hdr_size = sizeof(*ihv3); 781438ef2d0SNikolay Aleksandrov if (pg && with_srcs) { 782d3d065c0SNikolay Aleksandrov lmqt = now + (brmctx->multicast_last_member_interval * 783d3d065c0SNikolay Aleksandrov brmctx->multicast_last_member_count); 784438ef2d0SNikolay Aleksandrov hlist_for_each_entry(ent, &pg->src_list, node) { 785438ef2d0SNikolay Aleksandrov if (over_lmqt == time_after(ent->timer.expires, 786438ef2d0SNikolay Aleksandrov lmqt) && 787438ef2d0SNikolay Aleksandrov ent->src_query_rexmit_cnt > 0) 788438ef2d0SNikolay Aleksandrov lmqt_srcs++; 789438ef2d0SNikolay Aleksandrov } 790438ef2d0SNikolay Aleksandrov 791438ef2d0SNikolay Aleksandrov if (!lmqt_srcs) 792438ef2d0SNikolay Aleksandrov return NULL; 793438ef2d0SNikolay Aleksandrov igmp_hdr_size += lmqt_srcs * sizeof(__be32); 794438ef2d0SNikolay Aleksandrov } 795438ef2d0SNikolay Aleksandrov } 796438ef2d0SNikolay Aleksandrov 797438ef2d0SNikolay Aleksandrov pkt_size = sizeof(*eth) + sizeof(*iph) + 4 + igmp_hdr_size; 798438ef2d0SNikolay Aleksandrov if ((p && pkt_size > p->dev->mtu) || 799adc47037SNikolay Aleksandrov pkt_size > brmctx->br->dev->mtu) 800438ef2d0SNikolay Aleksandrov return NULL; 801438ef2d0SNikolay Aleksandrov 802adc47037SNikolay Aleksandrov skb = netdev_alloc_skb_ip_align(brmctx->br->dev, pkt_size); 803eb1d1641SHerbert Xu if (!skb) 804eb1d1641SHerbert Xu goto out; 805eb1d1641SHerbert Xu 806eb1d1641SHerbert Xu skb->protocol = htons(ETH_P_IP); 807eb1d1641SHerbert Xu 808eb1d1641SHerbert Xu skb_reset_mac_header(skb); 809eb1d1641SHerbert Xu eth = eth_hdr(skb); 810eb1d1641SHerbert Xu 811adc47037SNikolay Aleksandrov ether_addr_copy(eth->h_source, brmctx->br->dev->dev_addr); 812438ef2d0SNikolay Aleksandrov ip_eth_mc_map(ip_dst, eth->h_dest); 813eb1d1641SHerbert Xu eth->h_proto = htons(ETH_P_IP); 814eb1d1641SHerbert Xu skb_put(skb, sizeof(*eth)); 815eb1d1641SHerbert Xu 816eb1d1641SHerbert Xu skb_set_network_header(skb, skb->len); 817eb1d1641SHerbert Xu iph = ip_hdr(skb); 818438ef2d0SNikolay Aleksandrov iph->tot_len = htons(pkt_size - sizeof(*eth)); 819eb1d1641SHerbert Xu 820eb1d1641SHerbert Xu iph->version = 4; 821eb1d1641SHerbert Xu iph->ihl = 6; 822eb1d1641SHerbert Xu iph->tos = 0xc0; 823eb1d1641SHerbert Xu iph->id = 0; 824eb1d1641SHerbert Xu iph->frag_off = htons(IP_DF); 825eb1d1641SHerbert Xu iph->ttl = 1; 826eb1d1641SHerbert Xu iph->protocol = IPPROTO_IGMP; 827adc47037SNikolay Aleksandrov iph->saddr = br_opt_get(brmctx->br, BROPT_MULTICAST_QUERY_USE_IFADDR) ? 828adc47037SNikolay Aleksandrov inet_select_addr(brmctx->br->dev, 0, RT_SCOPE_LINK) : 0; 829438ef2d0SNikolay Aleksandrov iph->daddr = ip_dst; 830eb1d1641SHerbert Xu ((u8 *)&iph[1])[0] = IPOPT_RA; 831eb1d1641SHerbert Xu ((u8 *)&iph[1])[1] = 4; 832eb1d1641SHerbert Xu ((u8 *)&iph[1])[2] = 0; 833eb1d1641SHerbert Xu ((u8 *)&iph[1])[3] = 0; 834eb1d1641SHerbert Xu ip_send_check(iph); 835eb1d1641SHerbert Xu skb_put(skb, 24); 836eb1d1641SHerbert Xu 837eb1d1641SHerbert Xu skb_set_transport_header(skb, skb->len); 8381080ab95SNikolay Aleksandrov *igmp_type = IGMP_HOST_MEMBERSHIP_QUERY; 8395e923585SNikolay Aleksandrov 840d3d065c0SNikolay Aleksandrov switch (brmctx->multicast_igmp_version) { 8415e923585SNikolay Aleksandrov case 2: 8425e923585SNikolay Aleksandrov ih = igmp_hdr(skb); 843eb1d1641SHerbert Xu ih->type = IGMP_HOST_MEMBERSHIP_QUERY; 844d3d065c0SNikolay Aleksandrov ih->code = (group ? brmctx->multicast_last_member_interval : 845d3d065c0SNikolay Aleksandrov brmctx->multicast_query_response_interval) / 846eb1d1641SHerbert Xu (HZ / IGMP_TIMER_SCALE); 847eb1d1641SHerbert Xu ih->group = group; 848eb1d1641SHerbert Xu ih->csum = 0; 849438ef2d0SNikolay Aleksandrov csum = &ih->csum; 850438ef2d0SNikolay Aleksandrov csum_start = (void *)ih; 8515e923585SNikolay Aleksandrov break; 8525e923585SNikolay Aleksandrov case 3: 8535e923585SNikolay Aleksandrov ihv3 = igmpv3_query_hdr(skb); 8545e923585SNikolay Aleksandrov ihv3->type = IGMP_HOST_MEMBERSHIP_QUERY; 855d3d065c0SNikolay Aleksandrov ihv3->code = (group ? brmctx->multicast_last_member_interval : 856d3d065c0SNikolay Aleksandrov brmctx->multicast_query_response_interval) / 8575e923585SNikolay Aleksandrov (HZ / IGMP_TIMER_SCALE); 8585e923585SNikolay Aleksandrov ihv3->group = group; 859d3d065c0SNikolay Aleksandrov ihv3->qqic = brmctx->multicast_query_interval / HZ; 860438ef2d0SNikolay Aleksandrov ihv3->nsrcs = htons(lmqt_srcs); 8615e923585SNikolay Aleksandrov ihv3->resv = 0; 862438ef2d0SNikolay Aleksandrov ihv3->suppress = sflag; 8635e923585SNikolay Aleksandrov ihv3->qrv = 2; 8645e923585SNikolay Aleksandrov ihv3->csum = 0; 865438ef2d0SNikolay Aleksandrov csum = &ihv3->csum; 866438ef2d0SNikolay Aleksandrov csum_start = (void *)ihv3; 867438ef2d0SNikolay Aleksandrov if (!pg || !with_srcs) 868438ef2d0SNikolay Aleksandrov break; 869438ef2d0SNikolay Aleksandrov 870438ef2d0SNikolay Aleksandrov lmqt_srcs = 0; 871438ef2d0SNikolay Aleksandrov hlist_for_each_entry(ent, &pg->src_list, node) { 872438ef2d0SNikolay Aleksandrov if (over_lmqt == time_after(ent->timer.expires, 873438ef2d0SNikolay Aleksandrov lmqt) && 874438ef2d0SNikolay Aleksandrov ent->src_query_rexmit_cnt > 0) { 875deb96566SNikolay Aleksandrov ihv3->srcs[lmqt_srcs++] = ent->addr.src.ip4; 876438ef2d0SNikolay Aleksandrov ent->src_query_rexmit_cnt--; 87742c11ccfSNikolay Aleksandrov if (need_rexmit && ent->src_query_rexmit_cnt) 87842c11ccfSNikolay Aleksandrov *need_rexmit = true; 879438ef2d0SNikolay Aleksandrov } 880438ef2d0SNikolay Aleksandrov } 881438ef2d0SNikolay Aleksandrov if (WARN_ON(lmqt_srcs != ntohs(ihv3->nsrcs))) { 882438ef2d0SNikolay Aleksandrov kfree_skb(skb); 883438ef2d0SNikolay Aleksandrov return NULL; 884438ef2d0SNikolay Aleksandrov } 8855e923585SNikolay Aleksandrov break; 8865e923585SNikolay Aleksandrov } 887eb1d1641SHerbert Xu 888438ef2d0SNikolay Aleksandrov if (WARN_ON(!csum || !csum_start)) { 889438ef2d0SNikolay Aleksandrov kfree_skb(skb); 890438ef2d0SNikolay Aleksandrov return NULL; 891438ef2d0SNikolay Aleksandrov } 892438ef2d0SNikolay Aleksandrov 893438ef2d0SNikolay Aleksandrov *csum = ip_compute_csum(csum_start, igmp_hdr_size); 8945e923585SNikolay Aleksandrov skb_put(skb, igmp_hdr_size); 895eb1d1641SHerbert Xu __skb_pull(skb, sizeof(*eth)); 896eb1d1641SHerbert Xu 897eb1d1641SHerbert Xu out: 898eb1d1641SHerbert Xu return skb; 899eb1d1641SHerbert Xu } 900eb1d1641SHerbert Xu 901dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 902adc47037SNikolay Aleksandrov static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge_mcast *brmctx, 903438ef2d0SNikolay Aleksandrov struct net_bridge_port_group *pg, 904438ef2d0SNikolay Aleksandrov const struct in6_addr *ip6_dst, 905438ef2d0SNikolay Aleksandrov const struct in6_addr *group, 906438ef2d0SNikolay Aleksandrov bool with_srcs, bool over_llqt, 90742c11ccfSNikolay Aleksandrov u8 sflag, u8 *igmp_type, 90842c11ccfSNikolay Aleksandrov bool *need_rexmit) 90908b202b6SYOSHIFUJI Hideaki { 910085b53c8SNikolay Aleksandrov struct net_bridge_port *p = pg ? pg->key.port : NULL; 911438ef2d0SNikolay Aleksandrov struct net_bridge_group_src *ent; 912438ef2d0SNikolay Aleksandrov size_t pkt_size, mld_hdr_size; 913438ef2d0SNikolay Aleksandrov unsigned long now = jiffies; 914aa2ae3e7SNikolay Aleksandrov struct mld2_query *mld2q; 915438ef2d0SNikolay Aleksandrov void *csum_start = NULL; 916aa2ae3e7SNikolay Aleksandrov unsigned long interval; 917438ef2d0SNikolay Aleksandrov __sum16 *csum = NULL; 91808b202b6SYOSHIFUJI Hideaki struct ipv6hdr *ip6h; 91908b202b6SYOSHIFUJI Hideaki struct mld_msg *mldq; 920aa2ae3e7SNikolay Aleksandrov struct sk_buff *skb; 921438ef2d0SNikolay Aleksandrov unsigned long llqt; 92208b202b6SYOSHIFUJI Hideaki struct ethhdr *eth; 923438ef2d0SNikolay Aleksandrov u16 llqt_srcs = 0; 92408b202b6SYOSHIFUJI Hideaki u8 *hopopt; 92508b202b6SYOSHIFUJI Hideaki 926aa2ae3e7SNikolay Aleksandrov mld_hdr_size = sizeof(*mldq); 927d3d065c0SNikolay Aleksandrov if (brmctx->multicast_mld_version == 2) { 928aa2ae3e7SNikolay Aleksandrov mld_hdr_size = sizeof(*mld2q); 929438ef2d0SNikolay Aleksandrov if (pg && with_srcs) { 930d3d065c0SNikolay Aleksandrov llqt = now + (brmctx->multicast_last_member_interval * 931d3d065c0SNikolay Aleksandrov brmctx->multicast_last_member_count); 932438ef2d0SNikolay Aleksandrov hlist_for_each_entry(ent, &pg->src_list, node) { 933438ef2d0SNikolay Aleksandrov if (over_llqt == time_after(ent->timer.expires, 934438ef2d0SNikolay Aleksandrov llqt) && 935438ef2d0SNikolay Aleksandrov ent->src_query_rexmit_cnt > 0) 936438ef2d0SNikolay Aleksandrov llqt_srcs++; 937438ef2d0SNikolay Aleksandrov } 938438ef2d0SNikolay Aleksandrov 939438ef2d0SNikolay Aleksandrov if (!llqt_srcs) 940438ef2d0SNikolay Aleksandrov return NULL; 941438ef2d0SNikolay Aleksandrov mld_hdr_size += llqt_srcs * sizeof(struct in6_addr); 942438ef2d0SNikolay Aleksandrov } 943438ef2d0SNikolay Aleksandrov } 944438ef2d0SNikolay Aleksandrov 945438ef2d0SNikolay Aleksandrov pkt_size = sizeof(*eth) + sizeof(*ip6h) + 8 + mld_hdr_size; 946438ef2d0SNikolay Aleksandrov if ((p && pkt_size > p->dev->mtu) || 947adc47037SNikolay Aleksandrov pkt_size > brmctx->br->dev->mtu) 948438ef2d0SNikolay Aleksandrov return NULL; 949438ef2d0SNikolay Aleksandrov 950adc47037SNikolay Aleksandrov skb = netdev_alloc_skb_ip_align(brmctx->br->dev, pkt_size); 95108b202b6SYOSHIFUJI Hideaki if (!skb) 95208b202b6SYOSHIFUJI Hideaki goto out; 95308b202b6SYOSHIFUJI Hideaki 95408b202b6SYOSHIFUJI Hideaki skb->protocol = htons(ETH_P_IPV6); 95508b202b6SYOSHIFUJI Hideaki 95608b202b6SYOSHIFUJI Hideaki /* Ethernet header */ 95708b202b6SYOSHIFUJI Hideaki skb_reset_mac_header(skb); 95808b202b6SYOSHIFUJI Hideaki eth = eth_hdr(skb); 95908b202b6SYOSHIFUJI Hideaki 960adc47037SNikolay Aleksandrov ether_addr_copy(eth->h_source, brmctx->br->dev->dev_addr); 96108b202b6SYOSHIFUJI Hideaki eth->h_proto = htons(ETH_P_IPV6); 96208b202b6SYOSHIFUJI Hideaki skb_put(skb, sizeof(*eth)); 96308b202b6SYOSHIFUJI Hideaki 96408b202b6SYOSHIFUJI Hideaki /* IPv6 header + HbH option */ 96508b202b6SYOSHIFUJI Hideaki skb_set_network_header(skb, skb->len); 96608b202b6SYOSHIFUJI Hideaki ip6h = ipv6_hdr(skb); 96708b202b6SYOSHIFUJI Hideaki 96808b202b6SYOSHIFUJI Hideaki *(__force __be32 *)ip6h = htonl(0x60000000); 969aa2ae3e7SNikolay Aleksandrov ip6h->payload_len = htons(8 + mld_hdr_size); 97008b202b6SYOSHIFUJI Hideaki ip6h->nexthdr = IPPROTO_HOPOPTS; 97108b202b6SYOSHIFUJI Hideaki ip6h->hop_limit = 1; 972438ef2d0SNikolay Aleksandrov ip6h->daddr = *ip6_dst; 973adc47037SNikolay Aleksandrov if (ipv6_dev_get_saddr(dev_net(brmctx->br->dev), brmctx->br->dev, 974adc47037SNikolay Aleksandrov &ip6h->daddr, 0, &ip6h->saddr)) { 975d1d81d4cSUlrich Weber kfree_skb(skb); 976adc47037SNikolay Aleksandrov br_opt_toggle(brmctx->br, BROPT_HAS_IPV6_ADDR, false); 977d1d81d4cSUlrich Weber return NULL; 978d1d81d4cSUlrich Weber } 9790888d5f3Sdaniel 980adc47037SNikolay Aleksandrov br_opt_toggle(brmctx->br, BROPT_HAS_IPV6_ADDR, true); 98136cff5a1SLinus Lüssing ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest); 98208b202b6SYOSHIFUJI Hideaki 98308b202b6SYOSHIFUJI Hideaki hopopt = (u8 *)(ip6h + 1); 98408b202b6SYOSHIFUJI Hideaki hopopt[0] = IPPROTO_ICMPV6; /* next hdr */ 98508b202b6SYOSHIFUJI Hideaki hopopt[1] = 0; /* length of HbH */ 98608b202b6SYOSHIFUJI Hideaki hopopt[2] = IPV6_TLV_ROUTERALERT; /* Router Alert */ 98708b202b6SYOSHIFUJI Hideaki hopopt[3] = 2; /* Length of RA Option */ 98808b202b6SYOSHIFUJI Hideaki hopopt[4] = 0; /* Type = 0x0000 (MLD) */ 98908b202b6SYOSHIFUJI Hideaki hopopt[5] = 0; 9901de5a71cSEldad Zack hopopt[6] = IPV6_TLV_PAD1; /* Pad1 */ 9911de5a71cSEldad Zack hopopt[7] = IPV6_TLV_PAD1; /* Pad1 */ 99208b202b6SYOSHIFUJI Hideaki 99308b202b6SYOSHIFUJI Hideaki skb_put(skb, sizeof(*ip6h) + 8); 99408b202b6SYOSHIFUJI Hideaki 99508b202b6SYOSHIFUJI Hideaki /* ICMPv6 */ 99608b202b6SYOSHIFUJI Hideaki skb_set_transport_header(skb, skb->len); 997438ef2d0SNikolay Aleksandrov interval = ipv6_addr_any(group) ? 998d3d065c0SNikolay Aleksandrov brmctx->multicast_query_response_interval : 999d3d065c0SNikolay Aleksandrov brmctx->multicast_last_member_interval; 10001080ab95SNikolay Aleksandrov *igmp_type = ICMPV6_MGM_QUERY; 1001d3d065c0SNikolay Aleksandrov switch (brmctx->multicast_mld_version) { 1002aa2ae3e7SNikolay Aleksandrov case 1: 1003aa2ae3e7SNikolay Aleksandrov mldq = (struct mld_msg *)icmp6_hdr(skb); 100408b202b6SYOSHIFUJI Hideaki mldq->mld_type = ICMPV6_MGM_QUERY; 100508b202b6SYOSHIFUJI Hideaki mldq->mld_code = 0; 100608b202b6SYOSHIFUJI Hideaki mldq->mld_cksum = 0; 100708b202b6SYOSHIFUJI Hideaki mldq->mld_maxdelay = htons((u16)jiffies_to_msecs(interval)); 100808b202b6SYOSHIFUJI Hideaki mldq->mld_reserved = 0; 1009438ef2d0SNikolay Aleksandrov mldq->mld_mca = *group; 1010438ef2d0SNikolay Aleksandrov csum = &mldq->mld_cksum; 1011438ef2d0SNikolay Aleksandrov csum_start = (void *)mldq; 1012aa2ae3e7SNikolay Aleksandrov break; 1013aa2ae3e7SNikolay Aleksandrov case 2: 1014aa2ae3e7SNikolay Aleksandrov mld2q = (struct mld2_query *)icmp6_hdr(skb); 101553631a5fSLance Richardson mld2q->mld2q_mrc = htons((u16)jiffies_to_msecs(interval)); 1016aa2ae3e7SNikolay Aleksandrov mld2q->mld2q_type = ICMPV6_MGM_QUERY; 1017aa2ae3e7SNikolay Aleksandrov mld2q->mld2q_code = 0; 1018aa2ae3e7SNikolay Aleksandrov mld2q->mld2q_cksum = 0; 1019aa2ae3e7SNikolay Aleksandrov mld2q->mld2q_resv1 = 0; 1020aa2ae3e7SNikolay Aleksandrov mld2q->mld2q_resv2 = 0; 1021438ef2d0SNikolay Aleksandrov mld2q->mld2q_suppress = sflag; 1022aa2ae3e7SNikolay Aleksandrov mld2q->mld2q_qrv = 2; 1023438ef2d0SNikolay Aleksandrov mld2q->mld2q_nsrcs = htons(llqt_srcs); 1024d3d065c0SNikolay Aleksandrov mld2q->mld2q_qqic = brmctx->multicast_query_interval / HZ; 1025438ef2d0SNikolay Aleksandrov mld2q->mld2q_mca = *group; 1026438ef2d0SNikolay Aleksandrov csum = &mld2q->mld2q_cksum; 1027438ef2d0SNikolay Aleksandrov csum_start = (void *)mld2q; 1028438ef2d0SNikolay Aleksandrov if (!pg || !with_srcs) 1029438ef2d0SNikolay Aleksandrov break; 1030438ef2d0SNikolay Aleksandrov 1031438ef2d0SNikolay Aleksandrov llqt_srcs = 0; 1032438ef2d0SNikolay Aleksandrov hlist_for_each_entry(ent, &pg->src_list, node) { 1033438ef2d0SNikolay Aleksandrov if (over_llqt == time_after(ent->timer.expires, 1034438ef2d0SNikolay Aleksandrov llqt) && 1035438ef2d0SNikolay Aleksandrov ent->src_query_rexmit_cnt > 0) { 1036deb96566SNikolay Aleksandrov mld2q->mld2q_srcs[llqt_srcs++] = ent->addr.src.ip6; 1037438ef2d0SNikolay Aleksandrov ent->src_query_rexmit_cnt--; 103842c11ccfSNikolay Aleksandrov if (need_rexmit && ent->src_query_rexmit_cnt) 103942c11ccfSNikolay Aleksandrov *need_rexmit = true; 1040438ef2d0SNikolay Aleksandrov } 1041438ef2d0SNikolay Aleksandrov } 1042438ef2d0SNikolay Aleksandrov if (WARN_ON(llqt_srcs != ntohs(mld2q->mld2q_nsrcs))) { 1043438ef2d0SNikolay Aleksandrov kfree_skb(skb); 1044438ef2d0SNikolay Aleksandrov return NULL; 1045438ef2d0SNikolay Aleksandrov } 1046aa2ae3e7SNikolay Aleksandrov break; 1047aa2ae3e7SNikolay Aleksandrov } 104808b202b6SYOSHIFUJI Hideaki 1049438ef2d0SNikolay Aleksandrov if (WARN_ON(!csum || !csum_start)) { 1050438ef2d0SNikolay Aleksandrov kfree_skb(skb); 1051438ef2d0SNikolay Aleksandrov return NULL; 1052438ef2d0SNikolay Aleksandrov } 1053438ef2d0SNikolay Aleksandrov 1054438ef2d0SNikolay Aleksandrov *csum = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, mld_hdr_size, 1055438ef2d0SNikolay Aleksandrov IPPROTO_ICMPV6, 1056438ef2d0SNikolay Aleksandrov csum_partial(csum_start, mld_hdr_size, 0)); 1057438ef2d0SNikolay Aleksandrov skb_put(skb, mld_hdr_size); 105808b202b6SYOSHIFUJI Hideaki __skb_pull(skb, sizeof(*eth)); 105908b202b6SYOSHIFUJI Hideaki 106008b202b6SYOSHIFUJI Hideaki out: 106108b202b6SYOSHIFUJI Hideaki return skb; 106208b202b6SYOSHIFUJI Hideaki } 106308b202b6SYOSHIFUJI Hideaki #endif 106408b202b6SYOSHIFUJI Hideaki 1065adc47037SNikolay Aleksandrov static struct sk_buff *br_multicast_alloc_query(struct net_bridge_mcast *brmctx, 1066438ef2d0SNikolay Aleksandrov struct net_bridge_port_group *pg, 1067438ef2d0SNikolay Aleksandrov struct br_ip *ip_dst, 1068438ef2d0SNikolay Aleksandrov struct br_ip *group, 1069438ef2d0SNikolay Aleksandrov bool with_srcs, bool over_lmqt, 107042c11ccfSNikolay Aleksandrov u8 sflag, u8 *igmp_type, 107142c11ccfSNikolay Aleksandrov bool *need_rexmit) 10728ef2a9a5SYOSHIFUJI Hideaki { 1073438ef2d0SNikolay Aleksandrov __be32 ip4_dst; 1074438ef2d0SNikolay Aleksandrov 1075438ef2d0SNikolay Aleksandrov switch (group->proto) { 10768ef2a9a5SYOSHIFUJI Hideaki case htons(ETH_P_IP): 1077eab3227bSNikolay Aleksandrov ip4_dst = ip_dst ? ip_dst->dst.ip4 : htonl(INADDR_ALLHOSTS_GROUP); 1078adc47037SNikolay Aleksandrov return br_ip4_multicast_alloc_query(brmctx, pg, 1079eab3227bSNikolay Aleksandrov ip4_dst, group->dst.ip4, 1080438ef2d0SNikolay Aleksandrov with_srcs, over_lmqt, 108142c11ccfSNikolay Aleksandrov sflag, igmp_type, 108242c11ccfSNikolay Aleksandrov need_rexmit); 1083dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 1084438ef2d0SNikolay Aleksandrov case htons(ETH_P_IPV6): { 1085438ef2d0SNikolay Aleksandrov struct in6_addr ip6_dst; 1086438ef2d0SNikolay Aleksandrov 1087438ef2d0SNikolay Aleksandrov if (ip_dst) 1088eab3227bSNikolay Aleksandrov ip6_dst = ip_dst->dst.ip6; 1089438ef2d0SNikolay Aleksandrov else 1090438ef2d0SNikolay Aleksandrov ipv6_addr_set(&ip6_dst, htonl(0xff020000), 0, 0, 1091438ef2d0SNikolay Aleksandrov htonl(1)); 1092438ef2d0SNikolay Aleksandrov 1093adc47037SNikolay Aleksandrov return br_ip6_multicast_alloc_query(brmctx, pg, 1094eab3227bSNikolay Aleksandrov &ip6_dst, &group->dst.ip6, 1095438ef2d0SNikolay Aleksandrov with_srcs, over_lmqt, 109642c11ccfSNikolay Aleksandrov sflag, igmp_type, 109742c11ccfSNikolay Aleksandrov need_rexmit); 1098438ef2d0SNikolay Aleksandrov } 109908b202b6SYOSHIFUJI Hideaki #endif 11008ef2a9a5SYOSHIFUJI Hideaki } 11018ef2a9a5SYOSHIFUJI Hideaki return NULL; 11028ef2a9a5SYOSHIFUJI Hideaki } 11038ef2a9a5SYOSHIFUJI Hideaki 1104cfd56754SCong Wang struct net_bridge_mdb_entry *br_multicast_new_group(struct net_bridge *br, 11055e923585SNikolay Aleksandrov struct br_ip *group) 1106eb1d1641SHerbert Xu { 1107eb1d1641SHerbert Xu struct net_bridge_mdb_entry *mp; 11084c0833bcSTobias Klauser int err; 1109eb1d1641SHerbert Xu 111019e3a9c9SNikolay Aleksandrov mp = br_mdb_ip_get(br, group); 111119e3a9c9SNikolay Aleksandrov if (mp) 111219e3a9c9SNikolay Aleksandrov return mp; 1113eb1d1641SHerbert Xu 111419e3a9c9SNikolay Aleksandrov if (atomic_read(&br->mdb_hash_tbl.nelems) >= br->hash_max) { 111519e3a9c9SNikolay Aleksandrov br_opt_toggle(br, BROPT_MULTICAST_ENABLED, false); 111619e3a9c9SNikolay Aleksandrov return ERR_PTR(-E2BIG); 1117eb1d1641SHerbert Xu } 1118eb1d1641SHerbert Xu 1119eb1d1641SHerbert Xu mp = kzalloc(sizeof(*mp), GFP_ATOMIC); 1120eb1d1641SHerbert Xu if (unlikely(!mp)) 11214c0833bcSTobias Klauser return ERR_PTR(-ENOMEM); 1122eb1d1641SHerbert Xu 1123eb1d1641SHerbert Xu mp->br = br; 11248ef2a9a5SYOSHIFUJI Hideaki mp->addr = *group; 1125e12cec65SNikolay Aleksandrov mp->mcast_gc.destroy = br_multicast_destroy_mdb_entry; 112688c1f37fSAllen Pais timer_setup(&mp->timer, br_multicast_group_expired, 0); 112719e3a9c9SNikolay Aleksandrov err = rhashtable_lookup_insert_fast(&br->mdb_hash_tbl, &mp->rhnode, 112819e3a9c9SNikolay Aleksandrov br_mdb_rht_params); 112919e3a9c9SNikolay Aleksandrov if (err) { 113019e3a9c9SNikolay Aleksandrov kfree(mp); 113119e3a9c9SNikolay Aleksandrov mp = ERR_PTR(err); 113219e3a9c9SNikolay Aleksandrov } else { 113319e3a9c9SNikolay Aleksandrov hlist_add_head_rcu(&mp->mdb_node, &br->mdb_list); 113419e3a9c9SNikolay Aleksandrov } 11351faabf2aSEric Dumazet 1136eb1d1641SHerbert Xu return mp; 1137eb1d1641SHerbert Xu } 1138eb1d1641SHerbert Xu 11398b671779SNikolay Aleksandrov static void br_multicast_group_src_expired(struct timer_list *t) 11408b671779SNikolay Aleksandrov { 11418b671779SNikolay Aleksandrov struct net_bridge_group_src *src = from_timer(src, t, timer); 11428b671779SNikolay Aleksandrov struct net_bridge_port_group *pg; 11438b671779SNikolay Aleksandrov struct net_bridge *br = src->br; 11448b671779SNikolay Aleksandrov 11458b671779SNikolay Aleksandrov spin_lock(&br->multicast_lock); 11468b671779SNikolay Aleksandrov if (hlist_unhashed(&src->node) || !netif_running(br->dev) || 11478b671779SNikolay Aleksandrov timer_pending(&src->timer)) 11488b671779SNikolay Aleksandrov goto out; 11498b671779SNikolay Aleksandrov 11508b671779SNikolay Aleksandrov pg = src->pg; 11518b671779SNikolay Aleksandrov if (pg->filter_mode == MCAST_INCLUDE) { 1152d5a10222SNikolay Aleksandrov br_multicast_del_group_src(src, false); 11538b671779SNikolay Aleksandrov if (!hlist_empty(&pg->src_list)) 11548b671779SNikolay Aleksandrov goto out; 11558b671779SNikolay Aleksandrov br_multicast_find_del_pg(br, pg); 11569116ffbfSNikolay Aleksandrov } else { 11579116ffbfSNikolay Aleksandrov br_multicast_fwd_src_handle(src); 11588b671779SNikolay Aleksandrov } 11599116ffbfSNikolay Aleksandrov 11608b671779SNikolay Aleksandrov out: 11618b671779SNikolay Aleksandrov spin_unlock(&br->multicast_lock); 11628b671779SNikolay Aleksandrov } 11638b671779SNikolay Aleksandrov 1164474ddb37SNikolay Aleksandrov struct net_bridge_group_src * 11658b671779SNikolay Aleksandrov br_multicast_find_group_src(struct net_bridge_port_group *pg, struct br_ip *ip) 11668b671779SNikolay Aleksandrov { 11678b671779SNikolay Aleksandrov struct net_bridge_group_src *ent; 11688b671779SNikolay Aleksandrov 11698b671779SNikolay Aleksandrov switch (ip->proto) { 11708b671779SNikolay Aleksandrov case htons(ETH_P_IP): 11718b671779SNikolay Aleksandrov hlist_for_each_entry(ent, &pg->src_list, node) 1172deb96566SNikolay Aleksandrov if (ip->src.ip4 == ent->addr.src.ip4) 11738b671779SNikolay Aleksandrov return ent; 11748b671779SNikolay Aleksandrov break; 11758b671779SNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6) 11768b671779SNikolay Aleksandrov case htons(ETH_P_IPV6): 11778b671779SNikolay Aleksandrov hlist_for_each_entry(ent, &pg->src_list, node) 1178deb96566SNikolay Aleksandrov if (!ipv6_addr_cmp(&ent->addr.src.ip6, &ip->src.ip6)) 11798b671779SNikolay Aleksandrov return ent; 11808b671779SNikolay Aleksandrov break; 11818b671779SNikolay Aleksandrov #endif 11828b671779SNikolay Aleksandrov } 11838b671779SNikolay Aleksandrov 11848b671779SNikolay Aleksandrov return NULL; 11858b671779SNikolay Aleksandrov } 11868b671779SNikolay Aleksandrov 11878b671779SNikolay Aleksandrov static struct net_bridge_group_src * 11888b671779SNikolay Aleksandrov br_multicast_new_group_src(struct net_bridge_port_group *pg, struct br_ip *src_ip) 11898b671779SNikolay Aleksandrov { 11908b671779SNikolay Aleksandrov struct net_bridge_group_src *grp_src; 11918b671779SNikolay Aleksandrov 11928b671779SNikolay Aleksandrov if (unlikely(pg->src_ents >= PG_SRC_ENT_LIMIT)) 11938b671779SNikolay Aleksandrov return NULL; 11948b671779SNikolay Aleksandrov 11958b671779SNikolay Aleksandrov switch (src_ip->proto) { 11968b671779SNikolay Aleksandrov case htons(ETH_P_IP): 1197deb96566SNikolay Aleksandrov if (ipv4_is_zeronet(src_ip->src.ip4) || 1198deb96566SNikolay Aleksandrov ipv4_is_multicast(src_ip->src.ip4)) 11998b671779SNikolay Aleksandrov return NULL; 12008b671779SNikolay Aleksandrov break; 12018b671779SNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6) 12028b671779SNikolay Aleksandrov case htons(ETH_P_IPV6): 1203deb96566SNikolay Aleksandrov if (ipv6_addr_any(&src_ip->src.ip6) || 1204deb96566SNikolay Aleksandrov ipv6_addr_is_multicast(&src_ip->src.ip6)) 12058b671779SNikolay Aleksandrov return NULL; 12068b671779SNikolay Aleksandrov break; 12078b671779SNikolay Aleksandrov #endif 12088b671779SNikolay Aleksandrov } 12098b671779SNikolay Aleksandrov 12108b671779SNikolay Aleksandrov grp_src = kzalloc(sizeof(*grp_src), GFP_ATOMIC); 12118b671779SNikolay Aleksandrov if (unlikely(!grp_src)) 12128b671779SNikolay Aleksandrov return NULL; 12138b671779SNikolay Aleksandrov 12148b671779SNikolay Aleksandrov grp_src->pg = pg; 1215085b53c8SNikolay Aleksandrov grp_src->br = pg->key.port->br; 12168b671779SNikolay Aleksandrov grp_src->addr = *src_ip; 1217e12cec65SNikolay Aleksandrov grp_src->mcast_gc.destroy = br_multicast_destroy_group_src; 12188b671779SNikolay Aleksandrov timer_setup(&grp_src->timer, br_multicast_group_src_expired, 0); 12198b671779SNikolay Aleksandrov 12208b671779SNikolay Aleksandrov hlist_add_head_rcu(&grp_src->node, &pg->src_list); 12218b671779SNikolay Aleksandrov pg->src_ents++; 12228b671779SNikolay Aleksandrov 12238b671779SNikolay Aleksandrov return grp_src; 12248b671779SNikolay Aleksandrov } 12258b671779SNikolay Aleksandrov 1226cfd56754SCong Wang struct net_bridge_port_group *br_multicast_new_port_group( 1227cfd56754SCong Wang struct net_bridge_port *port, 1228cfd56754SCong Wang struct br_ip *group, 1229ccb1c31aSAmerigo Wang struct net_bridge_port_group __rcu *next, 12306db6f0eaSFelix Fietkau unsigned char flags, 12318b671779SNikolay Aleksandrov const unsigned char *src, 12328f8cb77eSNikolay Aleksandrov u8 filter_mode, 12338f8cb77eSNikolay Aleksandrov u8 rt_protocol) 1234cfd56754SCong Wang { 1235cfd56754SCong Wang struct net_bridge_port_group *p; 1236cfd56754SCong Wang 1237cfd56754SCong Wang p = kzalloc(sizeof(*p), GFP_ATOMIC); 1238cfd56754SCong Wang if (unlikely(!p)) 1239cfd56754SCong Wang return NULL; 1240cfd56754SCong Wang 1241085b53c8SNikolay Aleksandrov p->key.addr = *group; 1242085b53c8SNikolay Aleksandrov p->key.port = port; 12439d06b6d8SElad Raz p->flags = flags; 12448b671779SNikolay Aleksandrov p->filter_mode = filter_mode; 12458f8cb77eSNikolay Aleksandrov p->rt_protocol = rt_protocol; 12465b163288SNikolay Aleksandrov p->eht_host_tree = RB_ROOT; 1247baa74d39SNikolay Aleksandrov p->eht_set_tree = RB_ROOT; 1248e12cec65SNikolay Aleksandrov p->mcast_gc.destroy = br_multicast_destroy_port_group; 12498b671779SNikolay Aleksandrov INIT_HLIST_HEAD(&p->src_list); 1250085b53c8SNikolay Aleksandrov 1251085b53c8SNikolay Aleksandrov if (!br_multicast_is_star_g(group) && 1252085b53c8SNikolay Aleksandrov rhashtable_lookup_insert_fast(&port->br->sg_port_tbl, &p->rhnode, 1253085b53c8SNikolay Aleksandrov br_sg_port_rht_params)) { 1254085b53c8SNikolay Aleksandrov kfree(p); 1255085b53c8SNikolay Aleksandrov return NULL; 1256085b53c8SNikolay Aleksandrov } 1257085b53c8SNikolay Aleksandrov 1258eca2a43bSstephen hemminger rcu_assign_pointer(p->next, next); 125988c1f37fSAllen Pais timer_setup(&p->timer, br_multicast_port_group_expired, 0); 126042c11ccfSNikolay Aleksandrov timer_setup(&p->rexmit_timer, br_multicast_port_group_rexmit, 0); 126142c11ccfSNikolay Aleksandrov hlist_add_head(&p->mglist, &port->mglist); 12626db6f0eaSFelix Fietkau 12636db6f0eaSFelix Fietkau if (src) 12646db6f0eaSFelix Fietkau memcpy(p->eth_addr, src, ETH_ALEN); 12656db6f0eaSFelix Fietkau else 12661bfe45f4SMao Wenan eth_broadcast_addr(p->eth_addr); 12676db6f0eaSFelix Fietkau 1268cfd56754SCong Wang return p; 1269cfd56754SCong Wang } 1270cfd56754SCong Wang 12711bc844eeSNikolay Aleksandrov void br_multicast_host_join(struct net_bridge_mdb_entry *mp, bool notify) 12721bc844eeSNikolay Aleksandrov { 12731bc844eeSNikolay Aleksandrov if (!mp->host_joined) { 12741bc844eeSNikolay Aleksandrov mp->host_joined = true; 1275094b82fdSNikolay Aleksandrov if (br_multicast_is_star_g(&mp->addr)) 1276094b82fdSNikolay Aleksandrov br_multicast_star_g_host_state(mp); 12771bc844eeSNikolay Aleksandrov if (notify) 127881f19838SNikolay Aleksandrov br_mdb_notify(mp->br->dev, mp, NULL, RTM_NEWMDB); 12791bc844eeSNikolay Aleksandrov } 1280955062b0SNikolay Aleksandrov 1281955062b0SNikolay Aleksandrov if (br_group_is_l2(&mp->addr)) 1282955062b0SNikolay Aleksandrov return; 1283955062b0SNikolay Aleksandrov 1284d3d065c0SNikolay Aleksandrov mod_timer(&mp->timer, 1285d3d065c0SNikolay Aleksandrov jiffies + mp->br->multicast_ctx.multicast_membership_interval); 12861bc844eeSNikolay Aleksandrov } 12871bc844eeSNikolay Aleksandrov 12881bc844eeSNikolay Aleksandrov void br_multicast_host_leave(struct net_bridge_mdb_entry *mp, bool notify) 12891bc844eeSNikolay Aleksandrov { 12901bc844eeSNikolay Aleksandrov if (!mp->host_joined) 12911bc844eeSNikolay Aleksandrov return; 12921bc844eeSNikolay Aleksandrov 12931bc844eeSNikolay Aleksandrov mp->host_joined = false; 1294094b82fdSNikolay Aleksandrov if (br_multicast_is_star_g(&mp->addr)) 1295094b82fdSNikolay Aleksandrov br_multicast_star_g_host_state(mp); 12961bc844eeSNikolay Aleksandrov if (notify) 129781f19838SNikolay Aleksandrov br_mdb_notify(mp->br->dev, mp, NULL, RTM_DELMDB); 12981bc844eeSNikolay Aleksandrov } 12991bc844eeSNikolay Aleksandrov 1300b0812368SNikolay Aleksandrov static struct net_bridge_port_group * 1301adc47037SNikolay Aleksandrov __br_multicast_add_group(struct net_bridge_mcast *brmctx, 1302adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 13036db6f0eaSFelix Fietkau struct br_ip *group, 13048b671779SNikolay Aleksandrov const unsigned char *src, 13050436862eSNikolay Aleksandrov u8 filter_mode, 13069116ffbfSNikolay Aleksandrov bool igmpv2_mldv1, 13079116ffbfSNikolay Aleksandrov bool blocked) 1308eb1d1641SHerbert Xu { 1309e8051688SEric Dumazet struct net_bridge_port_group __rcu **pp; 1310b0812368SNikolay Aleksandrov struct net_bridge_port_group *p = NULL; 13115e923585SNikolay Aleksandrov struct net_bridge_mdb_entry *mp; 1312454594f3SLinus Lüssing unsigned long now = jiffies; 1313eb1d1641SHerbert Xu 1314adc47037SNikolay Aleksandrov if (!netif_running(brmctx->br->dev) || 1315adc47037SNikolay Aleksandrov (pmctx && pmctx->port->state == BR_STATE_DISABLED)) 1316eb1d1641SHerbert Xu goto out; 1317eb1d1641SHerbert Xu 1318adc47037SNikolay Aleksandrov mp = br_multicast_new_group(brmctx->br, group); 13194c0833bcSTobias Klauser if (IS_ERR(mp)) 13201697291dSXu Wang return ERR_CAST(mp); 1321eb1d1641SHerbert Xu 1322adc47037SNikolay Aleksandrov if (!pmctx) { 13231bc844eeSNikolay Aleksandrov br_multicast_host_join(mp, true); 1324eb1d1641SHerbert Xu goto out; 1325eb1d1641SHerbert Xu } 1326eb1d1641SHerbert Xu 1327e8051688SEric Dumazet for (pp = &mp->ports; 1328adc47037SNikolay Aleksandrov (p = mlock_dereference(*pp, brmctx->br)) != NULL; 1329e8051688SEric Dumazet pp = &p->next) { 1330adc47037SNikolay Aleksandrov if (br_port_group_equal(p, pmctx->port, src)) 1331454594f3SLinus Lüssing goto found; 1332adc47037SNikolay Aleksandrov if ((unsigned long)p->key.port < (unsigned long)pmctx->port) 1333eb1d1641SHerbert Xu break; 1334eb1d1641SHerbert Xu } 1335eb1d1641SHerbert Xu 1336adc47037SNikolay Aleksandrov p = br_multicast_new_port_group(pmctx->port, group, *pp, 0, src, 1337955062b0SNikolay Aleksandrov filter_mode, RTPROT_KERNEL); 1338b0812368SNikolay Aleksandrov if (unlikely(!p)) { 1339b0812368SNikolay Aleksandrov p = ERR_PTR(-ENOMEM); 1340b0812368SNikolay Aleksandrov goto out; 1341b0812368SNikolay Aleksandrov } 1342eb1d1641SHerbert Xu rcu_assign_pointer(*pp, p); 13439116ffbfSNikolay Aleksandrov if (blocked) 13449116ffbfSNikolay Aleksandrov p->flags |= MDB_PG_FLAGS_BLOCKED; 1345adc47037SNikolay Aleksandrov br_mdb_notify(brmctx->br->dev, mp, p, RTM_NEWMDB); 1346eb1d1641SHerbert Xu 1347454594f3SLinus Lüssing found: 13480436862eSNikolay Aleksandrov if (igmpv2_mldv1) 1349d3d065c0SNikolay Aleksandrov mod_timer(&p->timer, 1350adc47037SNikolay Aleksandrov now + brmctx->multicast_membership_interval); 135181f19838SNikolay Aleksandrov 1352eb1d1641SHerbert Xu out: 1353b0812368SNikolay Aleksandrov return p; 1354b0812368SNikolay Aleksandrov } 1355eb1d1641SHerbert Xu 1356adc47037SNikolay Aleksandrov static int br_multicast_add_group(struct net_bridge_mcast *brmctx, 1357adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 1358b0812368SNikolay Aleksandrov struct br_ip *group, 1359b0812368SNikolay Aleksandrov const unsigned char *src, 1360b0812368SNikolay Aleksandrov u8 filter_mode, 1361b0812368SNikolay Aleksandrov bool igmpv2_mldv1) 1362b0812368SNikolay Aleksandrov { 1363b0812368SNikolay Aleksandrov struct net_bridge_port_group *pg; 1364b0812368SNikolay Aleksandrov int err; 1365b0812368SNikolay Aleksandrov 1366adc47037SNikolay Aleksandrov spin_lock(&brmctx->br->multicast_lock); 1367adc47037SNikolay Aleksandrov pg = __br_multicast_add_group(brmctx, pmctx, group, src, filter_mode, 13689116ffbfSNikolay Aleksandrov igmpv2_mldv1, false); 1369b0812368SNikolay Aleksandrov /* NULL is considered valid for host joined groups */ 13708d21c882SJiapeng Zhong err = PTR_ERR_OR_ZERO(pg); 1371adc47037SNikolay Aleksandrov spin_unlock(&brmctx->br->multicast_lock); 1372b0812368SNikolay Aleksandrov 1373eb1d1641SHerbert Xu return err; 1374eb1d1641SHerbert Xu } 1375eb1d1641SHerbert Xu 1376adc47037SNikolay Aleksandrov static int br_ip4_multicast_add_group(struct net_bridge_mcast *brmctx, 1377adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 1378b0e9a30dSVlad Yasevich __be32 group, 13796db6f0eaSFelix Fietkau __u16 vid, 13808b671779SNikolay Aleksandrov const unsigned char *src, 13818b671779SNikolay Aleksandrov bool igmpv2) 13828ef2a9a5SYOSHIFUJI Hideaki { 13838ef2a9a5SYOSHIFUJI Hideaki struct br_ip br_group; 13848b671779SNikolay Aleksandrov u8 filter_mode; 13858ef2a9a5SYOSHIFUJI Hideaki 13868ef2a9a5SYOSHIFUJI Hideaki if (ipv4_is_local_multicast(group)) 13878ef2a9a5SYOSHIFUJI Hideaki return 0; 13888ef2a9a5SYOSHIFUJI Hideaki 13891515a63fSNikolay Aleksandrov memset(&br_group, 0, sizeof(br_group)); 1390eab3227bSNikolay Aleksandrov br_group.dst.ip4 = group; 13918ef2a9a5SYOSHIFUJI Hideaki br_group.proto = htons(ETH_P_IP); 1392b0e9a30dSVlad Yasevich br_group.vid = vid; 13938b671779SNikolay Aleksandrov filter_mode = igmpv2 ? MCAST_EXCLUDE : MCAST_INCLUDE; 13948ef2a9a5SYOSHIFUJI Hideaki 1395adc47037SNikolay Aleksandrov return br_multicast_add_group(brmctx, pmctx, &br_group, src, 1396adc47037SNikolay Aleksandrov filter_mode, igmpv2); 13978ef2a9a5SYOSHIFUJI Hideaki } 13988ef2a9a5SYOSHIFUJI Hideaki 1399dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 1400adc47037SNikolay Aleksandrov static int br_ip6_multicast_add_group(struct net_bridge_mcast *brmctx, 1401adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 1402b0e9a30dSVlad Yasevich const struct in6_addr *group, 14036db6f0eaSFelix Fietkau __u16 vid, 14048b671779SNikolay Aleksandrov const unsigned char *src, 14058b671779SNikolay Aleksandrov bool mldv1) 140608b202b6SYOSHIFUJI Hideaki { 140708b202b6SYOSHIFUJI Hideaki struct br_ip br_group; 14088b671779SNikolay Aleksandrov u8 filter_mode; 140908b202b6SYOSHIFUJI Hideaki 14103c3769e6SLinus Lüssing if (ipv6_addr_is_ll_all_nodes(group)) 141108b202b6SYOSHIFUJI Hideaki return 0; 141208b202b6SYOSHIFUJI Hideaki 141319e3a9c9SNikolay Aleksandrov memset(&br_group, 0, sizeof(br_group)); 1414eab3227bSNikolay Aleksandrov br_group.dst.ip6 = *group; 14159cc6e0c4SLinus Lüssing br_group.proto = htons(ETH_P_IPV6); 1416b0e9a30dSVlad Yasevich br_group.vid = vid; 14178b671779SNikolay Aleksandrov filter_mode = mldv1 ? MCAST_EXCLUDE : MCAST_INCLUDE; 141808b202b6SYOSHIFUJI Hideaki 1419adc47037SNikolay Aleksandrov return br_multicast_add_group(brmctx, pmctx, &br_group, src, 1420adc47037SNikolay Aleksandrov filter_mode, mldv1); 142108b202b6SYOSHIFUJI Hideaki } 142208b202b6SYOSHIFUJI Hideaki #endif 142308b202b6SYOSHIFUJI Hideaki 1424ed2d3597SLinus Lüssing static bool br_multicast_rport_del(struct hlist_node *rlist) 1425ed2d3597SLinus Lüssing { 1426ed2d3597SLinus Lüssing if (hlist_unhashed(rlist)) 1427ed2d3597SLinus Lüssing return false; 1428ed2d3597SLinus Lüssing 1429ed2d3597SLinus Lüssing hlist_del_init_rcu(rlist); 1430ed2d3597SLinus Lüssing return true; 1431ed2d3597SLinus Lüssing } 1432ed2d3597SLinus Lüssing 1433adc47037SNikolay Aleksandrov static bool br_ip4_multicast_rport_del(struct net_bridge_mcast_port *pmctx) 1434ed2d3597SLinus Lüssing { 1435adc47037SNikolay Aleksandrov return br_multicast_rport_del(&pmctx->ip4_rlist); 1436ed2d3597SLinus Lüssing } 1437ed2d3597SLinus Lüssing 1438adc47037SNikolay Aleksandrov static bool br_ip6_multicast_rport_del(struct net_bridge_mcast_port *pmctx) 1439a3c02e76SLinus Lüssing { 1440a3c02e76SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 1441adc47037SNikolay Aleksandrov return br_multicast_rport_del(&pmctx->ip6_rlist); 1442a3c02e76SLinus Lüssing #else 1443a3c02e76SLinus Lüssing return false; 1444a3c02e76SLinus Lüssing #endif 1445a3c02e76SLinus Lüssing } 1446a3c02e76SLinus Lüssing 14479632233eSNikolay Aleksandrov static void br_multicast_router_expired(struct net_bridge_mcast_port *pmctx, 1448ee5fb222SLinus Lüssing struct timer_list *t, 1449ee5fb222SLinus Lüssing struct hlist_node *rlist) 1450eb1d1641SHerbert Xu { 14519632233eSNikolay Aleksandrov struct net_bridge *br = pmctx->port->br; 1452ed2d3597SLinus Lüssing bool del; 1453eb1d1641SHerbert Xu 1454eb1d1641SHerbert Xu spin_lock(&br->multicast_lock); 14559632233eSNikolay Aleksandrov if (pmctx->multicast_router == MDB_RTR_TYPE_DISABLED || 14569632233eSNikolay Aleksandrov pmctx->multicast_router == MDB_RTR_TYPE_PERM || 1457ee5fb222SLinus Lüssing timer_pending(t)) 1458eb1d1641SHerbert Xu goto out; 1459eb1d1641SHerbert Xu 1460ed2d3597SLinus Lüssing del = br_multicast_rport_del(rlist); 1461adc47037SNikolay Aleksandrov br_multicast_rport_del_notify(pmctx, del); 1462eb1d1641SHerbert Xu out: 1463eb1d1641SHerbert Xu spin_unlock(&br->multicast_lock); 1464eb1d1641SHerbert Xu } 1465eb1d1641SHerbert Xu 1466ee5fb222SLinus Lüssing static void br_ip4_multicast_router_expired(struct timer_list *t) 1467ee5fb222SLinus Lüssing { 14689632233eSNikolay Aleksandrov struct net_bridge_mcast_port *pmctx = from_timer(pmctx, t, 14699632233eSNikolay Aleksandrov ip4_mc_router_timer); 1470ee5fb222SLinus Lüssing 14719632233eSNikolay Aleksandrov br_multicast_router_expired(pmctx, t, &pmctx->ip4_rlist); 1472ee5fb222SLinus Lüssing } 1473ee5fb222SLinus Lüssing 1474a3c02e76SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 1475a3c02e76SLinus Lüssing static void br_ip6_multicast_router_expired(struct timer_list *t) 1476a3c02e76SLinus Lüssing { 14779632233eSNikolay Aleksandrov struct net_bridge_mcast_port *pmctx = from_timer(pmctx, t, 14789632233eSNikolay Aleksandrov ip6_mc_router_timer); 1479a3c02e76SLinus Lüssing 14809632233eSNikolay Aleksandrov br_multicast_router_expired(pmctx, t, &pmctx->ip6_rlist); 1481a3c02e76SLinus Lüssing } 1482a3c02e76SLinus Lüssing #endif 1483a3c02e76SLinus Lüssing 148477041420SYotam Gigi static void br_mc_router_state_change(struct net_bridge *p, 148577041420SYotam Gigi bool is_mc_router) 148677041420SYotam Gigi { 148777041420SYotam Gigi struct switchdev_attr attr = { 148877041420SYotam Gigi .orig_dev = p->dev, 148977041420SYotam Gigi .id = SWITCHDEV_ATTR_ID_BRIDGE_MROUTER, 149077041420SYotam Gigi .flags = SWITCHDEV_F_DEFER, 149177041420SYotam Gigi .u.mrouter = is_mc_router, 149277041420SYotam Gigi }; 149377041420SYotam Gigi 1494dcbdf135SVladimir Oltean switchdev_port_attr_set(p->dev, &attr, NULL); 149577041420SYotam Gigi } 149677041420SYotam Gigi 1497d3d065c0SNikolay Aleksandrov static void br_multicast_local_router_expired(struct net_bridge_mcast *brmctx, 1498ee5fb222SLinus Lüssing struct timer_list *timer) 1499eb1d1641SHerbert Xu { 1500d3d065c0SNikolay Aleksandrov spin_lock(&brmctx->br->multicast_lock); 1501d3d065c0SNikolay Aleksandrov if (brmctx->multicast_router == MDB_RTR_TYPE_DISABLED || 1502d3d065c0SNikolay Aleksandrov brmctx->multicast_router == MDB_RTR_TYPE_PERM || 1503d3d065c0SNikolay Aleksandrov br_ip4_multicast_is_router(brmctx) || 1504d3d065c0SNikolay Aleksandrov br_ip6_multicast_is_router(brmctx)) 150577041420SYotam Gigi goto out; 150677041420SYotam Gigi 1507d3d065c0SNikolay Aleksandrov br_mc_router_state_change(brmctx->br, false); 150877041420SYotam Gigi out: 1509d3d065c0SNikolay Aleksandrov spin_unlock(&brmctx->br->multicast_lock); 1510eb1d1641SHerbert Xu } 1511eb1d1641SHerbert Xu 1512ee5fb222SLinus Lüssing static void br_ip4_multicast_local_router_expired(struct timer_list *t) 1513ee5fb222SLinus Lüssing { 1514d3d065c0SNikolay Aleksandrov struct net_bridge_mcast *brmctx = from_timer(brmctx, t, 1515d3d065c0SNikolay Aleksandrov ip4_mc_router_timer); 1516ee5fb222SLinus Lüssing 1517d3d065c0SNikolay Aleksandrov br_multicast_local_router_expired(brmctx, t); 1518ee5fb222SLinus Lüssing } 1519ee5fb222SLinus Lüssing 1520a3c02e76SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 1521a3c02e76SLinus Lüssing static void br_ip6_multicast_local_router_expired(struct timer_list *t) 1522a3c02e76SLinus Lüssing { 1523d3d065c0SNikolay Aleksandrov struct net_bridge_mcast *brmctx = from_timer(brmctx, t, 1524d3d065c0SNikolay Aleksandrov ip6_mc_router_timer); 1525a3c02e76SLinus Lüssing 1526d3d065c0SNikolay Aleksandrov br_multicast_local_router_expired(brmctx, t); 1527a3c02e76SLinus Lüssing } 1528a3c02e76SLinus Lüssing #endif 1529a3c02e76SLinus Lüssing 1530d3d065c0SNikolay Aleksandrov static void br_multicast_querier_expired(struct net_bridge_mcast *brmctx, 153190010b36SLinus Lüssing struct bridge_mcast_own_query *query) 1532c83b8fabSHerbert Xu { 1533d3d065c0SNikolay Aleksandrov spin_lock(&brmctx->br->multicast_lock); 1534d3d065c0SNikolay Aleksandrov if (!netif_running(brmctx->br->dev) || 1535d3d065c0SNikolay Aleksandrov !br_opt_get(brmctx->br, BROPT_MULTICAST_ENABLED)) 1536c83b8fabSHerbert Xu goto out; 1537c83b8fabSHerbert Xu 1538adc47037SNikolay Aleksandrov br_multicast_start_querier(brmctx, query); 1539c83b8fabSHerbert Xu 1540c83b8fabSHerbert Xu out: 1541d3d065c0SNikolay Aleksandrov spin_unlock(&brmctx->br->multicast_lock); 1542c83b8fabSHerbert Xu } 1543c83b8fabSHerbert Xu 154488c1f37fSAllen Pais static void br_ip4_multicast_querier_expired(struct timer_list *t) 1545cc0fdd80SLinus Lüssing { 1546d3d065c0SNikolay Aleksandrov struct net_bridge_mcast *brmctx = from_timer(brmctx, t, 1547d3d065c0SNikolay Aleksandrov ip4_other_query.timer); 1548cc0fdd80SLinus Lüssing 1549d3d065c0SNikolay Aleksandrov br_multicast_querier_expired(brmctx, &brmctx->ip4_own_query); 1550cc0fdd80SLinus Lüssing } 1551cc0fdd80SLinus Lüssing 1552cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 155388c1f37fSAllen Pais static void br_ip6_multicast_querier_expired(struct timer_list *t) 1554cc0fdd80SLinus Lüssing { 1555d3d065c0SNikolay Aleksandrov struct net_bridge_mcast *brmctx = from_timer(brmctx, t, 1556d3d065c0SNikolay Aleksandrov ip6_other_query.timer); 1557cc0fdd80SLinus Lüssing 1558d3d065c0SNikolay Aleksandrov br_multicast_querier_expired(brmctx, &brmctx->ip6_own_query); 1559cc0fdd80SLinus Lüssing } 1560cc0fdd80SLinus Lüssing #endif 1561cc0fdd80SLinus Lüssing 1562adc47037SNikolay Aleksandrov static void br_multicast_select_own_querier(struct net_bridge_mcast *brmctx, 1563dc4eb53aSLinus Lüssing struct br_ip *ip, 1564dc4eb53aSLinus Lüssing struct sk_buff *skb) 1565dc4eb53aSLinus Lüssing { 1566dc4eb53aSLinus Lüssing if (ip->proto == htons(ETH_P_IP)) 1567d3d065c0SNikolay Aleksandrov brmctx->ip4_querier.addr.src.ip4 = ip_hdr(skb)->saddr; 1568dc4eb53aSLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 1569dc4eb53aSLinus Lüssing else 1570d3d065c0SNikolay Aleksandrov brmctx->ip6_querier.addr.src.ip6 = ipv6_hdr(skb)->saddr; 1571dc4eb53aSLinus Lüssing #endif 1572dc4eb53aSLinus Lüssing } 1573dc4eb53aSLinus Lüssing 1574adc47037SNikolay Aleksandrov static void __br_multicast_send_query(struct net_bridge_mcast *brmctx, 1575adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 1576438ef2d0SNikolay Aleksandrov struct net_bridge_port_group *pg, 1577438ef2d0SNikolay Aleksandrov struct br_ip *ip_dst, 1578438ef2d0SNikolay Aleksandrov struct br_ip *group, 1579438ef2d0SNikolay Aleksandrov bool with_srcs, 158042c11ccfSNikolay Aleksandrov u8 sflag, 158142c11ccfSNikolay Aleksandrov bool *need_rexmit) 1582eb1d1641SHerbert Xu { 1583438ef2d0SNikolay Aleksandrov bool over_lmqt = !!sflag; 1584eb1d1641SHerbert Xu struct sk_buff *skb; 15851080ab95SNikolay Aleksandrov u8 igmp_type; 1586eb1d1641SHerbert Xu 1587438ef2d0SNikolay Aleksandrov again_under_lmqt: 1588adc47037SNikolay Aleksandrov skb = br_multicast_alloc_query(brmctx, pg, ip_dst, group, with_srcs, 158942c11ccfSNikolay Aleksandrov over_lmqt, sflag, &igmp_type, 159042c11ccfSNikolay Aleksandrov need_rexmit); 1591eb1d1641SHerbert Xu if (!skb) 15928ef2a9a5SYOSHIFUJI Hideaki return; 1593eb1d1641SHerbert Xu 1594adc47037SNikolay Aleksandrov if (pmctx) { 1595adc47037SNikolay Aleksandrov skb->dev = pmctx->port->dev; 1596adc47037SNikolay Aleksandrov br_multicast_count(brmctx->br, pmctx->port, skb, igmp_type, 15971080ab95SNikolay Aleksandrov BR_MCAST_DIR_TX); 159829a26a56SEric W. Biederman NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, 1599adc47037SNikolay Aleksandrov dev_net(pmctx->port->dev), NULL, skb, NULL, skb->dev, 1600f0b4eeceSLinus Lüssing br_dev_queue_push_xmit); 1601438ef2d0SNikolay Aleksandrov 1602438ef2d0SNikolay Aleksandrov if (over_lmqt && with_srcs && sflag) { 1603438ef2d0SNikolay Aleksandrov over_lmqt = false; 1604438ef2d0SNikolay Aleksandrov goto again_under_lmqt; 1605438ef2d0SNikolay Aleksandrov } 1606dc4eb53aSLinus Lüssing } else { 1607adc47037SNikolay Aleksandrov br_multicast_select_own_querier(brmctx, group, skb); 1608adc47037SNikolay Aleksandrov br_multicast_count(brmctx->br, NULL, skb, igmp_type, 16091080ab95SNikolay Aleksandrov BR_MCAST_DIR_RX); 1610eb1d1641SHerbert Xu netif_rx(skb); 16118ef2a9a5SYOSHIFUJI Hideaki } 1612dc4eb53aSLinus Lüssing } 1613eb1d1641SHerbert Xu 1614adc47037SNikolay Aleksandrov static void br_multicast_send_query(struct net_bridge_mcast *brmctx, 1615adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 161690010b36SLinus Lüssing struct bridge_mcast_own_query *own_query) 16178ef2a9a5SYOSHIFUJI Hideaki { 161890010b36SLinus Lüssing struct bridge_mcast_other_query *other_query = NULL; 16195e923585SNikolay Aleksandrov struct br_ip br_group; 16205e923585SNikolay Aleksandrov unsigned long time; 16218ef2a9a5SYOSHIFUJI Hideaki 1622adc47037SNikolay Aleksandrov if (!netif_running(brmctx->br->dev) || 1623adc47037SNikolay Aleksandrov !br_opt_get(brmctx->br, BROPT_MULTICAST_ENABLED) || 1624adc47037SNikolay Aleksandrov !br_opt_get(brmctx->br, BROPT_MULTICAST_QUERIER)) 16258ef2a9a5SYOSHIFUJI Hideaki return; 16268ef2a9a5SYOSHIFUJI Hideaki 1627eab3227bSNikolay Aleksandrov memset(&br_group.dst, 0, sizeof(br_group.dst)); 16288ef2a9a5SYOSHIFUJI Hideaki 1629adc47037SNikolay Aleksandrov if (pmctx ? (own_query == &pmctx->ip4_own_query) : 1630d3d065c0SNikolay Aleksandrov (own_query == &brmctx->ip4_own_query)) { 1631d3d065c0SNikolay Aleksandrov other_query = &brmctx->ip4_other_query; 163208b202b6SYOSHIFUJI Hideaki br_group.proto = htons(ETH_P_IP); 1633dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 1634cc0fdd80SLinus Lüssing } else { 1635d3d065c0SNikolay Aleksandrov other_query = &brmctx->ip6_other_query; 163608b202b6SYOSHIFUJI Hideaki br_group.proto = htons(ETH_P_IPV6); 163708b202b6SYOSHIFUJI Hideaki #endif 1638eb1d1641SHerbert Xu } 1639eb1d1641SHerbert Xu 164090010b36SLinus Lüssing if (!other_query || timer_pending(&other_query->timer)) 1641cc0fdd80SLinus Lüssing return; 1642cc0fdd80SLinus Lüssing 1643adc47037SNikolay Aleksandrov __br_multicast_send_query(brmctx, pmctx, NULL, NULL, &br_group, false, 1644adc47037SNikolay Aleksandrov 0, NULL); 1645cc0fdd80SLinus Lüssing 1646cc0fdd80SLinus Lüssing time = jiffies; 1647d3d065c0SNikolay Aleksandrov time += own_query->startup_sent < brmctx->multicast_startup_query_count ? 1648d3d065c0SNikolay Aleksandrov brmctx->multicast_startup_query_interval : 1649d3d065c0SNikolay Aleksandrov brmctx->multicast_query_interval; 165090010b36SLinus Lüssing mod_timer(&own_query->timer, time); 1651cc0fdd80SLinus Lüssing } 1652cc0fdd80SLinus Lüssing 165390010b36SLinus Lüssing static void 16549632233eSNikolay Aleksandrov br_multicast_port_query_expired(struct net_bridge_mcast_port *pmctx, 165590010b36SLinus Lüssing struct bridge_mcast_own_query *query) 1656eb1d1641SHerbert Xu { 16579632233eSNikolay Aleksandrov struct net_bridge *br = pmctx->port->br; 1658eb1d1641SHerbert Xu 1659eb1d1641SHerbert Xu spin_lock(&br->multicast_lock); 16609632233eSNikolay Aleksandrov if (pmctx->port->state == BR_STATE_DISABLED || 16619632233eSNikolay Aleksandrov pmctx->port->state == BR_STATE_BLOCKING) 1662eb1d1641SHerbert Xu goto out; 1663eb1d1641SHerbert Xu 1664d3d065c0SNikolay Aleksandrov if (query->startup_sent < br->multicast_ctx.multicast_startup_query_count) 1665cc0fdd80SLinus Lüssing query->startup_sent++; 1666eb1d1641SHerbert Xu 1667adc47037SNikolay Aleksandrov br_multicast_send_query(&br->multicast_ctx, pmctx, query); 1668eb1d1641SHerbert Xu 1669eb1d1641SHerbert Xu out: 1670eb1d1641SHerbert Xu spin_unlock(&br->multicast_lock); 1671eb1d1641SHerbert Xu } 1672eb1d1641SHerbert Xu 167388c1f37fSAllen Pais static void br_ip4_multicast_port_query_expired(struct timer_list *t) 1674cc0fdd80SLinus Lüssing { 16759632233eSNikolay Aleksandrov struct net_bridge_mcast_port *pmctx = from_timer(pmctx, t, 16769632233eSNikolay Aleksandrov ip4_own_query.timer); 1677cc0fdd80SLinus Lüssing 16789632233eSNikolay Aleksandrov br_multicast_port_query_expired(pmctx, &pmctx->ip4_own_query); 1679cc0fdd80SLinus Lüssing } 1680cc0fdd80SLinus Lüssing 1681cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 168288c1f37fSAllen Pais static void br_ip6_multicast_port_query_expired(struct timer_list *t) 1683cc0fdd80SLinus Lüssing { 16849632233eSNikolay Aleksandrov struct net_bridge_mcast_port *pmctx = from_timer(pmctx, t, 16859632233eSNikolay Aleksandrov ip6_own_query.timer); 1686cc0fdd80SLinus Lüssing 16879632233eSNikolay Aleksandrov br_multicast_port_query_expired(pmctx, &pmctx->ip6_own_query); 1688cc0fdd80SLinus Lüssing } 1689cc0fdd80SLinus Lüssing #endif 1690cc0fdd80SLinus Lüssing 169142c11ccfSNikolay Aleksandrov static void br_multicast_port_group_rexmit(struct timer_list *t) 169242c11ccfSNikolay Aleksandrov { 169342c11ccfSNikolay Aleksandrov struct net_bridge_port_group *pg = from_timer(pg, t, rexmit_timer); 169442c11ccfSNikolay Aleksandrov struct bridge_mcast_other_query *other_query = NULL; 1695085b53c8SNikolay Aleksandrov struct net_bridge *br = pg->key.port->br; 1696adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx; 1697adc47037SNikolay Aleksandrov struct net_bridge_mcast *brmctx; 169842c11ccfSNikolay Aleksandrov bool need_rexmit = false; 169942c11ccfSNikolay Aleksandrov 170042c11ccfSNikolay Aleksandrov spin_lock(&br->multicast_lock); 170142c11ccfSNikolay Aleksandrov if (!netif_running(br->dev) || hlist_unhashed(&pg->mglist) || 170242c11ccfSNikolay Aleksandrov !br_opt_get(br, BROPT_MULTICAST_ENABLED) || 170342c11ccfSNikolay Aleksandrov !br_opt_get(br, BROPT_MULTICAST_QUERIER)) 170442c11ccfSNikolay Aleksandrov goto out; 170542c11ccfSNikolay Aleksandrov 1706*eb1593a0SNikolay Aleksandrov pmctx = br_multicast_pg_to_port_ctx(pg); 1707*eb1593a0SNikolay Aleksandrov if (!pmctx) 1708*eb1593a0SNikolay Aleksandrov goto out; 1709*eb1593a0SNikolay Aleksandrov brmctx = br_multicast_port_ctx_get_global(pmctx); 1710085b53c8SNikolay Aleksandrov if (pg->key.addr.proto == htons(ETH_P_IP)) 1711d3d065c0SNikolay Aleksandrov other_query = &brmctx->ip4_other_query; 171242c11ccfSNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6) 171342c11ccfSNikolay Aleksandrov else 1714d3d065c0SNikolay Aleksandrov other_query = &brmctx->ip6_other_query; 171542c11ccfSNikolay Aleksandrov #endif 171642c11ccfSNikolay Aleksandrov 171742c11ccfSNikolay Aleksandrov if (!other_query || timer_pending(&other_query->timer)) 171842c11ccfSNikolay Aleksandrov goto out; 171942c11ccfSNikolay Aleksandrov 172042c11ccfSNikolay Aleksandrov if (pg->grp_query_rexmit_cnt) { 172142c11ccfSNikolay Aleksandrov pg->grp_query_rexmit_cnt--; 1722adc47037SNikolay Aleksandrov __br_multicast_send_query(brmctx, pmctx, pg, &pg->key.addr, 1723085b53c8SNikolay Aleksandrov &pg->key.addr, false, 1, NULL); 172442c11ccfSNikolay Aleksandrov } 1725adc47037SNikolay Aleksandrov __br_multicast_send_query(brmctx, pmctx, pg, &pg->key.addr, 1726085b53c8SNikolay Aleksandrov &pg->key.addr, true, 0, &need_rexmit); 172742c11ccfSNikolay Aleksandrov 172842c11ccfSNikolay Aleksandrov if (pg->grp_query_rexmit_cnt || need_rexmit) 172942c11ccfSNikolay Aleksandrov mod_timer(&pg->rexmit_timer, jiffies + 1730d3d065c0SNikolay Aleksandrov brmctx->multicast_last_member_interval); 173142c11ccfSNikolay Aleksandrov out: 173242c11ccfSNikolay Aleksandrov spin_unlock(&br->multicast_lock); 173342c11ccfSNikolay Aleksandrov } 173442c11ccfSNikolay Aleksandrov 1735ae1ea84bSFlorian Fainelli static int br_mc_disabled_update(struct net_device *dev, bool value, 1736ae1ea84bSFlorian Fainelli struct netlink_ext_ack *extack) 1737147c1e9bSNogah Frankel { 1738147c1e9bSNogah Frankel struct switchdev_attr attr = { 1739147c1e9bSNogah Frankel .orig_dev = dev, 1740147c1e9bSNogah Frankel .id = SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED, 1741147c1e9bSNogah Frankel .flags = SWITCHDEV_F_DEFER, 174213cefad2SNikolay Aleksandrov .u.mc_disabled = !value, 1743147c1e9bSNogah Frankel }; 1744147c1e9bSNogah Frankel 1745ae1ea84bSFlorian Fainelli return switchdev_port_attr_set(dev, &attr, extack); 1746147c1e9bSNogah Frankel } 1747147c1e9bSNogah Frankel 1748613d61dbSNikolay Aleksandrov void br_multicast_port_ctx_init(struct net_bridge_port *port, 1749613d61dbSNikolay Aleksandrov struct net_bridge_vlan *vlan, 17509632233eSNikolay Aleksandrov struct net_bridge_mcast_port *pmctx) 17519632233eSNikolay Aleksandrov { 17529632233eSNikolay Aleksandrov pmctx->port = port; 1753613d61dbSNikolay Aleksandrov pmctx->vlan = vlan; 17549632233eSNikolay Aleksandrov pmctx->multicast_router = MDB_RTR_TYPE_TEMP_QUERY; 17559632233eSNikolay Aleksandrov timer_setup(&pmctx->ip4_mc_router_timer, 17569632233eSNikolay Aleksandrov br_ip4_multicast_router_expired, 0); 17579632233eSNikolay Aleksandrov timer_setup(&pmctx->ip4_own_query.timer, 17589632233eSNikolay Aleksandrov br_ip4_multicast_port_query_expired, 0); 17599632233eSNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6) 17609632233eSNikolay Aleksandrov timer_setup(&pmctx->ip6_mc_router_timer, 17619632233eSNikolay Aleksandrov br_ip6_multicast_router_expired, 0); 17629632233eSNikolay Aleksandrov timer_setup(&pmctx->ip6_own_query.timer, 17639632233eSNikolay Aleksandrov br_ip6_multicast_port_query_expired, 0); 17649632233eSNikolay Aleksandrov #endif 17659632233eSNikolay Aleksandrov } 17669632233eSNikolay Aleksandrov 1767613d61dbSNikolay Aleksandrov void br_multicast_port_ctx_deinit(struct net_bridge_mcast_port *pmctx) 17689632233eSNikolay Aleksandrov { 17699632233eSNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6) 17709632233eSNikolay Aleksandrov del_timer_sync(&pmctx->ip6_mc_router_timer); 17719632233eSNikolay Aleksandrov #endif 17729632233eSNikolay Aleksandrov del_timer_sync(&pmctx->ip4_mc_router_timer); 17739632233eSNikolay Aleksandrov } 17749632233eSNikolay Aleksandrov 17751080ab95SNikolay Aleksandrov int br_multicast_add_port(struct net_bridge_port *port) 1776eb1d1641SHerbert Xu { 1777ae1ea84bSFlorian Fainelli int err; 1778ae1ea84bSFlorian Fainelli 177989268b05SNikolay Aleksandrov port->multicast_eht_hosts_limit = BR_MCAST_DEFAULT_EHT_HOSTS_LIMIT; 1780613d61dbSNikolay Aleksandrov br_multicast_port_ctx_init(port, NULL, &port->multicast_ctx); 1781eb1d1641SHerbert Xu 1782ae1ea84bSFlorian Fainelli err = br_mc_disabled_update(port->dev, 1783ae1ea84bSFlorian Fainelli br_opt_get(port->br, 1784ae1ea84bSFlorian Fainelli BROPT_MULTICAST_ENABLED), 1785ae1ea84bSFlorian Fainelli NULL); 178668f5c12aSVladimir Oltean if (err && err != -EOPNOTSUPP) 1787ae1ea84bSFlorian Fainelli return err; 1788147c1e9bSNogah Frankel 17891080ab95SNikolay Aleksandrov port->mcast_stats = netdev_alloc_pcpu_stats(struct bridge_mcast_stats); 17901080ab95SNikolay Aleksandrov if (!port->mcast_stats) 17911080ab95SNikolay Aleksandrov return -ENOMEM; 17921080ab95SNikolay Aleksandrov 17931080ab95SNikolay Aleksandrov return 0; 1794eb1d1641SHerbert Xu } 1795eb1d1641SHerbert Xu 1796eb1d1641SHerbert Xu void br_multicast_del_port(struct net_bridge_port *port) 1797eb1d1641SHerbert Xu { 1798e10177abSSatish Ashok struct net_bridge *br = port->br; 1799e10177abSSatish Ashok struct net_bridge_port_group *pg; 1800e12cec65SNikolay Aleksandrov HLIST_HEAD(deleted_head); 1801e10177abSSatish Ashok struct hlist_node *n; 1802e10177abSSatish Ashok 1803e10177abSSatish Ashok /* Take care of the remaining groups, only perm ones should be left */ 1804e10177abSSatish Ashok spin_lock_bh(&br->multicast_lock); 1805e10177abSSatish Ashok hlist_for_each_entry_safe(pg, n, &port->mglist, mglist) 1806681590bdSNikolay Aleksandrov br_multicast_find_del_pg(br, pg); 1807e12cec65SNikolay Aleksandrov hlist_move_list(&br->mcast_gc_list, &deleted_head); 1808e10177abSSatish Ashok spin_unlock_bh(&br->multicast_lock); 1809e12cec65SNikolay Aleksandrov br_multicast_gc(&deleted_head); 18109632233eSNikolay Aleksandrov br_multicast_port_ctx_deinit(&port->multicast_ctx); 18111080ab95SNikolay Aleksandrov free_percpu(port->mcast_stats); 1812eb1d1641SHerbert Xu } 1813eb1d1641SHerbert Xu 181490010b36SLinus Lüssing static void br_multicast_enable(struct bridge_mcast_own_query *query) 1815561f1103SHerbert Xu { 1816cc0fdd80SLinus Lüssing query->startup_sent = 0; 1817561f1103SHerbert Xu 1818cc0fdd80SLinus Lüssing if (try_to_del_timer_sync(&query->timer) >= 0 || 1819cc0fdd80SLinus Lüssing del_timer(&query->timer)) 1820cc0fdd80SLinus Lüssing mod_timer(&query->timer, jiffies); 1821561f1103SHerbert Xu } 1822561f1103SHerbert Xu 1823adc47037SNikolay Aleksandrov static void __br_multicast_enable_port_ctx(struct net_bridge_mcast_port *pmctx) 1824eb1d1641SHerbert Xu { 1825adc47037SNikolay Aleksandrov struct net_bridge *br = pmctx->port->br; 18267b54aaafSNikolay Aleksandrov struct net_bridge_mcast *brmctx; 1827eb1d1641SHerbert Xu 18287b54aaafSNikolay Aleksandrov brmctx = br_multicast_port_ctx_get_global(pmctx); 18297b54aaafSNikolay Aleksandrov if (!br_opt_get(br, BROPT_MULTICAST_ENABLED) || 18307b54aaafSNikolay Aleksandrov !netif_running(br->dev)) 18317cb3f921SNikolay Aleksandrov return; 1832eb1d1641SHerbert Xu 1833adc47037SNikolay Aleksandrov br_multicast_enable(&pmctx->ip4_own_query); 1834cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 1835adc47037SNikolay Aleksandrov br_multicast_enable(&pmctx->ip6_own_query); 1836cc0fdd80SLinus Lüssing #endif 1837adc47037SNikolay Aleksandrov if (pmctx->multicast_router == MDB_RTR_TYPE_PERM) { 1838adc47037SNikolay Aleksandrov br_ip4_multicast_add_router(brmctx, pmctx); 1839adc47037SNikolay Aleksandrov br_ip6_multicast_add_router(brmctx, pmctx); 1840a3c02e76SLinus Lüssing } 18417cb3f921SNikolay Aleksandrov } 1842eb1d1641SHerbert Xu 18437cb3f921SNikolay Aleksandrov void br_multicast_enable_port(struct net_bridge_port *port) 18447cb3f921SNikolay Aleksandrov { 18457cb3f921SNikolay Aleksandrov struct net_bridge *br = port->br; 18467cb3f921SNikolay Aleksandrov 1847f4b7002aSNikolay Aleksandrov spin_lock_bh(&br->multicast_lock); 1848adc47037SNikolay Aleksandrov __br_multicast_enable_port_ctx(&port->multicast_ctx); 1849f4b7002aSNikolay Aleksandrov spin_unlock_bh(&br->multicast_lock); 1850eb1d1641SHerbert Xu } 1851eb1d1641SHerbert Xu 18527b54aaafSNikolay Aleksandrov static void __br_multicast_disable_port_ctx(struct net_bridge_mcast_port *pmctx) 1853eb1d1641SHerbert Xu { 1854eb1d1641SHerbert Xu struct net_bridge_port_group *pg; 1855b67bfe0dSSasha Levin struct hlist_node *n; 1856ed2d3597SLinus Lüssing bool del = false; 1857eb1d1641SHerbert Xu 18587b54aaafSNikolay Aleksandrov hlist_for_each_entry_safe(pg, n, &pmctx->port->mglist, mglist) 18597b54aaafSNikolay Aleksandrov if (!(pg->flags & MDB_PG_FLAGS_PERMANENT) && 18607b54aaafSNikolay Aleksandrov (!br_multicast_port_ctx_is_vlan(pmctx) || 18617b54aaafSNikolay Aleksandrov pg->key.addr.vid == pmctx->vlan->vid)) 18627b54aaafSNikolay Aleksandrov br_multicast_find_del_pg(pmctx->port->br, pg); 1863eb1d1641SHerbert Xu 1864adc47037SNikolay Aleksandrov del |= br_ip4_multicast_rport_del(pmctx); 1865adc47037SNikolay Aleksandrov del_timer(&pmctx->ip4_mc_router_timer); 1866adc47037SNikolay Aleksandrov del_timer(&pmctx->ip4_own_query.timer); 1867adc47037SNikolay Aleksandrov del |= br_ip6_multicast_rport_del(pmctx); 1868cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 1869adc47037SNikolay Aleksandrov del_timer(&pmctx->ip6_mc_router_timer); 1870adc47037SNikolay Aleksandrov del_timer(&pmctx->ip6_own_query.timer); 1871cc0fdd80SLinus Lüssing #endif 1872adc47037SNikolay Aleksandrov br_multicast_rport_del_notify(pmctx, del); 18737b54aaafSNikolay Aleksandrov } 18747b54aaafSNikolay Aleksandrov 18757b54aaafSNikolay Aleksandrov void br_multicast_disable_port(struct net_bridge_port *port) 18767b54aaafSNikolay Aleksandrov { 1877f4b7002aSNikolay Aleksandrov spin_lock_bh(&port->br->multicast_lock); 18787b54aaafSNikolay Aleksandrov __br_multicast_disable_port_ctx(&port->multicast_ctx); 1879f4b7002aSNikolay Aleksandrov spin_unlock_bh(&port->br->multicast_lock); 1880eb1d1641SHerbert Xu } 1881eb1d1641SHerbert Xu 1882e6231bcaSNikolay Aleksandrov static int __grp_src_delete_marked(struct net_bridge_port_group *pg) 1883e6231bcaSNikolay Aleksandrov { 1884e6231bcaSNikolay Aleksandrov struct net_bridge_group_src *ent; 1885e6231bcaSNikolay Aleksandrov struct hlist_node *tmp; 1886e6231bcaSNikolay Aleksandrov int deleted = 0; 1887e6231bcaSNikolay Aleksandrov 1888e6231bcaSNikolay Aleksandrov hlist_for_each_entry_safe(ent, tmp, &pg->src_list, node) 1889e6231bcaSNikolay Aleksandrov if (ent->flags & BR_SGRP_F_DELETE) { 1890d5a10222SNikolay Aleksandrov br_multicast_del_group_src(ent, false); 1891e6231bcaSNikolay Aleksandrov deleted++; 1892e6231bcaSNikolay Aleksandrov } 1893e6231bcaSNikolay Aleksandrov 1894e6231bcaSNikolay Aleksandrov return deleted; 1895e6231bcaSNikolay Aleksandrov } 1896e6231bcaSNikolay Aleksandrov 1897b0812368SNikolay Aleksandrov static void __grp_src_mod_timer(struct net_bridge_group_src *src, 1898b0812368SNikolay Aleksandrov unsigned long expires) 1899b0812368SNikolay Aleksandrov { 1900b0812368SNikolay Aleksandrov mod_timer(&src->timer, expires); 1901b0812368SNikolay Aleksandrov br_multicast_fwd_src_handle(src); 1902b0812368SNikolay Aleksandrov } 1903b0812368SNikolay Aleksandrov 1904adc47037SNikolay Aleksandrov static void __grp_src_query_marked_and_rexmit(struct net_bridge_mcast *brmctx, 1905adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 1906adc47037SNikolay Aleksandrov struct net_bridge_port_group *pg) 19075bf1e00bSNikolay Aleksandrov { 19085bf1e00bSNikolay Aleksandrov struct bridge_mcast_other_query *other_query = NULL; 1909d3d065c0SNikolay Aleksandrov u32 lmqc = brmctx->multicast_last_member_count; 19105bf1e00bSNikolay Aleksandrov unsigned long lmqt, lmi, now = jiffies; 19115bf1e00bSNikolay Aleksandrov struct net_bridge_group_src *ent; 19125bf1e00bSNikolay Aleksandrov 1913adc47037SNikolay Aleksandrov if (!netif_running(brmctx->br->dev) || 1914adc47037SNikolay Aleksandrov !br_opt_get(brmctx->br, BROPT_MULTICAST_ENABLED)) 19155bf1e00bSNikolay Aleksandrov return; 19165bf1e00bSNikolay Aleksandrov 1917085b53c8SNikolay Aleksandrov if (pg->key.addr.proto == htons(ETH_P_IP)) 1918d3d065c0SNikolay Aleksandrov other_query = &brmctx->ip4_other_query; 19195bf1e00bSNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6) 19205bf1e00bSNikolay Aleksandrov else 1921d3d065c0SNikolay Aleksandrov other_query = &brmctx->ip6_other_query; 19225bf1e00bSNikolay Aleksandrov #endif 19235bf1e00bSNikolay Aleksandrov 1924adc47037SNikolay Aleksandrov lmqt = now + br_multicast_lmqt(brmctx); 19255bf1e00bSNikolay Aleksandrov hlist_for_each_entry(ent, &pg->src_list, node) { 19265bf1e00bSNikolay Aleksandrov if (ent->flags & BR_SGRP_F_SEND) { 19275bf1e00bSNikolay Aleksandrov ent->flags &= ~BR_SGRP_F_SEND; 19285bf1e00bSNikolay Aleksandrov if (ent->timer.expires > lmqt) { 1929adc47037SNikolay Aleksandrov if (br_opt_get(brmctx->br, 1930adc47037SNikolay Aleksandrov BROPT_MULTICAST_QUERIER) && 19315bf1e00bSNikolay Aleksandrov other_query && 19325bf1e00bSNikolay Aleksandrov !timer_pending(&other_query->timer)) 19335bf1e00bSNikolay Aleksandrov ent->src_query_rexmit_cnt = lmqc; 1934b0812368SNikolay Aleksandrov __grp_src_mod_timer(ent, lmqt); 19355bf1e00bSNikolay Aleksandrov } 19365bf1e00bSNikolay Aleksandrov } 19375bf1e00bSNikolay Aleksandrov } 19385bf1e00bSNikolay Aleksandrov 1939adc47037SNikolay Aleksandrov if (!br_opt_get(brmctx->br, BROPT_MULTICAST_QUERIER) || 19405bf1e00bSNikolay Aleksandrov !other_query || timer_pending(&other_query->timer)) 19415bf1e00bSNikolay Aleksandrov return; 19425bf1e00bSNikolay Aleksandrov 1943adc47037SNikolay Aleksandrov __br_multicast_send_query(brmctx, pmctx, pg, &pg->key.addr, 1944085b53c8SNikolay Aleksandrov &pg->key.addr, true, 1, NULL); 19455bf1e00bSNikolay Aleksandrov 1946d3d065c0SNikolay Aleksandrov lmi = now + brmctx->multicast_last_member_interval; 19475bf1e00bSNikolay Aleksandrov if (!timer_pending(&pg->rexmit_timer) || 19485bf1e00bSNikolay Aleksandrov time_after(pg->rexmit_timer.expires, lmi)) 19495bf1e00bSNikolay Aleksandrov mod_timer(&pg->rexmit_timer, lmi); 19505bf1e00bSNikolay Aleksandrov } 19515bf1e00bSNikolay Aleksandrov 1952adc47037SNikolay Aleksandrov static void __grp_send_query_and_rexmit(struct net_bridge_mcast *brmctx, 1953adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 1954adc47037SNikolay Aleksandrov struct net_bridge_port_group *pg) 19555bf1e00bSNikolay Aleksandrov { 19565bf1e00bSNikolay Aleksandrov struct bridge_mcast_other_query *other_query = NULL; 19575bf1e00bSNikolay Aleksandrov unsigned long now = jiffies, lmi; 19585bf1e00bSNikolay Aleksandrov 1959adc47037SNikolay Aleksandrov if (!netif_running(brmctx->br->dev) || 1960adc47037SNikolay Aleksandrov !br_opt_get(brmctx->br, BROPT_MULTICAST_ENABLED)) 19615bf1e00bSNikolay Aleksandrov return; 19625bf1e00bSNikolay Aleksandrov 1963085b53c8SNikolay Aleksandrov if (pg->key.addr.proto == htons(ETH_P_IP)) 1964d3d065c0SNikolay Aleksandrov other_query = &brmctx->ip4_other_query; 19655bf1e00bSNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6) 19665bf1e00bSNikolay Aleksandrov else 1967d3d065c0SNikolay Aleksandrov other_query = &brmctx->ip6_other_query; 19685bf1e00bSNikolay Aleksandrov #endif 19695bf1e00bSNikolay Aleksandrov 1970adc47037SNikolay Aleksandrov if (br_opt_get(brmctx->br, BROPT_MULTICAST_QUERIER) && 19715bf1e00bSNikolay Aleksandrov other_query && !timer_pending(&other_query->timer)) { 1972d3d065c0SNikolay Aleksandrov lmi = now + brmctx->multicast_last_member_interval; 1973d3d065c0SNikolay Aleksandrov pg->grp_query_rexmit_cnt = brmctx->multicast_last_member_count - 1; 1974adc47037SNikolay Aleksandrov __br_multicast_send_query(brmctx, pmctx, pg, &pg->key.addr, 1975085b53c8SNikolay Aleksandrov &pg->key.addr, false, 0, NULL); 19765bf1e00bSNikolay Aleksandrov if (!timer_pending(&pg->rexmit_timer) || 19775bf1e00bSNikolay Aleksandrov time_after(pg->rexmit_timer.expires, lmi)) 19785bf1e00bSNikolay Aleksandrov mod_timer(&pg->rexmit_timer, lmi); 19795bf1e00bSNikolay Aleksandrov } 19805bf1e00bSNikolay Aleksandrov 19815bf1e00bSNikolay Aleksandrov if (pg->filter_mode == MCAST_EXCLUDE && 19825bf1e00bSNikolay Aleksandrov (!timer_pending(&pg->timer) || 1983adc47037SNikolay Aleksandrov time_after(pg->timer.expires, now + br_multicast_lmqt(brmctx)))) 1984adc47037SNikolay Aleksandrov mod_timer(&pg->timer, now + br_multicast_lmqt(brmctx)); 19855bf1e00bSNikolay Aleksandrov } 19865bf1e00bSNikolay Aleksandrov 19870436862eSNikolay Aleksandrov /* State Msg type New state Actions 19880436862eSNikolay Aleksandrov * INCLUDE (A) IS_IN (B) INCLUDE (A+B) (B)=GMI 19890436862eSNikolay Aleksandrov * INCLUDE (A) ALLOW (B) INCLUDE (A+B) (B)=GMI 19900436862eSNikolay Aleksandrov * EXCLUDE (X,Y) ALLOW (A) EXCLUDE (X+A,Y-A) (A)=GMI 19910436862eSNikolay Aleksandrov */ 1992adc47037SNikolay Aleksandrov static bool br_multicast_isinc_allow(const struct net_bridge_mcast *brmctx, 1993adc47037SNikolay Aleksandrov struct net_bridge_port_group *pg, void *h_addr, 1994474ddb37SNikolay Aleksandrov void *srcs, u32 nsrcs, size_t addr_size, 1995474ddb37SNikolay Aleksandrov int grec_type) 19960436862eSNikolay Aleksandrov { 19970436862eSNikolay Aleksandrov struct net_bridge_group_src *ent; 19980436862eSNikolay Aleksandrov unsigned long now = jiffies; 19990436862eSNikolay Aleksandrov bool changed = false; 20000436862eSNikolay Aleksandrov struct br_ip src_ip; 20010436862eSNikolay Aleksandrov u32 src_idx; 20020436862eSNikolay Aleksandrov 20030436862eSNikolay Aleksandrov memset(&src_ip, 0, sizeof(src_ip)); 2004085b53c8SNikolay Aleksandrov src_ip.proto = pg->key.addr.proto; 20050436862eSNikolay Aleksandrov for (src_idx = 0; src_idx < nsrcs; src_idx++) { 2006e7cfcf2cSNikolay Aleksandrov memcpy(&src_ip.src, srcs + (src_idx * addr_size), addr_size); 20070436862eSNikolay Aleksandrov ent = br_multicast_find_group_src(pg, &src_ip); 20080436862eSNikolay Aleksandrov if (!ent) { 20090436862eSNikolay Aleksandrov ent = br_multicast_new_group_src(pg, &src_ip); 20100436862eSNikolay Aleksandrov if (ent) 20110436862eSNikolay Aleksandrov changed = true; 20120436862eSNikolay Aleksandrov } 20130436862eSNikolay Aleksandrov 20140436862eSNikolay Aleksandrov if (ent) 2015adc47037SNikolay Aleksandrov __grp_src_mod_timer(ent, now + br_multicast_gmi(brmctx)); 20160436862eSNikolay Aleksandrov } 20170436862eSNikolay Aleksandrov 2018adc47037SNikolay Aleksandrov if (br_multicast_eht_handle(brmctx, pg, h_addr, srcs, nsrcs, addr_size, 2019adc47037SNikolay Aleksandrov grec_type)) 2020474ddb37SNikolay Aleksandrov changed = true; 2021474ddb37SNikolay Aleksandrov 20220436862eSNikolay Aleksandrov return changed; 20230436862eSNikolay Aleksandrov } 20240436862eSNikolay Aleksandrov 2025e6231bcaSNikolay Aleksandrov /* State Msg type New state Actions 2026e6231bcaSNikolay Aleksandrov * INCLUDE (A) IS_EX (B) EXCLUDE (A*B,B-A) (B-A)=0 2027e6231bcaSNikolay Aleksandrov * Delete (A-B) 2028e6231bcaSNikolay Aleksandrov * Group Timer=GMI 2029e6231bcaSNikolay Aleksandrov */ 2030adc47037SNikolay Aleksandrov static void __grp_src_isexc_incl(const struct net_bridge_mcast *brmctx, 2031adc47037SNikolay Aleksandrov struct net_bridge_port_group *pg, void *h_addr, 2032ddc255d9SNikolay Aleksandrov void *srcs, u32 nsrcs, size_t addr_size, 2033ddc255d9SNikolay Aleksandrov int grec_type) 2034e6231bcaSNikolay Aleksandrov { 2035e6231bcaSNikolay Aleksandrov struct net_bridge_group_src *ent; 2036e6231bcaSNikolay Aleksandrov struct br_ip src_ip; 2037e6231bcaSNikolay Aleksandrov u32 src_idx; 2038e6231bcaSNikolay Aleksandrov 2039e6231bcaSNikolay Aleksandrov hlist_for_each_entry(ent, &pg->src_list, node) 2040e6231bcaSNikolay Aleksandrov ent->flags |= BR_SGRP_F_DELETE; 2041e6231bcaSNikolay Aleksandrov 2042e6231bcaSNikolay Aleksandrov memset(&src_ip, 0, sizeof(src_ip)); 2043085b53c8SNikolay Aleksandrov src_ip.proto = pg->key.addr.proto; 2044e6231bcaSNikolay Aleksandrov for (src_idx = 0; src_idx < nsrcs; src_idx++) { 2045e7cfcf2cSNikolay Aleksandrov memcpy(&src_ip.src, srcs + (src_idx * addr_size), addr_size); 2046e6231bcaSNikolay Aleksandrov ent = br_multicast_find_group_src(pg, &src_ip); 2047e6231bcaSNikolay Aleksandrov if (ent) 2048e6231bcaSNikolay Aleksandrov ent->flags &= ~BR_SGRP_F_DELETE; 2049e6231bcaSNikolay Aleksandrov else 2050b0812368SNikolay Aleksandrov ent = br_multicast_new_group_src(pg, &src_ip); 2051b0812368SNikolay Aleksandrov if (ent) 2052b0812368SNikolay Aleksandrov br_multicast_fwd_src_handle(ent); 2053e6231bcaSNikolay Aleksandrov } 2054e6231bcaSNikolay Aleksandrov 2055adc47037SNikolay Aleksandrov br_multicast_eht_handle(brmctx, pg, h_addr, srcs, nsrcs, addr_size, 2056adc47037SNikolay Aleksandrov grec_type); 2057ddc255d9SNikolay Aleksandrov 2058e6231bcaSNikolay Aleksandrov __grp_src_delete_marked(pg); 2059e6231bcaSNikolay Aleksandrov } 2060e6231bcaSNikolay Aleksandrov 2061e6231bcaSNikolay Aleksandrov /* State Msg type New state Actions 2062e6231bcaSNikolay Aleksandrov * EXCLUDE (X,Y) IS_EX (A) EXCLUDE (A-Y,Y*A) (A-X-Y)=GMI 2063e6231bcaSNikolay Aleksandrov * Delete (X-A) 2064e6231bcaSNikolay Aleksandrov * Delete (Y-A) 2065e6231bcaSNikolay Aleksandrov * Group Timer=GMI 2066e6231bcaSNikolay Aleksandrov */ 2067adc47037SNikolay Aleksandrov static bool __grp_src_isexc_excl(const struct net_bridge_mcast *brmctx, 2068adc47037SNikolay Aleksandrov struct net_bridge_port_group *pg, void *h_addr, 2069ddc255d9SNikolay Aleksandrov void *srcs, u32 nsrcs, size_t addr_size, 2070ddc255d9SNikolay Aleksandrov int grec_type) 2071e6231bcaSNikolay Aleksandrov { 2072e6231bcaSNikolay Aleksandrov struct net_bridge_group_src *ent; 2073e6231bcaSNikolay Aleksandrov unsigned long now = jiffies; 2074e6231bcaSNikolay Aleksandrov bool changed = false; 2075e6231bcaSNikolay Aleksandrov struct br_ip src_ip; 2076e6231bcaSNikolay Aleksandrov u32 src_idx; 2077e6231bcaSNikolay Aleksandrov 2078e6231bcaSNikolay Aleksandrov hlist_for_each_entry(ent, &pg->src_list, node) 2079e6231bcaSNikolay Aleksandrov ent->flags |= BR_SGRP_F_DELETE; 2080e6231bcaSNikolay Aleksandrov 2081e6231bcaSNikolay Aleksandrov memset(&src_ip, 0, sizeof(src_ip)); 2082085b53c8SNikolay Aleksandrov src_ip.proto = pg->key.addr.proto; 2083e6231bcaSNikolay Aleksandrov for (src_idx = 0; src_idx < nsrcs; src_idx++) { 2084e7cfcf2cSNikolay Aleksandrov memcpy(&src_ip.src, srcs + (src_idx * addr_size), addr_size); 2085e6231bcaSNikolay Aleksandrov ent = br_multicast_find_group_src(pg, &src_ip); 2086e6231bcaSNikolay Aleksandrov if (ent) { 2087e6231bcaSNikolay Aleksandrov ent->flags &= ~BR_SGRP_F_DELETE; 2088e6231bcaSNikolay Aleksandrov } else { 2089e6231bcaSNikolay Aleksandrov ent = br_multicast_new_group_src(pg, &src_ip); 2090e6231bcaSNikolay Aleksandrov if (ent) { 2091b0812368SNikolay Aleksandrov __grp_src_mod_timer(ent, 2092adc47037SNikolay Aleksandrov now + br_multicast_gmi(brmctx)); 2093e6231bcaSNikolay Aleksandrov changed = true; 2094e6231bcaSNikolay Aleksandrov } 2095e6231bcaSNikolay Aleksandrov } 2096e6231bcaSNikolay Aleksandrov } 2097e6231bcaSNikolay Aleksandrov 2098adc47037SNikolay Aleksandrov if (br_multicast_eht_handle(brmctx, pg, h_addr, srcs, nsrcs, addr_size, 2099adc47037SNikolay Aleksandrov grec_type)) 2100ddc255d9SNikolay Aleksandrov changed = true; 2101ddc255d9SNikolay Aleksandrov 2102e6231bcaSNikolay Aleksandrov if (__grp_src_delete_marked(pg)) 2103e6231bcaSNikolay Aleksandrov changed = true; 2104e6231bcaSNikolay Aleksandrov 2105e6231bcaSNikolay Aleksandrov return changed; 2106e6231bcaSNikolay Aleksandrov } 2107e6231bcaSNikolay Aleksandrov 2108adc47037SNikolay Aleksandrov static bool br_multicast_isexc(const struct net_bridge_mcast *brmctx, 2109adc47037SNikolay Aleksandrov struct net_bridge_port_group *pg, void *h_addr, 2110ddc255d9SNikolay Aleksandrov void *srcs, u32 nsrcs, size_t addr_size, 2111ddc255d9SNikolay Aleksandrov int grec_type) 2112e6231bcaSNikolay Aleksandrov { 2113e6231bcaSNikolay Aleksandrov bool changed = false; 2114e6231bcaSNikolay Aleksandrov 2115e6231bcaSNikolay Aleksandrov switch (pg->filter_mode) { 2116e6231bcaSNikolay Aleksandrov case MCAST_INCLUDE: 2117adc47037SNikolay Aleksandrov __grp_src_isexc_incl(brmctx, pg, h_addr, srcs, nsrcs, addr_size, 2118ddc255d9SNikolay Aleksandrov grec_type); 21198266a049SNikolay Aleksandrov br_multicast_star_g_handle_mode(pg, MCAST_EXCLUDE); 2120e6231bcaSNikolay Aleksandrov changed = true; 2121e6231bcaSNikolay Aleksandrov break; 2122e6231bcaSNikolay Aleksandrov case MCAST_EXCLUDE: 2123adc47037SNikolay Aleksandrov changed = __grp_src_isexc_excl(brmctx, pg, h_addr, srcs, nsrcs, 2124adc47037SNikolay Aleksandrov addr_size, grec_type); 2125e6231bcaSNikolay Aleksandrov break; 2126e6231bcaSNikolay Aleksandrov } 2127e6231bcaSNikolay Aleksandrov 2128e6231bcaSNikolay Aleksandrov pg->filter_mode = MCAST_EXCLUDE; 2129adc47037SNikolay Aleksandrov mod_timer(&pg->timer, jiffies + br_multicast_gmi(brmctx)); 2130e6231bcaSNikolay Aleksandrov 2131e6231bcaSNikolay Aleksandrov return changed; 2132e6231bcaSNikolay Aleksandrov } 2133e6231bcaSNikolay Aleksandrov 21345bf1e00bSNikolay Aleksandrov /* State Msg type New state Actions 21355bf1e00bSNikolay Aleksandrov * INCLUDE (A) TO_IN (B) INCLUDE (A+B) (B)=GMI 21365bf1e00bSNikolay Aleksandrov * Send Q(G,A-B) 21375bf1e00bSNikolay Aleksandrov */ 2138adc47037SNikolay Aleksandrov static bool __grp_src_toin_incl(struct net_bridge_mcast *brmctx, 2139adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 2140adc47037SNikolay Aleksandrov struct net_bridge_port_group *pg, void *h_addr, 2141ddc255d9SNikolay Aleksandrov void *srcs, u32 nsrcs, size_t addr_size, 2142ddc255d9SNikolay Aleksandrov int grec_type) 21435bf1e00bSNikolay Aleksandrov { 21445bf1e00bSNikolay Aleksandrov u32 src_idx, to_send = pg->src_ents; 21455bf1e00bSNikolay Aleksandrov struct net_bridge_group_src *ent; 21465bf1e00bSNikolay Aleksandrov unsigned long now = jiffies; 21475bf1e00bSNikolay Aleksandrov bool changed = false; 21485bf1e00bSNikolay Aleksandrov struct br_ip src_ip; 21495bf1e00bSNikolay Aleksandrov 21505bf1e00bSNikolay Aleksandrov hlist_for_each_entry(ent, &pg->src_list, node) 21515bf1e00bSNikolay Aleksandrov ent->flags |= BR_SGRP_F_SEND; 21525bf1e00bSNikolay Aleksandrov 21535bf1e00bSNikolay Aleksandrov memset(&src_ip, 0, sizeof(src_ip)); 2154085b53c8SNikolay Aleksandrov src_ip.proto = pg->key.addr.proto; 21555bf1e00bSNikolay Aleksandrov for (src_idx = 0; src_idx < nsrcs; src_idx++) { 2156e7cfcf2cSNikolay Aleksandrov memcpy(&src_ip.src, srcs + (src_idx * addr_size), addr_size); 21575bf1e00bSNikolay Aleksandrov ent = br_multicast_find_group_src(pg, &src_ip); 21585bf1e00bSNikolay Aleksandrov if (ent) { 21595bf1e00bSNikolay Aleksandrov ent->flags &= ~BR_SGRP_F_SEND; 21605bf1e00bSNikolay Aleksandrov to_send--; 21615bf1e00bSNikolay Aleksandrov } else { 21625bf1e00bSNikolay Aleksandrov ent = br_multicast_new_group_src(pg, &src_ip); 21635bf1e00bSNikolay Aleksandrov if (ent) 21645bf1e00bSNikolay Aleksandrov changed = true; 21655bf1e00bSNikolay Aleksandrov } 21665bf1e00bSNikolay Aleksandrov if (ent) 2167adc47037SNikolay Aleksandrov __grp_src_mod_timer(ent, now + br_multicast_gmi(brmctx)); 21685bf1e00bSNikolay Aleksandrov } 21695bf1e00bSNikolay Aleksandrov 2170adc47037SNikolay Aleksandrov if (br_multicast_eht_handle(brmctx, pg, h_addr, srcs, nsrcs, addr_size, 2171adc47037SNikolay Aleksandrov grec_type)) 2172ddc255d9SNikolay Aleksandrov changed = true; 2173ddc255d9SNikolay Aleksandrov 21745bf1e00bSNikolay Aleksandrov if (to_send) 2175adc47037SNikolay Aleksandrov __grp_src_query_marked_and_rexmit(brmctx, pmctx, pg); 21765bf1e00bSNikolay Aleksandrov 21775bf1e00bSNikolay Aleksandrov return changed; 21785bf1e00bSNikolay Aleksandrov } 21795bf1e00bSNikolay Aleksandrov 21805bf1e00bSNikolay Aleksandrov /* State Msg type New state Actions 21815bf1e00bSNikolay Aleksandrov * EXCLUDE (X,Y) TO_IN (A) EXCLUDE (X+A,Y-A) (A)=GMI 21825bf1e00bSNikolay Aleksandrov * Send Q(G,X-A) 21835bf1e00bSNikolay Aleksandrov * Send Q(G) 21845bf1e00bSNikolay Aleksandrov */ 2185adc47037SNikolay Aleksandrov static bool __grp_src_toin_excl(struct net_bridge_mcast *brmctx, 2186adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 2187adc47037SNikolay Aleksandrov struct net_bridge_port_group *pg, void *h_addr, 2188ddc255d9SNikolay Aleksandrov void *srcs, u32 nsrcs, size_t addr_size, 2189ddc255d9SNikolay Aleksandrov int grec_type) 21905bf1e00bSNikolay Aleksandrov { 21915bf1e00bSNikolay Aleksandrov u32 src_idx, to_send = pg->src_ents; 21925bf1e00bSNikolay Aleksandrov struct net_bridge_group_src *ent; 21935bf1e00bSNikolay Aleksandrov unsigned long now = jiffies; 21945bf1e00bSNikolay Aleksandrov bool changed = false; 21955bf1e00bSNikolay Aleksandrov struct br_ip src_ip; 21965bf1e00bSNikolay Aleksandrov 21975bf1e00bSNikolay Aleksandrov hlist_for_each_entry(ent, &pg->src_list, node) 21985bf1e00bSNikolay Aleksandrov if (timer_pending(&ent->timer)) 21995bf1e00bSNikolay Aleksandrov ent->flags |= BR_SGRP_F_SEND; 22005bf1e00bSNikolay Aleksandrov 22015bf1e00bSNikolay Aleksandrov memset(&src_ip, 0, sizeof(src_ip)); 2202085b53c8SNikolay Aleksandrov src_ip.proto = pg->key.addr.proto; 22035bf1e00bSNikolay Aleksandrov for (src_idx = 0; src_idx < nsrcs; src_idx++) { 2204e7cfcf2cSNikolay Aleksandrov memcpy(&src_ip.src, srcs + (src_idx * addr_size), addr_size); 22055bf1e00bSNikolay Aleksandrov ent = br_multicast_find_group_src(pg, &src_ip); 22065bf1e00bSNikolay Aleksandrov if (ent) { 22075bf1e00bSNikolay Aleksandrov if (timer_pending(&ent->timer)) { 22085bf1e00bSNikolay Aleksandrov ent->flags &= ~BR_SGRP_F_SEND; 22095bf1e00bSNikolay Aleksandrov to_send--; 22105bf1e00bSNikolay Aleksandrov } 22115bf1e00bSNikolay Aleksandrov } else { 22125bf1e00bSNikolay Aleksandrov ent = br_multicast_new_group_src(pg, &src_ip); 22135bf1e00bSNikolay Aleksandrov if (ent) 22145bf1e00bSNikolay Aleksandrov changed = true; 22155bf1e00bSNikolay Aleksandrov } 22165bf1e00bSNikolay Aleksandrov if (ent) 2217adc47037SNikolay Aleksandrov __grp_src_mod_timer(ent, now + br_multicast_gmi(brmctx)); 22185bf1e00bSNikolay Aleksandrov } 22195bf1e00bSNikolay Aleksandrov 2220adc47037SNikolay Aleksandrov if (br_multicast_eht_handle(brmctx, pg, h_addr, srcs, nsrcs, addr_size, 2221adc47037SNikolay Aleksandrov grec_type)) 2222ddc255d9SNikolay Aleksandrov changed = true; 2223ddc255d9SNikolay Aleksandrov 22245bf1e00bSNikolay Aleksandrov if (to_send) 2225adc47037SNikolay Aleksandrov __grp_src_query_marked_and_rexmit(brmctx, pmctx, pg); 22265bf1e00bSNikolay Aleksandrov 2227adc47037SNikolay Aleksandrov __grp_send_query_and_rexmit(brmctx, pmctx, pg); 22285bf1e00bSNikolay Aleksandrov 22295bf1e00bSNikolay Aleksandrov return changed; 22305bf1e00bSNikolay Aleksandrov } 22315bf1e00bSNikolay Aleksandrov 2232adc47037SNikolay Aleksandrov static bool br_multicast_toin(struct net_bridge_mcast *brmctx, 2233adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 2234adc47037SNikolay Aleksandrov struct net_bridge_port_group *pg, void *h_addr, 2235ddc255d9SNikolay Aleksandrov void *srcs, u32 nsrcs, size_t addr_size, 2236ddc255d9SNikolay Aleksandrov int grec_type) 22375bf1e00bSNikolay Aleksandrov { 22385bf1e00bSNikolay Aleksandrov bool changed = false; 22395bf1e00bSNikolay Aleksandrov 22405bf1e00bSNikolay Aleksandrov switch (pg->filter_mode) { 22415bf1e00bSNikolay Aleksandrov case MCAST_INCLUDE: 2242adc47037SNikolay Aleksandrov changed = __grp_src_toin_incl(brmctx, pmctx, pg, h_addr, srcs, 2243adc47037SNikolay Aleksandrov nsrcs, addr_size, grec_type); 22445bf1e00bSNikolay Aleksandrov break; 22455bf1e00bSNikolay Aleksandrov case MCAST_EXCLUDE: 2246adc47037SNikolay Aleksandrov changed = __grp_src_toin_excl(brmctx, pmctx, pg, h_addr, srcs, 2247adc47037SNikolay Aleksandrov nsrcs, addr_size, grec_type); 22485bf1e00bSNikolay Aleksandrov break; 22495bf1e00bSNikolay Aleksandrov } 22505bf1e00bSNikolay Aleksandrov 2251ddc255d9SNikolay Aleksandrov if (br_multicast_eht_should_del_pg(pg)) { 2252d5a10222SNikolay Aleksandrov pg->flags |= MDB_PG_FLAGS_FAST_LEAVE; 2253ddc255d9SNikolay Aleksandrov br_multicast_find_del_pg(pg->key.port->br, pg); 2254ddc255d9SNikolay Aleksandrov /* a notification has already been sent and we shouldn't 2255ddc255d9SNikolay Aleksandrov * access pg after the delete so we have to return false 2256ddc255d9SNikolay Aleksandrov */ 2257ddc255d9SNikolay Aleksandrov changed = false; 2258ddc255d9SNikolay Aleksandrov } 2259ddc255d9SNikolay Aleksandrov 22605bf1e00bSNikolay Aleksandrov return changed; 22615bf1e00bSNikolay Aleksandrov } 22625bf1e00bSNikolay Aleksandrov 22635bf1e00bSNikolay Aleksandrov /* State Msg type New state Actions 22645bf1e00bSNikolay Aleksandrov * INCLUDE (A) TO_EX (B) EXCLUDE (A*B,B-A) (B-A)=0 22655bf1e00bSNikolay Aleksandrov * Delete (A-B) 22665bf1e00bSNikolay Aleksandrov * Send Q(G,A*B) 22675bf1e00bSNikolay Aleksandrov * Group Timer=GMI 22685bf1e00bSNikolay Aleksandrov */ 2269adc47037SNikolay Aleksandrov static void __grp_src_toex_incl(struct net_bridge_mcast *brmctx, 2270adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 2271adc47037SNikolay Aleksandrov struct net_bridge_port_group *pg, void *h_addr, 2272ddc255d9SNikolay Aleksandrov void *srcs, u32 nsrcs, size_t addr_size, 2273ddc255d9SNikolay Aleksandrov int grec_type) 22745bf1e00bSNikolay Aleksandrov { 22755bf1e00bSNikolay Aleksandrov struct net_bridge_group_src *ent; 22765bf1e00bSNikolay Aleksandrov u32 src_idx, to_send = 0; 22775bf1e00bSNikolay Aleksandrov struct br_ip src_ip; 22785bf1e00bSNikolay Aleksandrov 22795bf1e00bSNikolay Aleksandrov hlist_for_each_entry(ent, &pg->src_list, node) 22805bf1e00bSNikolay Aleksandrov ent->flags = (ent->flags & ~BR_SGRP_F_SEND) | BR_SGRP_F_DELETE; 22815bf1e00bSNikolay Aleksandrov 22825bf1e00bSNikolay Aleksandrov memset(&src_ip, 0, sizeof(src_ip)); 2283085b53c8SNikolay Aleksandrov src_ip.proto = pg->key.addr.proto; 22845bf1e00bSNikolay Aleksandrov for (src_idx = 0; src_idx < nsrcs; src_idx++) { 2285e7cfcf2cSNikolay Aleksandrov memcpy(&src_ip.src, srcs + (src_idx * addr_size), addr_size); 22865bf1e00bSNikolay Aleksandrov ent = br_multicast_find_group_src(pg, &src_ip); 22875bf1e00bSNikolay Aleksandrov if (ent) { 22885bf1e00bSNikolay Aleksandrov ent->flags = (ent->flags & ~BR_SGRP_F_DELETE) | 22895bf1e00bSNikolay Aleksandrov BR_SGRP_F_SEND; 22905bf1e00bSNikolay Aleksandrov to_send++; 22915bf1e00bSNikolay Aleksandrov } else { 2292b0812368SNikolay Aleksandrov ent = br_multicast_new_group_src(pg, &src_ip); 22935bf1e00bSNikolay Aleksandrov } 2294b0812368SNikolay Aleksandrov if (ent) 2295b0812368SNikolay Aleksandrov br_multicast_fwd_src_handle(ent); 22965bf1e00bSNikolay Aleksandrov } 22975bf1e00bSNikolay Aleksandrov 2298adc47037SNikolay Aleksandrov br_multicast_eht_handle(brmctx, pg, h_addr, srcs, nsrcs, addr_size, 2299adc47037SNikolay Aleksandrov grec_type); 2300ddc255d9SNikolay Aleksandrov 23015bf1e00bSNikolay Aleksandrov __grp_src_delete_marked(pg); 23025bf1e00bSNikolay Aleksandrov if (to_send) 2303adc47037SNikolay Aleksandrov __grp_src_query_marked_and_rexmit(brmctx, pmctx, pg); 23045bf1e00bSNikolay Aleksandrov } 23055bf1e00bSNikolay Aleksandrov 23065bf1e00bSNikolay Aleksandrov /* State Msg type New state Actions 23075bf1e00bSNikolay Aleksandrov * EXCLUDE (X,Y) TO_EX (A) EXCLUDE (A-Y,Y*A) (A-X-Y)=Group Timer 23085bf1e00bSNikolay Aleksandrov * Delete (X-A) 23095bf1e00bSNikolay Aleksandrov * Delete (Y-A) 23105bf1e00bSNikolay Aleksandrov * Send Q(G,A-Y) 23115bf1e00bSNikolay Aleksandrov * Group Timer=GMI 23125bf1e00bSNikolay Aleksandrov */ 2313adc47037SNikolay Aleksandrov static bool __grp_src_toex_excl(struct net_bridge_mcast *brmctx, 2314adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 2315adc47037SNikolay Aleksandrov struct net_bridge_port_group *pg, void *h_addr, 2316ddc255d9SNikolay Aleksandrov void *srcs, u32 nsrcs, size_t addr_size, 2317ddc255d9SNikolay Aleksandrov int grec_type) 23185bf1e00bSNikolay Aleksandrov { 23195bf1e00bSNikolay Aleksandrov struct net_bridge_group_src *ent; 23205bf1e00bSNikolay Aleksandrov u32 src_idx, to_send = 0; 23215bf1e00bSNikolay Aleksandrov bool changed = false; 23225bf1e00bSNikolay Aleksandrov struct br_ip src_ip; 23235bf1e00bSNikolay Aleksandrov 23245bf1e00bSNikolay Aleksandrov hlist_for_each_entry(ent, &pg->src_list, node) 23255bf1e00bSNikolay Aleksandrov ent->flags = (ent->flags & ~BR_SGRP_F_SEND) | BR_SGRP_F_DELETE; 23265bf1e00bSNikolay Aleksandrov 23275bf1e00bSNikolay Aleksandrov memset(&src_ip, 0, sizeof(src_ip)); 2328085b53c8SNikolay Aleksandrov src_ip.proto = pg->key.addr.proto; 23295bf1e00bSNikolay Aleksandrov for (src_idx = 0; src_idx < nsrcs; src_idx++) { 2330e7cfcf2cSNikolay Aleksandrov memcpy(&src_ip.src, srcs + (src_idx * addr_size), addr_size); 23315bf1e00bSNikolay Aleksandrov ent = br_multicast_find_group_src(pg, &src_ip); 23325bf1e00bSNikolay Aleksandrov if (ent) { 23335bf1e00bSNikolay Aleksandrov ent->flags &= ~BR_SGRP_F_DELETE; 23345bf1e00bSNikolay Aleksandrov } else { 23355bf1e00bSNikolay Aleksandrov ent = br_multicast_new_group_src(pg, &src_ip); 23365bf1e00bSNikolay Aleksandrov if (ent) { 2337b0812368SNikolay Aleksandrov __grp_src_mod_timer(ent, pg->timer.expires); 23385bf1e00bSNikolay Aleksandrov changed = true; 23395bf1e00bSNikolay Aleksandrov } 23405bf1e00bSNikolay Aleksandrov } 23415bf1e00bSNikolay Aleksandrov if (ent && timer_pending(&ent->timer)) { 23425bf1e00bSNikolay Aleksandrov ent->flags |= BR_SGRP_F_SEND; 23435bf1e00bSNikolay Aleksandrov to_send++; 23445bf1e00bSNikolay Aleksandrov } 23455bf1e00bSNikolay Aleksandrov } 23465bf1e00bSNikolay Aleksandrov 2347adc47037SNikolay Aleksandrov if (br_multicast_eht_handle(brmctx, pg, h_addr, srcs, nsrcs, addr_size, 2348adc47037SNikolay Aleksandrov grec_type)) 2349ddc255d9SNikolay Aleksandrov changed = true; 2350ddc255d9SNikolay Aleksandrov 23515bf1e00bSNikolay Aleksandrov if (__grp_src_delete_marked(pg)) 23525bf1e00bSNikolay Aleksandrov changed = true; 23535bf1e00bSNikolay Aleksandrov if (to_send) 2354adc47037SNikolay Aleksandrov __grp_src_query_marked_and_rexmit(brmctx, pmctx, pg); 23555bf1e00bSNikolay Aleksandrov 23565bf1e00bSNikolay Aleksandrov return changed; 23575bf1e00bSNikolay Aleksandrov } 23585bf1e00bSNikolay Aleksandrov 2359adc47037SNikolay Aleksandrov static bool br_multicast_toex(struct net_bridge_mcast *brmctx, 2360adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 2361adc47037SNikolay Aleksandrov struct net_bridge_port_group *pg, void *h_addr, 2362ddc255d9SNikolay Aleksandrov void *srcs, u32 nsrcs, size_t addr_size, 2363ddc255d9SNikolay Aleksandrov int grec_type) 23645bf1e00bSNikolay Aleksandrov { 23655bf1e00bSNikolay Aleksandrov bool changed = false; 23665bf1e00bSNikolay Aleksandrov 23675bf1e00bSNikolay Aleksandrov switch (pg->filter_mode) { 23685bf1e00bSNikolay Aleksandrov case MCAST_INCLUDE: 2369adc47037SNikolay Aleksandrov __grp_src_toex_incl(brmctx, pmctx, pg, h_addr, srcs, nsrcs, 2370adc47037SNikolay Aleksandrov addr_size, grec_type); 23718266a049SNikolay Aleksandrov br_multicast_star_g_handle_mode(pg, MCAST_EXCLUDE); 23725bf1e00bSNikolay Aleksandrov changed = true; 23735bf1e00bSNikolay Aleksandrov break; 23745bf1e00bSNikolay Aleksandrov case MCAST_EXCLUDE: 2375adc47037SNikolay Aleksandrov changed = __grp_src_toex_excl(brmctx, pmctx, pg, h_addr, srcs, 2376adc47037SNikolay Aleksandrov nsrcs, addr_size, grec_type); 23775bf1e00bSNikolay Aleksandrov break; 23785bf1e00bSNikolay Aleksandrov } 23795bf1e00bSNikolay Aleksandrov 23805bf1e00bSNikolay Aleksandrov pg->filter_mode = MCAST_EXCLUDE; 2381adc47037SNikolay Aleksandrov mod_timer(&pg->timer, jiffies + br_multicast_gmi(brmctx)); 23825bf1e00bSNikolay Aleksandrov 23835bf1e00bSNikolay Aleksandrov return changed; 23845bf1e00bSNikolay Aleksandrov } 23855bf1e00bSNikolay Aleksandrov 2386109865feSNikolay Aleksandrov /* State Msg type New state Actions 2387109865feSNikolay Aleksandrov * INCLUDE (A) BLOCK (B) INCLUDE (A) Send Q(G,A*B) 2388109865feSNikolay Aleksandrov */ 2389adc47037SNikolay Aleksandrov static bool __grp_src_block_incl(struct net_bridge_mcast *brmctx, 2390adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 2391adc47037SNikolay Aleksandrov struct net_bridge_port_group *pg, void *h_addr, 2392474ddb37SNikolay Aleksandrov void *srcs, u32 nsrcs, size_t addr_size, int grec_type) 2393109865feSNikolay Aleksandrov { 2394109865feSNikolay Aleksandrov struct net_bridge_group_src *ent; 2395109865feSNikolay Aleksandrov u32 src_idx, to_send = 0; 23960ad57c99SNikolay Aleksandrov bool changed = false; 2397109865feSNikolay Aleksandrov struct br_ip src_ip; 2398109865feSNikolay Aleksandrov 2399109865feSNikolay Aleksandrov hlist_for_each_entry(ent, &pg->src_list, node) 2400109865feSNikolay Aleksandrov ent->flags &= ~BR_SGRP_F_SEND; 2401109865feSNikolay Aleksandrov 2402109865feSNikolay Aleksandrov memset(&src_ip, 0, sizeof(src_ip)); 2403085b53c8SNikolay Aleksandrov src_ip.proto = pg->key.addr.proto; 2404109865feSNikolay Aleksandrov for (src_idx = 0; src_idx < nsrcs; src_idx++) { 2405e7cfcf2cSNikolay Aleksandrov memcpy(&src_ip.src, srcs + (src_idx * addr_size), addr_size); 2406109865feSNikolay Aleksandrov ent = br_multicast_find_group_src(pg, &src_ip); 2407109865feSNikolay Aleksandrov if (ent) { 2408109865feSNikolay Aleksandrov ent->flags |= BR_SGRP_F_SEND; 2409109865feSNikolay Aleksandrov to_send++; 2410109865feSNikolay Aleksandrov } 2411109865feSNikolay Aleksandrov } 2412109865feSNikolay Aleksandrov 2413adc47037SNikolay Aleksandrov if (br_multicast_eht_handle(brmctx, pg, h_addr, srcs, nsrcs, addr_size, 2414adc47037SNikolay Aleksandrov grec_type)) 2415474ddb37SNikolay Aleksandrov changed = true; 2416474ddb37SNikolay Aleksandrov 2417109865feSNikolay Aleksandrov if (to_send) 2418adc47037SNikolay Aleksandrov __grp_src_query_marked_and_rexmit(brmctx, pmctx, pg); 2419109865feSNikolay Aleksandrov 24200ad57c99SNikolay Aleksandrov return changed; 2421109865feSNikolay Aleksandrov } 2422109865feSNikolay Aleksandrov 2423109865feSNikolay Aleksandrov /* State Msg type New state Actions 2424109865feSNikolay Aleksandrov * EXCLUDE (X,Y) BLOCK (A) EXCLUDE (X+(A-Y),Y) (A-X-Y)=Group Timer 2425109865feSNikolay Aleksandrov * Send Q(G,A-Y) 2426109865feSNikolay Aleksandrov */ 2427adc47037SNikolay Aleksandrov static bool __grp_src_block_excl(struct net_bridge_mcast *brmctx, 2428adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 2429adc47037SNikolay Aleksandrov struct net_bridge_port_group *pg, void *h_addr, 2430474ddb37SNikolay Aleksandrov void *srcs, u32 nsrcs, size_t addr_size, int grec_type) 2431109865feSNikolay Aleksandrov { 2432109865feSNikolay Aleksandrov struct net_bridge_group_src *ent; 2433109865feSNikolay Aleksandrov u32 src_idx, to_send = 0; 2434109865feSNikolay Aleksandrov bool changed = false; 2435109865feSNikolay Aleksandrov struct br_ip src_ip; 2436109865feSNikolay Aleksandrov 2437109865feSNikolay Aleksandrov hlist_for_each_entry(ent, &pg->src_list, node) 2438109865feSNikolay Aleksandrov ent->flags &= ~BR_SGRP_F_SEND; 2439109865feSNikolay Aleksandrov 2440109865feSNikolay Aleksandrov memset(&src_ip, 0, sizeof(src_ip)); 2441085b53c8SNikolay Aleksandrov src_ip.proto = pg->key.addr.proto; 2442109865feSNikolay Aleksandrov for (src_idx = 0; src_idx < nsrcs; src_idx++) { 2443e7cfcf2cSNikolay Aleksandrov memcpy(&src_ip.src, srcs + (src_idx * addr_size), addr_size); 2444109865feSNikolay Aleksandrov ent = br_multicast_find_group_src(pg, &src_ip); 2445109865feSNikolay Aleksandrov if (!ent) { 2446109865feSNikolay Aleksandrov ent = br_multicast_new_group_src(pg, &src_ip); 2447109865feSNikolay Aleksandrov if (ent) { 2448b0812368SNikolay Aleksandrov __grp_src_mod_timer(ent, pg->timer.expires); 2449109865feSNikolay Aleksandrov changed = true; 2450109865feSNikolay Aleksandrov } 2451109865feSNikolay Aleksandrov } 2452109865feSNikolay Aleksandrov if (ent && timer_pending(&ent->timer)) { 2453109865feSNikolay Aleksandrov ent->flags |= BR_SGRP_F_SEND; 2454109865feSNikolay Aleksandrov to_send++; 2455109865feSNikolay Aleksandrov } 2456109865feSNikolay Aleksandrov } 2457109865feSNikolay Aleksandrov 2458adc47037SNikolay Aleksandrov if (br_multicast_eht_handle(brmctx, pg, h_addr, srcs, nsrcs, addr_size, 2459adc47037SNikolay Aleksandrov grec_type)) 2460474ddb37SNikolay Aleksandrov changed = true; 2461474ddb37SNikolay Aleksandrov 2462109865feSNikolay Aleksandrov if (to_send) 2463adc47037SNikolay Aleksandrov __grp_src_query_marked_and_rexmit(brmctx, pmctx, pg); 2464109865feSNikolay Aleksandrov 2465109865feSNikolay Aleksandrov return changed; 2466109865feSNikolay Aleksandrov } 2467109865feSNikolay Aleksandrov 2468adc47037SNikolay Aleksandrov static bool br_multicast_block(struct net_bridge_mcast *brmctx, 2469adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 2470adc47037SNikolay Aleksandrov struct net_bridge_port_group *pg, void *h_addr, 2471474ddb37SNikolay Aleksandrov void *srcs, u32 nsrcs, size_t addr_size, int grec_type) 2472109865feSNikolay Aleksandrov { 2473109865feSNikolay Aleksandrov bool changed = false; 2474109865feSNikolay Aleksandrov 2475109865feSNikolay Aleksandrov switch (pg->filter_mode) { 2476109865feSNikolay Aleksandrov case MCAST_INCLUDE: 2477adc47037SNikolay Aleksandrov changed = __grp_src_block_incl(brmctx, pmctx, pg, h_addr, srcs, 2478adc47037SNikolay Aleksandrov nsrcs, addr_size, grec_type); 2479109865feSNikolay Aleksandrov break; 2480109865feSNikolay Aleksandrov case MCAST_EXCLUDE: 2481adc47037SNikolay Aleksandrov changed = __grp_src_block_excl(brmctx, pmctx, pg, h_addr, srcs, 2482adc47037SNikolay Aleksandrov nsrcs, addr_size, grec_type); 2483109865feSNikolay Aleksandrov break; 2484109865feSNikolay Aleksandrov } 2485109865feSNikolay Aleksandrov 2486e87e4b5cSNikolay Aleksandrov if ((pg->filter_mode == MCAST_INCLUDE && hlist_empty(&pg->src_list)) || 2487e87e4b5cSNikolay Aleksandrov br_multicast_eht_should_del_pg(pg)) { 2488d5a10222SNikolay Aleksandrov if (br_multicast_eht_should_del_pg(pg)) 2489d5a10222SNikolay Aleksandrov pg->flags |= MDB_PG_FLAGS_FAST_LEAVE; 2490e87e4b5cSNikolay Aleksandrov br_multicast_find_del_pg(pg->key.port->br, pg); 2491e87e4b5cSNikolay Aleksandrov /* a notification has already been sent and we shouldn't 2492e87e4b5cSNikolay Aleksandrov * access pg after the delete so we have to return false 2493e87e4b5cSNikolay Aleksandrov */ 2494e87e4b5cSNikolay Aleksandrov changed = false; 2495e87e4b5cSNikolay Aleksandrov } 2496e87e4b5cSNikolay Aleksandrov 2497109865feSNikolay Aleksandrov return changed; 2498109865feSNikolay Aleksandrov } 2499109865feSNikolay Aleksandrov 25000436862eSNikolay Aleksandrov static struct net_bridge_port_group * 25010436862eSNikolay Aleksandrov br_multicast_find_port(struct net_bridge_mdb_entry *mp, 25020436862eSNikolay Aleksandrov struct net_bridge_port *p, 25030436862eSNikolay Aleksandrov const unsigned char *src) 25040436862eSNikolay Aleksandrov { 2505071445c6SNikolay Aleksandrov struct net_bridge *br __maybe_unused = mp->br; 25060436862eSNikolay Aleksandrov struct net_bridge_port_group *pg; 25070436862eSNikolay Aleksandrov 25080436862eSNikolay Aleksandrov for (pg = mlock_dereference(mp->ports, br); 25090436862eSNikolay Aleksandrov pg; 25100436862eSNikolay Aleksandrov pg = mlock_dereference(pg->next, br)) 25110436862eSNikolay Aleksandrov if (br_port_group_equal(pg, p, src)) 25120436862eSNikolay Aleksandrov return pg; 25130436862eSNikolay Aleksandrov 25140436862eSNikolay Aleksandrov return NULL; 25150436862eSNikolay Aleksandrov } 25160436862eSNikolay Aleksandrov 2517adc47037SNikolay Aleksandrov static int br_ip4_multicast_igmp3_report(struct net_bridge_mcast *brmctx, 2518adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 251906499098SVlad Yasevich struct sk_buff *skb, 252006499098SVlad Yasevich u16 vid) 2521eb1d1641SHerbert Xu { 2522adc47037SNikolay Aleksandrov bool igmpv2 = brmctx->multicast_igmp_version == 2; 25230436862eSNikolay Aleksandrov struct net_bridge_mdb_entry *mdst; 25240436862eSNikolay Aleksandrov struct net_bridge_port_group *pg; 25256db6f0eaSFelix Fietkau const unsigned char *src; 2526eb1d1641SHerbert Xu struct igmpv3_report *ih; 2527eb1d1641SHerbert Xu struct igmpv3_grec *grec; 25280436862eSNikolay Aleksandrov int i, len, num, type; 252954bea721SNikolay Aleksandrov __be32 group, *h_addr; 25300436862eSNikolay Aleksandrov bool changed = false; 25310436862eSNikolay Aleksandrov int err = 0; 2532e57f6185SNikolay Aleksandrov u16 nsrcs; 2533eb1d1641SHerbert Xu 2534eb1d1641SHerbert Xu ih = igmpv3_report_hdr(skb); 2535eb1d1641SHerbert Xu num = ntohs(ih->ngrec); 2536c2d4fbd2SLinus Lüssing len = skb_transport_offset(skb) + sizeof(*ih); 2537eb1d1641SHerbert Xu 2538eb1d1641SHerbert Xu for (i = 0; i < num; i++) { 2539eb1d1641SHerbert Xu len += sizeof(*grec); 2540ba5ea614SLinus Lüssing if (!ip_mc_may_pull(skb, len)) 2541eb1d1641SHerbert Xu return -EINVAL; 2542eb1d1641SHerbert Xu 2543fd218cf9SHerbert Xu grec = (void *)(skb->data + len - sizeof(*grec)); 2544eb1d1641SHerbert Xu group = grec->grec_mca; 2545eb1d1641SHerbert Xu type = grec->grec_type; 2546e57f6185SNikolay Aleksandrov nsrcs = ntohs(grec->grec_nsrcs); 2547eb1d1641SHerbert Xu 2548e57f6185SNikolay Aleksandrov len += nsrcs * 4; 2549ba5ea614SLinus Lüssing if (!ip_mc_may_pull(skb, len)) 2550eb1d1641SHerbert Xu return -EINVAL; 2551eb1d1641SHerbert Xu 2552eb1d1641SHerbert Xu switch (type) { 2553eb1d1641SHerbert Xu case IGMPV3_MODE_IS_INCLUDE: 2554eb1d1641SHerbert Xu case IGMPV3_MODE_IS_EXCLUDE: 2555eb1d1641SHerbert Xu case IGMPV3_CHANGE_TO_INCLUDE: 2556eb1d1641SHerbert Xu case IGMPV3_CHANGE_TO_EXCLUDE: 2557eb1d1641SHerbert Xu case IGMPV3_ALLOW_NEW_SOURCES: 2558eb1d1641SHerbert Xu case IGMPV3_BLOCK_OLD_SOURCES: 2559eb1d1641SHerbert Xu break; 2560eb1d1641SHerbert Xu 2561eb1d1641SHerbert Xu default: 2562eb1d1641SHerbert Xu continue; 2563eb1d1641SHerbert Xu } 2564eb1d1641SHerbert Xu 25656db6f0eaSFelix Fietkau src = eth_hdr(skb)->h_source; 25660436862eSNikolay Aleksandrov if (nsrcs == 0 && 25670436862eSNikolay Aleksandrov (type == IGMPV3_CHANGE_TO_INCLUDE || 25680436862eSNikolay Aleksandrov type == IGMPV3_MODE_IS_INCLUDE)) { 2569adc47037SNikolay Aleksandrov if (!pmctx || igmpv2) { 2570adc47037SNikolay Aleksandrov br_ip4_multicast_leave_group(brmctx, pmctx, 2571adc47037SNikolay Aleksandrov group, vid, src); 25720436862eSNikolay Aleksandrov continue; 25730436862eSNikolay Aleksandrov } 2574bc8c20acSSatish Ashok } else { 2575adc47037SNikolay Aleksandrov err = br_ip4_multicast_add_group(brmctx, pmctx, group, 2576adc47037SNikolay Aleksandrov vid, src, igmpv2); 2577eb1d1641SHerbert Xu if (err) 2578eb1d1641SHerbert Xu break; 2579eb1d1641SHerbert Xu } 25800436862eSNikolay Aleksandrov 2581adc47037SNikolay Aleksandrov if (!pmctx || igmpv2) 25820436862eSNikolay Aleksandrov continue; 25830436862eSNikolay Aleksandrov 2584adc47037SNikolay Aleksandrov spin_lock_bh(&brmctx->br->multicast_lock); 2585adc47037SNikolay Aleksandrov mdst = br_mdb_ip4_get(brmctx->br, group, vid); 25860436862eSNikolay Aleksandrov if (!mdst) 25870436862eSNikolay Aleksandrov goto unlock_continue; 2588adc47037SNikolay Aleksandrov pg = br_multicast_find_port(mdst, pmctx->port, src); 25890436862eSNikolay Aleksandrov if (!pg || (pg->flags & MDB_PG_FLAGS_PERMANENT)) 25900436862eSNikolay Aleksandrov goto unlock_continue; 259154bea721SNikolay Aleksandrov /* reload grec and host addr */ 25920436862eSNikolay Aleksandrov grec = (void *)(skb->data + len - sizeof(*grec) - (nsrcs * 4)); 259354bea721SNikolay Aleksandrov h_addr = &ip_hdr(skb)->saddr; 25940436862eSNikolay Aleksandrov switch (type) { 25950436862eSNikolay Aleksandrov case IGMPV3_ALLOW_NEW_SOURCES: 2596adc47037SNikolay Aleksandrov changed = br_multicast_isinc_allow(brmctx, pg, h_addr, 2597adc47037SNikolay Aleksandrov grec->grec_src, 2598474ddb37SNikolay Aleksandrov nsrcs, sizeof(__be32), type); 25990436862eSNikolay Aleksandrov break; 2600e6231bcaSNikolay Aleksandrov case IGMPV3_MODE_IS_INCLUDE: 2601adc47037SNikolay Aleksandrov changed = br_multicast_isinc_allow(brmctx, pg, h_addr, 2602adc47037SNikolay Aleksandrov grec->grec_src, 2603474ddb37SNikolay Aleksandrov nsrcs, sizeof(__be32), type); 2604e6231bcaSNikolay Aleksandrov break; 2605e6231bcaSNikolay Aleksandrov case IGMPV3_MODE_IS_EXCLUDE: 2606adc47037SNikolay Aleksandrov changed = br_multicast_isexc(brmctx, pg, h_addr, 2607adc47037SNikolay Aleksandrov grec->grec_src, 2608ddc255d9SNikolay Aleksandrov nsrcs, sizeof(__be32), type); 2609e6231bcaSNikolay Aleksandrov break; 26105bf1e00bSNikolay Aleksandrov case IGMPV3_CHANGE_TO_INCLUDE: 2611adc47037SNikolay Aleksandrov changed = br_multicast_toin(brmctx, pmctx, pg, h_addr, 2612adc47037SNikolay Aleksandrov grec->grec_src, 2613ddc255d9SNikolay Aleksandrov nsrcs, sizeof(__be32), type); 26145bf1e00bSNikolay Aleksandrov break; 26155bf1e00bSNikolay Aleksandrov case IGMPV3_CHANGE_TO_EXCLUDE: 2616adc47037SNikolay Aleksandrov changed = br_multicast_toex(brmctx, pmctx, pg, h_addr, 2617adc47037SNikolay Aleksandrov grec->grec_src, 2618ddc255d9SNikolay Aleksandrov nsrcs, sizeof(__be32), type); 26195bf1e00bSNikolay Aleksandrov break; 2620109865feSNikolay Aleksandrov case IGMPV3_BLOCK_OLD_SOURCES: 2621adc47037SNikolay Aleksandrov changed = br_multicast_block(brmctx, pmctx, pg, h_addr, 2622adc47037SNikolay Aleksandrov grec->grec_src, 2623474ddb37SNikolay Aleksandrov nsrcs, sizeof(__be32), type); 2624109865feSNikolay Aleksandrov break; 26250436862eSNikolay Aleksandrov } 26260436862eSNikolay Aleksandrov if (changed) 2627adc47037SNikolay Aleksandrov br_mdb_notify(brmctx->br->dev, mdst, pg, RTM_NEWMDB); 26280436862eSNikolay Aleksandrov unlock_continue: 2629adc47037SNikolay Aleksandrov spin_unlock_bh(&brmctx->br->multicast_lock); 2630bc8c20acSSatish Ashok } 2631eb1d1641SHerbert Xu 2632eb1d1641SHerbert Xu return err; 2633eb1d1641SHerbert Xu } 2634eb1d1641SHerbert Xu 2635dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 2636adc47037SNikolay Aleksandrov static int br_ip6_multicast_mld2_report(struct net_bridge_mcast *brmctx, 2637adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 263806499098SVlad Yasevich struct sk_buff *skb, 263906499098SVlad Yasevich u16 vid) 264008b202b6SYOSHIFUJI Hideaki { 2641adc47037SNikolay Aleksandrov bool mldv1 = brmctx->multicast_mld_version == 1; 26420436862eSNikolay Aleksandrov struct net_bridge_mdb_entry *mdst; 26430436862eSNikolay Aleksandrov struct net_bridge_port_group *pg; 2644ba5ea614SLinus Lüssing unsigned int nsrcs_offset; 26456db6f0eaSFelix Fietkau const unsigned char *src; 264608b202b6SYOSHIFUJI Hideaki struct icmp6hdr *icmp6h; 264754bea721SNikolay Aleksandrov struct in6_addr *h_addr; 264808b202b6SYOSHIFUJI Hideaki struct mld2_grec *grec; 2649ba5ea614SLinus Lüssing unsigned int grec_len; 26500436862eSNikolay Aleksandrov bool changed = false; 26510436862eSNikolay Aleksandrov int i, len, num; 265208b202b6SYOSHIFUJI Hideaki int err = 0; 265308b202b6SYOSHIFUJI Hideaki 2654ba5ea614SLinus Lüssing if (!ipv6_mc_may_pull(skb, sizeof(*icmp6h))) 265508b202b6SYOSHIFUJI Hideaki return -EINVAL; 265608b202b6SYOSHIFUJI Hideaki 265708b202b6SYOSHIFUJI Hideaki icmp6h = icmp6_hdr(skb); 265808b202b6SYOSHIFUJI Hideaki num = ntohs(icmp6h->icmp6_dataun.un_data16[1]); 2659c2d4fbd2SLinus Lüssing len = skb_transport_offset(skb) + sizeof(*icmp6h); 266008b202b6SYOSHIFUJI Hideaki 266108b202b6SYOSHIFUJI Hideaki for (i = 0; i < num; i++) { 2662e57f6185SNikolay Aleksandrov __be16 *_nsrcs, __nsrcs; 2663e57f6185SNikolay Aleksandrov u16 nsrcs; 266408b202b6SYOSHIFUJI Hideaki 2665ba5ea614SLinus Lüssing nsrcs_offset = len + offsetof(struct mld2_grec, grec_nsrcs); 2666ba5ea614SLinus Lüssing 2667ba5ea614SLinus Lüssing if (skb_transport_offset(skb) + ipv6_transport_len(skb) < 26685fc6266aSLinus Lüssing nsrcs_offset + sizeof(__nsrcs)) 2669ba5ea614SLinus Lüssing return -EINVAL; 2670ba5ea614SLinus Lüssing 2671e57f6185SNikolay Aleksandrov _nsrcs = skb_header_pointer(skb, nsrcs_offset, 2672e57f6185SNikolay Aleksandrov sizeof(__nsrcs), &__nsrcs); 2673e57f6185SNikolay Aleksandrov if (!_nsrcs) 267408b202b6SYOSHIFUJI Hideaki return -EINVAL; 267508b202b6SYOSHIFUJI Hideaki 2676e57f6185SNikolay Aleksandrov nsrcs = ntohs(*_nsrcs); 2677e57f6185SNikolay Aleksandrov grec_len = struct_size(grec, grec_src, nsrcs); 2678ba5ea614SLinus Lüssing 2679ba5ea614SLinus Lüssing if (!ipv6_mc_may_pull(skb, len + grec_len)) 268008b202b6SYOSHIFUJI Hideaki return -EINVAL; 268108b202b6SYOSHIFUJI Hideaki 268208b202b6SYOSHIFUJI Hideaki grec = (struct mld2_grec *)(skb->data + len); 2683ba5ea614SLinus Lüssing len += grec_len; 268408b202b6SYOSHIFUJI Hideaki 268508b202b6SYOSHIFUJI Hideaki switch (grec->grec_type) { 268608b202b6SYOSHIFUJI Hideaki case MLD2_MODE_IS_INCLUDE: 268708b202b6SYOSHIFUJI Hideaki case MLD2_MODE_IS_EXCLUDE: 268808b202b6SYOSHIFUJI Hideaki case MLD2_CHANGE_TO_INCLUDE: 268908b202b6SYOSHIFUJI Hideaki case MLD2_CHANGE_TO_EXCLUDE: 269008b202b6SYOSHIFUJI Hideaki case MLD2_ALLOW_NEW_SOURCES: 269108b202b6SYOSHIFUJI Hideaki case MLD2_BLOCK_OLD_SOURCES: 269208b202b6SYOSHIFUJI Hideaki break; 269308b202b6SYOSHIFUJI Hideaki 269408b202b6SYOSHIFUJI Hideaki default: 269508b202b6SYOSHIFUJI Hideaki continue; 269608b202b6SYOSHIFUJI Hideaki } 269708b202b6SYOSHIFUJI Hideaki 26986db6f0eaSFelix Fietkau src = eth_hdr(skb)->h_source; 2699bc8c20acSSatish Ashok if ((grec->grec_type == MLD2_CHANGE_TO_INCLUDE || 2700bc8c20acSSatish Ashok grec->grec_type == MLD2_MODE_IS_INCLUDE) && 2701e57f6185SNikolay Aleksandrov nsrcs == 0) { 2702adc47037SNikolay Aleksandrov if (!pmctx || mldv1) { 2703adc47037SNikolay Aleksandrov br_ip6_multicast_leave_group(brmctx, pmctx, 27040436862eSNikolay Aleksandrov &grec->grec_mca, 27056db6f0eaSFelix Fietkau vid, src); 27060436862eSNikolay Aleksandrov continue; 27070436862eSNikolay Aleksandrov } 2708bc8c20acSSatish Ashok } else { 2709adc47037SNikolay Aleksandrov err = br_ip6_multicast_add_group(brmctx, pmctx, 27106db6f0eaSFelix Fietkau &grec->grec_mca, vid, 27110436862eSNikolay Aleksandrov src, mldv1); 27129264251eSDavide Caratti if (err) 271308b202b6SYOSHIFUJI Hideaki break; 271408b202b6SYOSHIFUJI Hideaki } 27150436862eSNikolay Aleksandrov 2716adc47037SNikolay Aleksandrov if (!pmctx || mldv1) 27170436862eSNikolay Aleksandrov continue; 27180436862eSNikolay Aleksandrov 2719adc47037SNikolay Aleksandrov spin_lock_bh(&brmctx->br->multicast_lock); 2720adc47037SNikolay Aleksandrov mdst = br_mdb_ip6_get(brmctx->br, &grec->grec_mca, vid); 27210436862eSNikolay Aleksandrov if (!mdst) 27220436862eSNikolay Aleksandrov goto unlock_continue; 2723adc47037SNikolay Aleksandrov pg = br_multicast_find_port(mdst, pmctx->port, src); 27240436862eSNikolay Aleksandrov if (!pg || (pg->flags & MDB_PG_FLAGS_PERMANENT)) 27250436862eSNikolay Aleksandrov goto unlock_continue; 272654bea721SNikolay Aleksandrov h_addr = &ipv6_hdr(skb)->saddr; 27270436862eSNikolay Aleksandrov switch (grec->grec_type) { 27280436862eSNikolay Aleksandrov case MLD2_ALLOW_NEW_SOURCES: 2729adc47037SNikolay Aleksandrov changed = br_multicast_isinc_allow(brmctx, pg, h_addr, 273054bea721SNikolay Aleksandrov grec->grec_src, nsrcs, 2731474ddb37SNikolay Aleksandrov sizeof(struct in6_addr), 2732474ddb37SNikolay Aleksandrov grec->grec_type); 27330436862eSNikolay Aleksandrov break; 2734e6231bcaSNikolay Aleksandrov case MLD2_MODE_IS_INCLUDE: 2735adc47037SNikolay Aleksandrov changed = br_multicast_isinc_allow(brmctx, pg, h_addr, 273654bea721SNikolay Aleksandrov grec->grec_src, nsrcs, 2737474ddb37SNikolay Aleksandrov sizeof(struct in6_addr), 2738474ddb37SNikolay Aleksandrov grec->grec_type); 2739e6231bcaSNikolay Aleksandrov break; 2740e6231bcaSNikolay Aleksandrov case MLD2_MODE_IS_EXCLUDE: 2741adc47037SNikolay Aleksandrov changed = br_multicast_isexc(brmctx, pg, h_addr, 274254bea721SNikolay Aleksandrov grec->grec_src, nsrcs, 2743ddc255d9SNikolay Aleksandrov sizeof(struct in6_addr), 2744ddc255d9SNikolay Aleksandrov grec->grec_type); 2745e6231bcaSNikolay Aleksandrov break; 27465bf1e00bSNikolay Aleksandrov case MLD2_CHANGE_TO_INCLUDE: 2747adc47037SNikolay Aleksandrov changed = br_multicast_toin(brmctx, pmctx, pg, h_addr, 274854bea721SNikolay Aleksandrov grec->grec_src, nsrcs, 2749ddc255d9SNikolay Aleksandrov sizeof(struct in6_addr), 2750ddc255d9SNikolay Aleksandrov grec->grec_type); 27515bf1e00bSNikolay Aleksandrov break; 27525bf1e00bSNikolay Aleksandrov case MLD2_CHANGE_TO_EXCLUDE: 2753adc47037SNikolay Aleksandrov changed = br_multicast_toex(brmctx, pmctx, pg, h_addr, 275454bea721SNikolay Aleksandrov grec->grec_src, nsrcs, 2755ddc255d9SNikolay Aleksandrov sizeof(struct in6_addr), 2756ddc255d9SNikolay Aleksandrov grec->grec_type); 27575bf1e00bSNikolay Aleksandrov break; 2758109865feSNikolay Aleksandrov case MLD2_BLOCK_OLD_SOURCES: 2759adc47037SNikolay Aleksandrov changed = br_multicast_block(brmctx, pmctx, pg, h_addr, 276054bea721SNikolay Aleksandrov grec->grec_src, nsrcs, 2761474ddb37SNikolay Aleksandrov sizeof(struct in6_addr), 2762474ddb37SNikolay Aleksandrov grec->grec_type); 2763109865feSNikolay Aleksandrov break; 27640436862eSNikolay Aleksandrov } 27650436862eSNikolay Aleksandrov if (changed) 2766adc47037SNikolay Aleksandrov br_mdb_notify(brmctx->br->dev, mdst, pg, RTM_NEWMDB); 27670436862eSNikolay Aleksandrov unlock_continue: 2768adc47037SNikolay Aleksandrov spin_unlock_bh(&brmctx->br->multicast_lock); 2769bc8c20acSSatish Ashok } 277008b202b6SYOSHIFUJI Hideaki 277108b202b6SYOSHIFUJI Hideaki return err; 277208b202b6SYOSHIFUJI Hideaki } 277308b202b6SYOSHIFUJI Hideaki #endif 277408b202b6SYOSHIFUJI Hideaki 2775adc47037SNikolay Aleksandrov static bool br_ip4_multicast_select_querier(struct net_bridge_mcast *brmctx, 27762cd41431SLinus Lüssing struct net_bridge_port *port, 2777dc4eb53aSLinus Lüssing __be32 saddr) 2778dc4eb53aSLinus Lüssing { 2779d3d065c0SNikolay Aleksandrov if (!timer_pending(&brmctx->ip4_own_query.timer) && 2780d3d065c0SNikolay Aleksandrov !timer_pending(&brmctx->ip4_other_query.timer)) 2781dc4eb53aSLinus Lüssing goto update; 2782dc4eb53aSLinus Lüssing 2783d3d065c0SNikolay Aleksandrov if (!brmctx->ip4_querier.addr.src.ip4) 2784dc4eb53aSLinus Lüssing goto update; 2785dc4eb53aSLinus Lüssing 2786d3d065c0SNikolay Aleksandrov if (ntohl(saddr) <= ntohl(brmctx->ip4_querier.addr.src.ip4)) 2787dc4eb53aSLinus Lüssing goto update; 2788dc4eb53aSLinus Lüssing 2789dc4eb53aSLinus Lüssing return false; 2790dc4eb53aSLinus Lüssing 2791dc4eb53aSLinus Lüssing update: 2792d3d065c0SNikolay Aleksandrov brmctx->ip4_querier.addr.src.ip4 = saddr; 2793dc4eb53aSLinus Lüssing 27942cd41431SLinus Lüssing /* update protected by general multicast_lock by caller */ 2795d3d065c0SNikolay Aleksandrov rcu_assign_pointer(brmctx->ip4_querier.port, port); 27962cd41431SLinus Lüssing 2797dc4eb53aSLinus Lüssing return true; 2798dc4eb53aSLinus Lüssing } 2799dc4eb53aSLinus Lüssing 2800dc4eb53aSLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 2801adc47037SNikolay Aleksandrov static bool br_ip6_multicast_select_querier(struct net_bridge_mcast *brmctx, 28022cd41431SLinus Lüssing struct net_bridge_port *port, 2803dc4eb53aSLinus Lüssing struct in6_addr *saddr) 2804dc4eb53aSLinus Lüssing { 2805d3d065c0SNikolay Aleksandrov if (!timer_pending(&brmctx->ip6_own_query.timer) && 2806d3d065c0SNikolay Aleksandrov !timer_pending(&brmctx->ip6_other_query.timer)) 2807dc4eb53aSLinus Lüssing goto update; 2808dc4eb53aSLinus Lüssing 2809d3d065c0SNikolay Aleksandrov if (ipv6_addr_cmp(saddr, &brmctx->ip6_querier.addr.src.ip6) <= 0) 2810dc4eb53aSLinus Lüssing goto update; 2811dc4eb53aSLinus Lüssing 2812dc4eb53aSLinus Lüssing return false; 2813dc4eb53aSLinus Lüssing 2814dc4eb53aSLinus Lüssing update: 2815d3d065c0SNikolay Aleksandrov brmctx->ip6_querier.addr.src.ip6 = *saddr; 2816dc4eb53aSLinus Lüssing 28172cd41431SLinus Lüssing /* update protected by general multicast_lock by caller */ 2818d3d065c0SNikolay Aleksandrov rcu_assign_pointer(brmctx->ip6_querier.port, port); 28192cd41431SLinus Lüssing 2820dc4eb53aSLinus Lüssing return true; 2821dc4eb53aSLinus Lüssing } 2822dc4eb53aSLinus Lüssing #endif 2823dc4eb53aSLinus Lüssing 2824cc0fdd80SLinus Lüssing static void 2825adc47037SNikolay Aleksandrov br_multicast_update_query_timer(struct net_bridge_mcast *brmctx, 282690010b36SLinus Lüssing struct bridge_mcast_other_query *query, 2827b00589afSLinus Lüssing unsigned long max_delay) 2828b00589afSLinus Lüssing { 282990010b36SLinus Lüssing if (!timer_pending(&query->timer)) 283090010b36SLinus Lüssing query->delay_time = jiffies + max_delay; 2831b00589afSLinus Lüssing 2832adc47037SNikolay Aleksandrov mod_timer(&query->timer, jiffies + brmctx->multicast_querier_interval); 2833b00589afSLinus Lüssing } 2834b00589afSLinus Lüssing 28356d549648SNogah Frankel static void br_port_mc_router_state_change(struct net_bridge_port *p, 28366d549648SNogah Frankel bool is_mc_router) 28376d549648SNogah Frankel { 28386d549648SNogah Frankel struct switchdev_attr attr = { 28396d549648SNogah Frankel .orig_dev = p->dev, 28406d549648SNogah Frankel .id = SWITCHDEV_ATTR_ID_PORT_MROUTER, 28416d549648SNogah Frankel .flags = SWITCHDEV_F_DEFER, 28426d549648SNogah Frankel .u.mrouter = is_mc_router, 28436d549648SNogah Frankel }; 28446d549648SNogah Frankel 2845dcbdf135SVladimir Oltean switchdev_port_attr_set(p->dev, &attr, NULL); 28466d549648SNogah Frankel } 28476d549648SNogah Frankel 2848d9b8c4d8SLinus Lüssing static struct net_bridge_port * 2849d3d065c0SNikolay Aleksandrov br_multicast_rport_from_node(struct net_bridge_mcast *brmctx, 2850d9b8c4d8SLinus Lüssing struct hlist_head *mc_router_list, 2851d9b8c4d8SLinus Lüssing struct hlist_node *rlist) 2852d9b8c4d8SLinus Lüssing { 28539632233eSNikolay Aleksandrov struct net_bridge_mcast_port *pmctx; 28549632233eSNikolay Aleksandrov 2855a3c02e76SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 2856d3d065c0SNikolay Aleksandrov if (mc_router_list == &brmctx->ip6_mc_router_list) 28579632233eSNikolay Aleksandrov pmctx = hlist_entry(rlist, struct net_bridge_mcast_port, 28589632233eSNikolay Aleksandrov ip6_rlist); 28599632233eSNikolay Aleksandrov else 2860a3c02e76SLinus Lüssing #endif 28619632233eSNikolay Aleksandrov pmctx = hlist_entry(rlist, struct net_bridge_mcast_port, 28629632233eSNikolay Aleksandrov ip4_rlist); 28639632233eSNikolay Aleksandrov 28649632233eSNikolay Aleksandrov return pmctx->port; 2865d9b8c4d8SLinus Lüssing } 2866d9b8c4d8SLinus Lüssing 2867d9b8c4d8SLinus Lüssing static struct hlist_node * 2868d3d065c0SNikolay Aleksandrov br_multicast_get_rport_slot(struct net_bridge_mcast *brmctx, 2869d9b8c4d8SLinus Lüssing struct net_bridge_port *port, 2870d9b8c4d8SLinus Lüssing struct hlist_head *mc_router_list) 2871d9b8c4d8SLinus Lüssing 2872d9b8c4d8SLinus Lüssing { 2873d9b8c4d8SLinus Lüssing struct hlist_node *slot = NULL; 2874d9b8c4d8SLinus Lüssing struct net_bridge_port *p; 2875d9b8c4d8SLinus Lüssing struct hlist_node *rlist; 2876d9b8c4d8SLinus Lüssing 2877d9b8c4d8SLinus Lüssing hlist_for_each(rlist, mc_router_list) { 2878d3d065c0SNikolay Aleksandrov p = br_multicast_rport_from_node(brmctx, mc_router_list, rlist); 2879d9b8c4d8SLinus Lüssing 2880d9b8c4d8SLinus Lüssing if ((unsigned long)port >= (unsigned long)p) 2881d9b8c4d8SLinus Lüssing break; 2882d9b8c4d8SLinus Lüssing 2883d9b8c4d8SLinus Lüssing slot = rlist; 2884d9b8c4d8SLinus Lüssing } 2885d9b8c4d8SLinus Lüssing 2886d9b8c4d8SLinus Lüssing return slot; 2887d9b8c4d8SLinus Lüssing } 2888d9b8c4d8SLinus Lüssing 2889adc47037SNikolay Aleksandrov static bool br_multicast_no_router_otherpf(struct net_bridge_mcast_port *pmctx, 2890a3c02e76SLinus Lüssing struct hlist_node *rnode) 2891a3c02e76SLinus Lüssing { 2892a3c02e76SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 2893adc47037SNikolay Aleksandrov if (rnode != &pmctx->ip6_rlist) 2894adc47037SNikolay Aleksandrov return hlist_unhashed(&pmctx->ip6_rlist); 2895a3c02e76SLinus Lüssing else 2896adc47037SNikolay Aleksandrov return hlist_unhashed(&pmctx->ip4_rlist); 2897a3c02e76SLinus Lüssing #else 2898a3c02e76SLinus Lüssing return true; 2899a3c02e76SLinus Lüssing #endif 2900a3c02e76SLinus Lüssing } 2901a3c02e76SLinus Lüssing 2902d9b8c4d8SLinus Lüssing /* Add port to router_list 29037e80c124Sstephen hemminger * list is maintained ordered by pointer value 29047e80c124Sstephen hemminger * and locked by br->multicast_lock and RCU 29057e80c124Sstephen hemminger */ 2906d3d065c0SNikolay Aleksandrov static void br_multicast_add_router(struct net_bridge_mcast *brmctx, 2907adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 2908d9b8c4d8SLinus Lüssing struct hlist_node *rlist, 2909d9b8c4d8SLinus Lüssing struct hlist_head *mc_router_list) 29100909e117SHerbert Xu { 2911d9b8c4d8SLinus Lüssing struct hlist_node *slot; 29120909e117SHerbert Xu 2913d9b8c4d8SLinus Lüssing if (!hlist_unhashed(rlist)) 29141a040eacSNikolay Aleksandrov return; 29151a040eacSNikolay Aleksandrov 2916adc47037SNikolay Aleksandrov slot = br_multicast_get_rport_slot(brmctx, pmctx->port, mc_router_list); 29170909e117SHerbert Xu 29187e80c124Sstephen hemminger if (slot) 2919d9b8c4d8SLinus Lüssing hlist_add_behind_rcu(rlist, slot); 2920dcdca2c4Sstephen hemminger else 2921d9b8c4d8SLinus Lüssing hlist_add_head_rcu(rlist, mc_router_list); 2922d9b8c4d8SLinus Lüssing 2923a3c02e76SLinus Lüssing /* For backwards compatibility for now, only notify if we 2924a3c02e76SLinus Lüssing * switched from no IPv4/IPv6 multicast router to a new 2925a3c02e76SLinus Lüssing * IPv4 or IPv6 multicast router. 2926a3c02e76SLinus Lüssing */ 2927adc47037SNikolay Aleksandrov if (br_multicast_no_router_otherpf(pmctx, rlist)) { 2928adc47037SNikolay Aleksandrov br_rtr_notify(pmctx->port->br->dev, pmctx->port, RTM_NEWMDB); 2929adc47037SNikolay Aleksandrov br_port_mc_router_state_change(pmctx->port, true); 29300909e117SHerbert Xu } 2931a3c02e76SLinus Lüssing } 29320909e117SHerbert Xu 2933d9b8c4d8SLinus Lüssing /* Add port to router_list 2934d9b8c4d8SLinus Lüssing * list is maintained ordered by pointer value 2935d9b8c4d8SLinus Lüssing * and locked by br->multicast_lock and RCU 2936d9b8c4d8SLinus Lüssing */ 2937adc47037SNikolay Aleksandrov static void br_ip4_multicast_add_router(struct net_bridge_mcast *brmctx, 2938adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx) 2939eb1d1641SHerbert Xu { 2940adc47037SNikolay Aleksandrov br_multicast_add_router(brmctx, pmctx, &pmctx->ip4_rlist, 2941adc47037SNikolay Aleksandrov &brmctx->ip4_mc_router_list); 2942d9b8c4d8SLinus Lüssing } 2943d9b8c4d8SLinus Lüssing 2944a3c02e76SLinus Lüssing /* Add port to router_list 2945a3c02e76SLinus Lüssing * list is maintained ordered by pointer value 2946a3c02e76SLinus Lüssing * and locked by br->multicast_lock and RCU 2947a3c02e76SLinus Lüssing */ 2948adc47037SNikolay Aleksandrov static void br_ip6_multicast_add_router(struct net_bridge_mcast *brmctx, 2949adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx) 2950a3c02e76SLinus Lüssing { 2951a3c02e76SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 2952adc47037SNikolay Aleksandrov br_multicast_add_router(brmctx, pmctx, &pmctx->ip6_rlist, 2953adc47037SNikolay Aleksandrov &brmctx->ip6_mc_router_list); 2954a3c02e76SLinus Lüssing #endif 2955a3c02e76SLinus Lüssing } 2956a3c02e76SLinus Lüssing 2957adc47037SNikolay Aleksandrov static void br_multicast_mark_router(struct net_bridge_mcast *brmctx, 2958adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 2959d9b8c4d8SLinus Lüssing struct timer_list *timer, 2960d9b8c4d8SLinus Lüssing struct hlist_node *rlist, 2961d9b8c4d8SLinus Lüssing struct hlist_head *mc_router_list) 2962d9b8c4d8SLinus Lüssing { 2963eb1d1641SHerbert Xu unsigned long now = jiffies; 2964eb1d1641SHerbert Xu 2965adc47037SNikolay Aleksandrov if (!pmctx) { 2966d3d065c0SNikolay Aleksandrov if (brmctx->multicast_router == MDB_RTR_TYPE_TEMP_QUERY) { 2967d3d065c0SNikolay Aleksandrov if (!br_ip4_multicast_is_router(brmctx) && 2968d3d065c0SNikolay Aleksandrov !br_ip6_multicast_is_router(brmctx)) 2969adc47037SNikolay Aleksandrov br_mc_router_state_change(brmctx->br, true); 2970d3d065c0SNikolay Aleksandrov mod_timer(timer, now + brmctx->multicast_querier_interval); 297177041420SYotam Gigi } 2972eb1d1641SHerbert Xu return; 2973eb1d1641SHerbert Xu } 2974eb1d1641SHerbert Xu 2975adc47037SNikolay Aleksandrov if (pmctx->multicast_router == MDB_RTR_TYPE_DISABLED || 2976adc47037SNikolay Aleksandrov pmctx->multicast_router == MDB_RTR_TYPE_PERM) 2977eb1d1641SHerbert Xu return; 2978eb1d1641SHerbert Xu 2979adc47037SNikolay Aleksandrov br_multicast_add_router(brmctx, pmctx, rlist, mc_router_list); 2980d3d065c0SNikolay Aleksandrov mod_timer(timer, now + brmctx->multicast_querier_interval); 2981d9b8c4d8SLinus Lüssing } 2982eb1d1641SHerbert Xu 2983adc47037SNikolay Aleksandrov static void br_ip4_multicast_mark_router(struct net_bridge_mcast *brmctx, 2984adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx) 2985d9b8c4d8SLinus Lüssing { 2986adc47037SNikolay Aleksandrov struct timer_list *timer = &brmctx->ip4_mc_router_timer; 2987d9b8c4d8SLinus Lüssing struct hlist_node *rlist = NULL; 2988d9b8c4d8SLinus Lüssing 2989adc47037SNikolay Aleksandrov if (pmctx) { 2990adc47037SNikolay Aleksandrov timer = &pmctx->ip4_mc_router_timer; 2991adc47037SNikolay Aleksandrov rlist = &pmctx->ip4_rlist; 2992d9b8c4d8SLinus Lüssing } 2993d9b8c4d8SLinus Lüssing 2994adc47037SNikolay Aleksandrov br_multicast_mark_router(brmctx, pmctx, timer, rlist, 2995adc47037SNikolay Aleksandrov &brmctx->ip4_mc_router_list); 2996eb1d1641SHerbert Xu } 2997eb1d1641SHerbert Xu 2998adc47037SNikolay Aleksandrov static void br_ip6_multicast_mark_router(struct net_bridge_mcast *brmctx, 2999adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx) 3000a3c02e76SLinus Lüssing { 3001a3c02e76SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 3002adc47037SNikolay Aleksandrov struct timer_list *timer = &brmctx->ip6_mc_router_timer; 3003a3c02e76SLinus Lüssing struct hlist_node *rlist = NULL; 3004a3c02e76SLinus Lüssing 3005adc47037SNikolay Aleksandrov if (pmctx) { 3006adc47037SNikolay Aleksandrov timer = &pmctx->ip6_mc_router_timer; 3007adc47037SNikolay Aleksandrov rlist = &pmctx->ip6_rlist; 3008a3c02e76SLinus Lüssing } 3009a3c02e76SLinus Lüssing 3010adc47037SNikolay Aleksandrov br_multicast_mark_router(brmctx, pmctx, timer, rlist, 3011adc47037SNikolay Aleksandrov &brmctx->ip6_mc_router_list); 3012a3c02e76SLinus Lüssing #endif 3013a3c02e76SLinus Lüssing } 3014a3c02e76SLinus Lüssing 3015b19232efSLinus Lüssing static void 3016adc47037SNikolay Aleksandrov br_ip4_multicast_query_received(struct net_bridge_mcast *brmctx, 3017adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 301890010b36SLinus Lüssing struct bridge_mcast_other_query *query, 3019dc4eb53aSLinus Lüssing struct br_ip *saddr, 3020b00589afSLinus Lüssing unsigned long max_delay) 3021eb1d1641SHerbert Xu { 3022adc47037SNikolay Aleksandrov if (!br_ip4_multicast_select_querier(brmctx, pmctx->port, saddr->src.ip4)) 3023eb1d1641SHerbert Xu return; 3024eb1d1641SHerbert Xu 3025adc47037SNikolay Aleksandrov br_multicast_update_query_timer(brmctx, query, max_delay); 3026adc47037SNikolay Aleksandrov br_ip4_multicast_mark_router(brmctx, pmctx); 3027eb1d1641SHerbert Xu } 3028eb1d1641SHerbert Xu 3029b19232efSLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 3030b19232efSLinus Lüssing static void 3031adc47037SNikolay Aleksandrov br_ip6_multicast_query_received(struct net_bridge_mcast *brmctx, 3032adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 3033b19232efSLinus Lüssing struct bridge_mcast_other_query *query, 3034b19232efSLinus Lüssing struct br_ip *saddr, 3035b19232efSLinus Lüssing unsigned long max_delay) 3036b19232efSLinus Lüssing { 3037adc47037SNikolay Aleksandrov if (!br_ip6_multicast_select_querier(brmctx, pmctx->port, &saddr->src.ip6)) 3038b19232efSLinus Lüssing return; 3039b19232efSLinus Lüssing 3040adc47037SNikolay Aleksandrov br_multicast_update_query_timer(brmctx, query, max_delay); 3041adc47037SNikolay Aleksandrov br_ip6_multicast_mark_router(brmctx, pmctx); 3042b19232efSLinus Lüssing } 3043b19232efSLinus Lüssing #endif 3044b19232efSLinus Lüssing 3045adc47037SNikolay Aleksandrov static void br_ip4_multicast_query(struct net_bridge_mcast *brmctx, 3046adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 304706499098SVlad Yasevich struct sk_buff *skb, 304806499098SVlad Yasevich u16 vid) 3049eb1d1641SHerbert Xu { 3050ba5ea614SLinus Lüssing unsigned int transport_len = ip_transport_len(skb); 3051b71d1d42SEric Dumazet const struct iphdr *iph = ip_hdr(skb); 3052eb1d1641SHerbert Xu struct igmphdr *ih = igmp_hdr(skb); 3053eb1d1641SHerbert Xu struct net_bridge_mdb_entry *mp; 3054eb1d1641SHerbert Xu struct igmpv3_query *ih3; 3055eb1d1641SHerbert Xu struct net_bridge_port_group *p; 3056e8051688SEric Dumazet struct net_bridge_port_group __rcu **pp; 3057dc4eb53aSLinus Lüssing struct br_ip saddr; 3058eb1d1641SHerbert Xu unsigned long max_delay; 3059eb1d1641SHerbert Xu unsigned long now = jiffies; 3060eb1d1641SHerbert Xu __be32 group; 3061eb1d1641SHerbert Xu 3062adc47037SNikolay Aleksandrov spin_lock(&brmctx->br->multicast_lock); 3063adc47037SNikolay Aleksandrov if (!netif_running(brmctx->br->dev) || 3064adc47037SNikolay Aleksandrov (pmctx && pmctx->port->state == BR_STATE_DISABLED)) 3065eb1d1641SHerbert Xu goto out; 3066eb1d1641SHerbert Xu 3067eb1d1641SHerbert Xu group = ih->group; 3068eb1d1641SHerbert Xu 3069ba5ea614SLinus Lüssing if (transport_len == sizeof(*ih)) { 3070eb1d1641SHerbert Xu max_delay = ih->code * (HZ / IGMP_TIMER_SCALE); 3071eb1d1641SHerbert Xu 3072eb1d1641SHerbert Xu if (!max_delay) { 3073eb1d1641SHerbert Xu max_delay = 10 * HZ; 3074eb1d1641SHerbert Xu group = 0; 3075eb1d1641SHerbert Xu } 3076ba5ea614SLinus Lüssing } else if (transport_len >= sizeof(*ih3)) { 3077eb1d1641SHerbert Xu ih3 = igmpv3_query_hdr(skb); 307823550b83SNikolay Aleksandrov if (ih3->nsrcs || 3079d3d065c0SNikolay Aleksandrov (brmctx->multicast_igmp_version == 3 && group && 3080d3d065c0SNikolay Aleksandrov ih3->suppress)) 3081bec68ff1SYOSHIFUJI Hideaki goto out; 3082eb1d1641SHerbert Xu 30830ba8c9ecSYOSHIFUJI Hideaki / 吉藤英明 max_delay = ih3->code ? 30840ba8c9ecSYOSHIFUJI Hideaki / 吉藤英明 IGMPV3_MRC(ih3->code) * (HZ / IGMP_TIMER_SCALE) : 1; 30859afd85c9SLinus Lüssing } else { 30869ed973ccSLinus Lüssing goto out; 30879ed973ccSLinus Lüssing } 30889ed973ccSLinus Lüssing 3089dc4eb53aSLinus Lüssing if (!group) { 3090dc4eb53aSLinus Lüssing saddr.proto = htons(ETH_P_IP); 3091deb96566SNikolay Aleksandrov saddr.src.ip4 = iph->saddr; 3092b00589afSLinus Lüssing 3093adc47037SNikolay Aleksandrov br_ip4_multicast_query_received(brmctx, pmctx, 3094d3d065c0SNikolay Aleksandrov &brmctx->ip4_other_query, 3095dc4eb53aSLinus Lüssing &saddr, max_delay); 3096eb1d1641SHerbert Xu goto out; 3097dc4eb53aSLinus Lüssing } 3098eb1d1641SHerbert Xu 3099adc47037SNikolay Aleksandrov mp = br_mdb_ip4_get(brmctx->br, group, vid); 3100eb1d1641SHerbert Xu if (!mp) 3101eb1d1641SHerbert Xu goto out; 3102eb1d1641SHerbert Xu 3103d3d065c0SNikolay Aleksandrov max_delay *= brmctx->multicast_last_member_count; 3104eb1d1641SHerbert Xu 3105ff0fd34eSAndrew Lunn if (mp->host_joined && 3106eb1d1641SHerbert Xu (timer_pending(&mp->timer) ? 3107eb1d1641SHerbert Xu time_after(mp->timer.expires, now + max_delay) : 3108eb1d1641SHerbert Xu try_to_del_timer_sync(&mp->timer) >= 0)) 3109eb1d1641SHerbert Xu mod_timer(&mp->timer, now + max_delay); 3110eb1d1641SHerbert Xu 3111e8051688SEric Dumazet for (pp = &mp->ports; 3112adc47037SNikolay Aleksandrov (p = mlock_dereference(*pp, brmctx->br)) != NULL; 3113e8051688SEric Dumazet pp = &p->next) { 3114eb1d1641SHerbert Xu if (timer_pending(&p->timer) ? 3115eb1d1641SHerbert Xu time_after(p->timer.expires, now + max_delay) : 311623550b83SNikolay Aleksandrov try_to_del_timer_sync(&p->timer) >= 0 && 3117d3d065c0SNikolay Aleksandrov (brmctx->multicast_igmp_version == 2 || 311823550b83SNikolay Aleksandrov p->filter_mode == MCAST_EXCLUDE)) 311924f9cdcbSHerbert Xu mod_timer(&p->timer, now + max_delay); 3120eb1d1641SHerbert Xu } 3121eb1d1641SHerbert Xu 3122eb1d1641SHerbert Xu out: 3123adc47037SNikolay Aleksandrov spin_unlock(&brmctx->br->multicast_lock); 3124eb1d1641SHerbert Xu } 3125eb1d1641SHerbert Xu 3126dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 3127adc47037SNikolay Aleksandrov static int br_ip6_multicast_query(struct net_bridge_mcast *brmctx, 3128adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 312906499098SVlad Yasevich struct sk_buff *skb, 313006499098SVlad Yasevich u16 vid) 313108b202b6SYOSHIFUJI Hideaki { 3132ba5ea614SLinus Lüssing unsigned int transport_len = ipv6_transport_len(skb); 3133eca2a43bSstephen hemminger struct mld_msg *mld; 313408b202b6SYOSHIFUJI Hideaki struct net_bridge_mdb_entry *mp; 313508b202b6SYOSHIFUJI Hideaki struct mld2_query *mld2q; 3136e8051688SEric Dumazet struct net_bridge_port_group *p; 3137e8051688SEric Dumazet struct net_bridge_port_group __rcu **pp; 3138dc4eb53aSLinus Lüssing struct br_ip saddr; 313908b202b6SYOSHIFUJI Hideaki unsigned long max_delay; 314008b202b6SYOSHIFUJI Hideaki unsigned long now = jiffies; 3141856ce5d0SLinus Lüssing unsigned int offset = skb_transport_offset(skb); 3142b71d1d42SEric Dumazet const struct in6_addr *group = NULL; 31439ed973ccSLinus Lüssing bool is_general_query; 314408b202b6SYOSHIFUJI Hideaki int err = 0; 314508b202b6SYOSHIFUJI Hideaki 3146adc47037SNikolay Aleksandrov spin_lock(&brmctx->br->multicast_lock); 3147adc47037SNikolay Aleksandrov if (!netif_running(brmctx->br->dev) || 3148adc47037SNikolay Aleksandrov (pmctx && pmctx->port->state == BR_STATE_DISABLED)) 314908b202b6SYOSHIFUJI Hideaki goto out; 315008b202b6SYOSHIFUJI Hideaki 3151ba5ea614SLinus Lüssing if (transport_len == sizeof(*mld)) { 3152856ce5d0SLinus Lüssing if (!pskb_may_pull(skb, offset + sizeof(*mld))) { 315308b202b6SYOSHIFUJI Hideaki err = -EINVAL; 315408b202b6SYOSHIFUJI Hideaki goto out; 315508b202b6SYOSHIFUJI Hideaki } 315608b202b6SYOSHIFUJI Hideaki mld = (struct mld_msg *) icmp6_hdr(skb); 31574715213dSLi RongQing max_delay = msecs_to_jiffies(ntohs(mld->mld_maxdelay)); 315808b202b6SYOSHIFUJI Hideaki if (max_delay) 315908b202b6SYOSHIFUJI Hideaki group = &mld->mld_mca; 3160248ba8ecSLinus Lüssing } else { 3161856ce5d0SLinus Lüssing if (!pskb_may_pull(skb, offset + sizeof(*mld2q))) { 316208b202b6SYOSHIFUJI Hideaki err = -EINVAL; 316308b202b6SYOSHIFUJI Hideaki goto out; 316408b202b6SYOSHIFUJI Hideaki } 316508b202b6SYOSHIFUJI Hideaki mld2q = (struct mld2_query *)icmp6_hdr(skb); 316608b202b6SYOSHIFUJI Hideaki if (!mld2q->mld2q_nsrcs) 316708b202b6SYOSHIFUJI Hideaki group = &mld2q->mld2q_mca; 3168d3d065c0SNikolay Aleksandrov if (brmctx->multicast_mld_version == 2 && 316923550b83SNikolay Aleksandrov !ipv6_addr_any(&mld2q->mld2q_mca) && 317023550b83SNikolay Aleksandrov mld2q->mld2q_suppress) 317123550b83SNikolay Aleksandrov goto out; 3172e3f5b170SDaniel Borkmann 3173e3f5b170SDaniel Borkmann max_delay = max(msecs_to_jiffies(mldv2_mrc(mld2q)), 1UL); 317408b202b6SYOSHIFUJI Hideaki } 317508b202b6SYOSHIFUJI Hideaki 31769ed973ccSLinus Lüssing is_general_query = group && ipv6_addr_any(group); 31779ed973ccSLinus Lüssing 3178dc4eb53aSLinus Lüssing if (is_general_query) { 3179dc4eb53aSLinus Lüssing saddr.proto = htons(ETH_P_IPV6); 3180deb96566SNikolay Aleksandrov saddr.src.ip6 = ipv6_hdr(skb)->saddr; 3181b00589afSLinus Lüssing 3182adc47037SNikolay Aleksandrov br_ip6_multicast_query_received(brmctx, pmctx, 3183d3d065c0SNikolay Aleksandrov &brmctx->ip6_other_query, 3184dc4eb53aSLinus Lüssing &saddr, max_delay); 318508b202b6SYOSHIFUJI Hideaki goto out; 31866c03ee8bSLinus Lüssing } else if (!group) { 31876c03ee8bSLinus Lüssing goto out; 3188dc4eb53aSLinus Lüssing } 318908b202b6SYOSHIFUJI Hideaki 3190adc47037SNikolay Aleksandrov mp = br_mdb_ip6_get(brmctx->br, group, vid); 319108b202b6SYOSHIFUJI Hideaki if (!mp) 319208b202b6SYOSHIFUJI Hideaki goto out; 319308b202b6SYOSHIFUJI Hideaki 3194d3d065c0SNikolay Aleksandrov max_delay *= brmctx->multicast_last_member_count; 3195ff0fd34eSAndrew Lunn if (mp->host_joined && 319608b202b6SYOSHIFUJI Hideaki (timer_pending(&mp->timer) ? 319708b202b6SYOSHIFUJI Hideaki time_after(mp->timer.expires, now + max_delay) : 319808b202b6SYOSHIFUJI Hideaki try_to_del_timer_sync(&mp->timer) >= 0)) 319908b202b6SYOSHIFUJI Hideaki mod_timer(&mp->timer, now + max_delay); 320008b202b6SYOSHIFUJI Hideaki 3201e8051688SEric Dumazet for (pp = &mp->ports; 3202adc47037SNikolay Aleksandrov (p = mlock_dereference(*pp, brmctx->br)) != NULL; 3203e8051688SEric Dumazet pp = &p->next) { 320408b202b6SYOSHIFUJI Hideaki if (timer_pending(&p->timer) ? 320508b202b6SYOSHIFUJI Hideaki time_after(p->timer.expires, now + max_delay) : 320623550b83SNikolay Aleksandrov try_to_del_timer_sync(&p->timer) >= 0 && 3207d3d065c0SNikolay Aleksandrov (brmctx->multicast_mld_version == 1 || 320823550b83SNikolay Aleksandrov p->filter_mode == MCAST_EXCLUDE)) 320924f9cdcbSHerbert Xu mod_timer(&p->timer, now + max_delay); 321008b202b6SYOSHIFUJI Hideaki } 321108b202b6SYOSHIFUJI Hideaki 321208b202b6SYOSHIFUJI Hideaki out: 3213adc47037SNikolay Aleksandrov spin_unlock(&brmctx->br->multicast_lock); 321408b202b6SYOSHIFUJI Hideaki return err; 321508b202b6SYOSHIFUJI Hideaki } 321608b202b6SYOSHIFUJI Hideaki #endif 321708b202b6SYOSHIFUJI Hideaki 321890010b36SLinus Lüssing static void 3219adc47037SNikolay Aleksandrov br_multicast_leave_group(struct net_bridge_mcast *brmctx, 3220adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 3221cc0fdd80SLinus Lüssing struct br_ip *group, 322290010b36SLinus Lüssing struct bridge_mcast_other_query *other_query, 32236db6f0eaSFelix Fietkau struct bridge_mcast_own_query *own_query, 32246db6f0eaSFelix Fietkau const unsigned char *src) 3225eb1d1641SHerbert Xu { 3226eb1d1641SHerbert Xu struct net_bridge_mdb_entry *mp; 3227eb1d1641SHerbert Xu struct net_bridge_port_group *p; 3228eb1d1641SHerbert Xu unsigned long now; 3229eb1d1641SHerbert Xu unsigned long time; 3230eb1d1641SHerbert Xu 3231adc47037SNikolay Aleksandrov spin_lock(&brmctx->br->multicast_lock); 3232adc47037SNikolay Aleksandrov if (!netif_running(brmctx->br->dev) || 3233adc47037SNikolay Aleksandrov (pmctx && pmctx->port->state == BR_STATE_DISABLED)) 3234eb1d1641SHerbert Xu goto out; 3235eb1d1641SHerbert Xu 3236adc47037SNikolay Aleksandrov mp = br_mdb_ip_get(brmctx->br, group); 3237eb1d1641SHerbert Xu if (!mp) 3238eb1d1641SHerbert Xu goto out; 3239eb1d1641SHerbert Xu 3240adc47037SNikolay Aleksandrov if (pmctx && (pmctx->port->flags & BR_MULTICAST_FAST_LEAVE)) { 3241544586f7SSatish Ashok struct net_bridge_port_group __rcu **pp; 3242544586f7SSatish Ashok 3243544586f7SSatish Ashok for (pp = &mp->ports; 3244adc47037SNikolay Aleksandrov (p = mlock_dereference(*pp, brmctx->br)) != NULL; 3245544586f7SSatish Ashok pp = &p->next) { 3246adc47037SNikolay Aleksandrov if (!br_port_group_equal(p, pmctx->port, src)) 3247544586f7SSatish Ashok continue; 3248544586f7SSatish Ashok 32495c725b6bSNikolay Aleksandrov if (p->flags & MDB_PG_FLAGS_PERMANENT) 32505c725b6bSNikolay Aleksandrov break; 32515c725b6bSNikolay Aleksandrov 3252681590bdSNikolay Aleksandrov p->flags |= MDB_PG_FLAGS_FAST_LEAVE; 3253681590bdSNikolay Aleksandrov br_multicast_del_pg(mp, p, pp); 3254544586f7SSatish Ashok } 3255544586f7SSatish Ashok goto out; 3256544586f7SSatish Ashok } 3257544586f7SSatish Ashok 3258544586f7SSatish Ashok if (timer_pending(&other_query->timer)) 3259544586f7SSatish Ashok goto out; 3260544586f7SSatish Ashok 3261adc47037SNikolay Aleksandrov if (br_opt_get(brmctx->br, BROPT_MULTICAST_QUERIER)) { 3262adc47037SNikolay Aleksandrov __br_multicast_send_query(brmctx, pmctx, NULL, NULL, &mp->addr, 326342c11ccfSNikolay Aleksandrov false, 0, NULL); 32646b7df111SCong Wang 3265d3d065c0SNikolay Aleksandrov time = jiffies + brmctx->multicast_last_member_count * 3266d3d065c0SNikolay Aleksandrov brmctx->multicast_last_member_interval; 3267cc0fdd80SLinus Lüssing 326890010b36SLinus Lüssing mod_timer(&own_query->timer, time); 32696b7df111SCong Wang 3270adc47037SNikolay Aleksandrov for (p = mlock_dereference(mp->ports, brmctx->br); 32716b7df111SCong Wang p != NULL; 3272adc47037SNikolay Aleksandrov p = mlock_dereference(p->next, brmctx->br)) { 3273adc47037SNikolay Aleksandrov if (!br_port_group_equal(p, pmctx->port, src)) 32746b7df111SCong Wang continue; 32756b7df111SCong Wang 32766b7df111SCong Wang if (!hlist_unhashed(&p->mglist) && 32776b7df111SCong Wang (timer_pending(&p->timer) ? 32786b7df111SCong Wang time_after(p->timer.expires, time) : 32796b7df111SCong Wang try_to_del_timer_sync(&p->timer) >= 0)) { 32806b7df111SCong Wang mod_timer(&p->timer, time); 32816b7df111SCong Wang } 32826b7df111SCong Wang 32836b7df111SCong Wang break; 32846b7df111SCong Wang } 32856b7df111SCong Wang } 32866b7df111SCong Wang 3287eb1d1641SHerbert Xu now = jiffies; 3288d3d065c0SNikolay Aleksandrov time = now + brmctx->multicast_last_member_count * 3289d3d065c0SNikolay Aleksandrov brmctx->multicast_last_member_interval; 3290eb1d1641SHerbert Xu 3291adc47037SNikolay Aleksandrov if (!pmctx) { 3292ff0fd34eSAndrew Lunn if (mp->host_joined && 3293eb1d1641SHerbert Xu (timer_pending(&mp->timer) ? 3294eb1d1641SHerbert Xu time_after(mp->timer.expires, time) : 3295eb1d1641SHerbert Xu try_to_del_timer_sync(&mp->timer) >= 0)) { 3296eb1d1641SHerbert Xu mod_timer(&mp->timer, time); 3297eb1d1641SHerbert Xu } 3298454594f3SLinus Lüssing 3299454594f3SLinus Lüssing goto out; 3300454594f3SLinus Lüssing } 3301454594f3SLinus Lüssing 3302adc47037SNikolay Aleksandrov for (p = mlock_dereference(mp->ports, brmctx->br); 3303454594f3SLinus Lüssing p != NULL; 3304adc47037SNikolay Aleksandrov p = mlock_dereference(p->next, brmctx->br)) { 3305adc47037SNikolay Aleksandrov if (p->key.port != pmctx->port) 3306454594f3SLinus Lüssing continue; 3307454594f3SLinus Lüssing 3308454594f3SLinus Lüssing if (!hlist_unhashed(&p->mglist) && 3309454594f3SLinus Lüssing (timer_pending(&p->timer) ? 3310454594f3SLinus Lüssing time_after(p->timer.expires, time) : 3311454594f3SLinus Lüssing try_to_del_timer_sync(&p->timer) >= 0)) { 3312454594f3SLinus Lüssing mod_timer(&p->timer, time); 3313454594f3SLinus Lüssing } 3314454594f3SLinus Lüssing 3315454594f3SLinus Lüssing break; 3316eb1d1641SHerbert Xu } 3317eb1d1641SHerbert Xu out: 3318adc47037SNikolay Aleksandrov spin_unlock(&brmctx->br->multicast_lock); 3319eb1d1641SHerbert Xu } 3320eb1d1641SHerbert Xu 3321adc47037SNikolay Aleksandrov static void br_ip4_multicast_leave_group(struct net_bridge_mcast *brmctx, 3322adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 3323b0e9a30dSVlad Yasevich __be32 group, 33246db6f0eaSFelix Fietkau __u16 vid, 33256db6f0eaSFelix Fietkau const unsigned char *src) 33268ef2a9a5SYOSHIFUJI Hideaki { 33278ef2a9a5SYOSHIFUJI Hideaki struct br_ip br_group; 332890010b36SLinus Lüssing struct bridge_mcast_own_query *own_query; 33298ef2a9a5SYOSHIFUJI Hideaki 33308ef2a9a5SYOSHIFUJI Hideaki if (ipv4_is_local_multicast(group)) 33318ef2a9a5SYOSHIFUJI Hideaki return; 33328ef2a9a5SYOSHIFUJI Hideaki 3333adc47037SNikolay Aleksandrov own_query = pmctx ? &pmctx->ip4_own_query : &brmctx->ip4_own_query; 333490010b36SLinus Lüssing 33351515a63fSNikolay Aleksandrov memset(&br_group, 0, sizeof(br_group)); 3336eab3227bSNikolay Aleksandrov br_group.dst.ip4 = group; 33378ef2a9a5SYOSHIFUJI Hideaki br_group.proto = htons(ETH_P_IP); 3338b0e9a30dSVlad Yasevich br_group.vid = vid; 33398ef2a9a5SYOSHIFUJI Hideaki 3340adc47037SNikolay Aleksandrov br_multicast_leave_group(brmctx, pmctx, &br_group, 3341adc47037SNikolay Aleksandrov &brmctx->ip4_other_query, 33426db6f0eaSFelix Fietkau own_query, src); 33438ef2a9a5SYOSHIFUJI Hideaki } 33448ef2a9a5SYOSHIFUJI Hideaki 3345dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 3346adc47037SNikolay Aleksandrov static void br_ip6_multicast_leave_group(struct net_bridge_mcast *brmctx, 3347adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 3348b0e9a30dSVlad Yasevich const struct in6_addr *group, 33496db6f0eaSFelix Fietkau __u16 vid, 33506db6f0eaSFelix Fietkau const unsigned char *src) 335108b202b6SYOSHIFUJI Hideaki { 335208b202b6SYOSHIFUJI Hideaki struct br_ip br_group; 335390010b36SLinus Lüssing struct bridge_mcast_own_query *own_query; 335408b202b6SYOSHIFUJI Hideaki 33553c3769e6SLinus Lüssing if (ipv6_addr_is_ll_all_nodes(group)) 335608b202b6SYOSHIFUJI Hideaki return; 335708b202b6SYOSHIFUJI Hideaki 3358adc47037SNikolay Aleksandrov own_query = pmctx ? &pmctx->ip6_own_query : &brmctx->ip6_own_query; 335990010b36SLinus Lüssing 33601515a63fSNikolay Aleksandrov memset(&br_group, 0, sizeof(br_group)); 3361eab3227bSNikolay Aleksandrov br_group.dst.ip6 = *group; 336208b202b6SYOSHIFUJI Hideaki br_group.proto = htons(ETH_P_IPV6); 3363b0e9a30dSVlad Yasevich br_group.vid = vid; 336408b202b6SYOSHIFUJI Hideaki 3365adc47037SNikolay Aleksandrov br_multicast_leave_group(brmctx, pmctx, &br_group, 3366adc47037SNikolay Aleksandrov &brmctx->ip6_other_query, 33676db6f0eaSFelix Fietkau own_query, src); 336808b202b6SYOSHIFUJI Hideaki } 336908b202b6SYOSHIFUJI Hideaki #endif 33708ef2a9a5SYOSHIFUJI Hideaki 33711080ab95SNikolay Aleksandrov static void br_multicast_err_count(const struct net_bridge *br, 33721080ab95SNikolay Aleksandrov const struct net_bridge_port *p, 33731080ab95SNikolay Aleksandrov __be16 proto) 33741080ab95SNikolay Aleksandrov { 33751080ab95SNikolay Aleksandrov struct bridge_mcast_stats __percpu *stats; 33761080ab95SNikolay Aleksandrov struct bridge_mcast_stats *pstats; 33771080ab95SNikolay Aleksandrov 3378675779adSNikolay Aleksandrov if (!br_opt_get(br, BROPT_MULTICAST_STATS_ENABLED)) 33791080ab95SNikolay Aleksandrov return; 33801080ab95SNikolay Aleksandrov 33811080ab95SNikolay Aleksandrov if (p) 33821080ab95SNikolay Aleksandrov stats = p->mcast_stats; 33831080ab95SNikolay Aleksandrov else 33841080ab95SNikolay Aleksandrov stats = br->mcast_stats; 33851080ab95SNikolay Aleksandrov if (WARN_ON(!stats)) 33861080ab95SNikolay Aleksandrov return; 33871080ab95SNikolay Aleksandrov 33881080ab95SNikolay Aleksandrov pstats = this_cpu_ptr(stats); 33891080ab95SNikolay Aleksandrov 33901080ab95SNikolay Aleksandrov u64_stats_update_begin(&pstats->syncp); 33911080ab95SNikolay Aleksandrov switch (proto) { 33921080ab95SNikolay Aleksandrov case htons(ETH_P_IP): 33931080ab95SNikolay Aleksandrov pstats->mstats.igmp_parse_errors++; 33941080ab95SNikolay Aleksandrov break; 33951080ab95SNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6) 33961080ab95SNikolay Aleksandrov case htons(ETH_P_IPV6): 33971080ab95SNikolay Aleksandrov pstats->mstats.mld_parse_errors++; 33981080ab95SNikolay Aleksandrov break; 33991080ab95SNikolay Aleksandrov #endif 34001080ab95SNikolay Aleksandrov } 34011080ab95SNikolay Aleksandrov u64_stats_update_end(&pstats->syncp); 34021080ab95SNikolay Aleksandrov } 34031080ab95SNikolay Aleksandrov 3404adc47037SNikolay Aleksandrov static void br_multicast_pim(struct net_bridge_mcast *brmctx, 3405adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 340691b02d3dSNikolay Aleksandrov const struct sk_buff *skb) 340791b02d3dSNikolay Aleksandrov { 340891b02d3dSNikolay Aleksandrov unsigned int offset = skb_transport_offset(skb); 340991b02d3dSNikolay Aleksandrov struct pimhdr *pimhdr, _pimhdr; 341091b02d3dSNikolay Aleksandrov 341191b02d3dSNikolay Aleksandrov pimhdr = skb_header_pointer(skb, offset, sizeof(_pimhdr), &_pimhdr); 341291b02d3dSNikolay Aleksandrov if (!pimhdr || pim_hdr_version(pimhdr) != PIM_VERSION || 341391b02d3dSNikolay Aleksandrov pim_hdr_type(pimhdr) != PIM_TYPE_HELLO) 341491b02d3dSNikolay Aleksandrov return; 341591b02d3dSNikolay Aleksandrov 3416adc47037SNikolay Aleksandrov spin_lock(&brmctx->br->multicast_lock); 3417adc47037SNikolay Aleksandrov br_ip4_multicast_mark_router(brmctx, pmctx); 3418adc47037SNikolay Aleksandrov spin_unlock(&brmctx->br->multicast_lock); 341991b02d3dSNikolay Aleksandrov } 342091b02d3dSNikolay Aleksandrov 3421adc47037SNikolay Aleksandrov static int br_ip4_multicast_mrd_rcv(struct net_bridge_mcast *brmctx, 3422adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 34234b3087c7SLinus Lüssing struct sk_buff *skb) 34244b3087c7SLinus Lüssing { 34254b3087c7SLinus Lüssing if (ip_hdr(skb)->protocol != IPPROTO_IGMP || 34264b3087c7SLinus Lüssing igmp_hdr(skb)->type != IGMP_MRDISC_ADV) 34274b3087c7SLinus Lüssing return -ENOMSG; 34284b3087c7SLinus Lüssing 3429adc47037SNikolay Aleksandrov spin_lock(&brmctx->br->multicast_lock); 3430adc47037SNikolay Aleksandrov br_ip4_multicast_mark_router(brmctx, pmctx); 3431adc47037SNikolay Aleksandrov spin_unlock(&brmctx->br->multicast_lock); 34324b3087c7SLinus Lüssing 34334b3087c7SLinus Lüssing return 0; 34344b3087c7SLinus Lüssing } 34354b3087c7SLinus Lüssing 3436adc47037SNikolay Aleksandrov static int br_multicast_ipv4_rcv(struct net_bridge_mcast *brmctx, 3437adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 343806499098SVlad Yasevich struct sk_buff *skb, 343906499098SVlad Yasevich u16 vid) 3440eb1d1641SHerbert Xu { 3441adc47037SNikolay Aleksandrov struct net_bridge_port *p = pmctx ? pmctx->port : NULL; 34426db6f0eaSFelix Fietkau const unsigned char *src; 3443eb1d1641SHerbert Xu struct igmphdr *ih; 3444eb1d1641SHerbert Xu int err; 3445eb1d1641SHerbert Xu 3446ba5ea614SLinus Lüssing err = ip_mc_check_igmp(skb); 3447eb1d1641SHerbert Xu 34489afd85c9SLinus Lüssing if (err == -ENOMSG) { 344991b02d3dSNikolay Aleksandrov if (!ipv4_is_local_multicast(ip_hdr(skb)->daddr)) { 3450bd4265feSHerbert Xu BR_INPUT_SKB_CB(skb)->mrouters_only = 1; 345191b02d3dSNikolay Aleksandrov } else if (pim_ipv4_all_pim_routers(ip_hdr(skb)->daddr)) { 345291b02d3dSNikolay Aleksandrov if (ip_hdr(skb)->protocol == IPPROTO_PIM) 3453adc47037SNikolay Aleksandrov br_multicast_pim(brmctx, pmctx, skb); 34544b3087c7SLinus Lüssing } else if (ipv4_is_all_snoopers(ip_hdr(skb)->daddr)) { 3455adc47037SNikolay Aleksandrov br_ip4_multicast_mrd_rcv(brmctx, pmctx, skb); 34564b3087c7SLinus Lüssing } 34574b3087c7SLinus Lüssing 3458eb1d1641SHerbert Xu return 0; 34599afd85c9SLinus Lüssing } else if (err < 0) { 3460adc47037SNikolay Aleksandrov br_multicast_err_count(brmctx->br, p, skb->protocol); 34619afd85c9SLinus Lüssing return err; 3462bd4265feSHerbert Xu } 3463eb1d1641SHerbert Xu 34649afd85c9SLinus Lüssing ih = igmp_hdr(skb); 34656db6f0eaSFelix Fietkau src = eth_hdr(skb)->h_source; 34661080ab95SNikolay Aleksandrov BR_INPUT_SKB_CB(skb)->igmp = ih->type; 3467eb1d1641SHerbert Xu 3468eb1d1641SHerbert Xu switch (ih->type) { 3469eb1d1641SHerbert Xu case IGMP_HOST_MEMBERSHIP_REPORT: 3470eb1d1641SHerbert Xu case IGMPV2_HOST_MEMBERSHIP_REPORT: 347162b2bcb4SFernando Luis Vázquez Cao BR_INPUT_SKB_CB(skb)->mrouters_only = 1; 3472adc47037SNikolay Aleksandrov err = br_ip4_multicast_add_group(brmctx, pmctx, ih->group, vid, 3473adc47037SNikolay Aleksandrov src, true); 3474eb1d1641SHerbert Xu break; 3475eb1d1641SHerbert Xu case IGMPV3_HOST_MEMBERSHIP_REPORT: 3476adc47037SNikolay Aleksandrov err = br_ip4_multicast_igmp3_report(brmctx, pmctx, skb, vid); 3477eb1d1641SHerbert Xu break; 3478eb1d1641SHerbert Xu case IGMP_HOST_MEMBERSHIP_QUERY: 3479adc47037SNikolay Aleksandrov br_ip4_multicast_query(brmctx, pmctx, skb, vid); 3480eb1d1641SHerbert Xu break; 3481eb1d1641SHerbert Xu case IGMP_HOST_LEAVE_MESSAGE: 3482adc47037SNikolay Aleksandrov br_ip4_multicast_leave_group(brmctx, pmctx, ih->group, vid, src); 3483eb1d1641SHerbert Xu break; 3484eb1d1641SHerbert Xu } 3485eb1d1641SHerbert Xu 3486adc47037SNikolay Aleksandrov br_multicast_count(brmctx->br, p, skb, BR_INPUT_SKB_CB(skb)->igmp, 34871080ab95SNikolay Aleksandrov BR_MCAST_DIR_RX); 34881080ab95SNikolay Aleksandrov 3489eb1d1641SHerbert Xu return err; 3490eb1d1641SHerbert Xu } 3491eb1d1641SHerbert Xu 3492dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 3493adc47037SNikolay Aleksandrov static void br_ip6_multicast_mrd_rcv(struct net_bridge_mcast *brmctx, 3494adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 34954b3087c7SLinus Lüssing struct sk_buff *skb) 34964b3087c7SLinus Lüssing { 34974b3087c7SLinus Lüssing if (icmp6_hdr(skb)->icmp6_type != ICMPV6_MRDISC_ADV) 349899014088SLinus Lüssing return; 34994b3087c7SLinus Lüssing 3500adc47037SNikolay Aleksandrov spin_lock(&brmctx->br->multicast_lock); 3501adc47037SNikolay Aleksandrov br_ip6_multicast_mark_router(brmctx, pmctx); 3502adc47037SNikolay Aleksandrov spin_unlock(&brmctx->br->multicast_lock); 35034b3087c7SLinus Lüssing } 35044b3087c7SLinus Lüssing 3505adc47037SNikolay Aleksandrov static int br_multicast_ipv6_rcv(struct net_bridge_mcast *brmctx, 3506adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx, 350706499098SVlad Yasevich struct sk_buff *skb, 350806499098SVlad Yasevich u16 vid) 350908b202b6SYOSHIFUJI Hideaki { 3510adc47037SNikolay Aleksandrov struct net_bridge_port *p = pmctx ? pmctx->port : NULL; 35116db6f0eaSFelix Fietkau const unsigned char *src; 35129afd85c9SLinus Lüssing struct mld_msg *mld; 351308b202b6SYOSHIFUJI Hideaki int err; 351408b202b6SYOSHIFUJI Hideaki 3515ba5ea614SLinus Lüssing err = ipv6_mc_check_mld(skb); 351608b202b6SYOSHIFUJI Hideaki 351799014088SLinus Lüssing if (err == -ENOMSG || err == -ENODATA) { 35189afd85c9SLinus Lüssing if (!ipv6_addr_is_ll_all_nodes(&ipv6_hdr(skb)->daddr)) 35198fad9c39SLinus Lüssing BR_INPUT_SKB_CB(skb)->mrouters_only = 1; 352099014088SLinus Lüssing if (err == -ENODATA && 352199014088SLinus Lüssing ipv6_addr_is_all_snoopers(&ipv6_hdr(skb)->daddr)) 3522adc47037SNikolay Aleksandrov br_ip6_multicast_mrd_rcv(brmctx, pmctx, skb); 35234b3087c7SLinus Lüssing 352408b202b6SYOSHIFUJI Hideaki return 0; 35259afd85c9SLinus Lüssing } else if (err < 0) { 3526adc47037SNikolay Aleksandrov br_multicast_err_count(brmctx->br, p, skb->protocol); 35279afd85c9SLinus Lüssing return err; 352808b202b6SYOSHIFUJI Hideaki } 352908b202b6SYOSHIFUJI Hideaki 35309afd85c9SLinus Lüssing mld = (struct mld_msg *)skb_transport_header(skb); 35311080ab95SNikolay Aleksandrov BR_INPUT_SKB_CB(skb)->igmp = mld->mld_type; 353208b202b6SYOSHIFUJI Hideaki 35339afd85c9SLinus Lüssing switch (mld->mld_type) { 353408b202b6SYOSHIFUJI Hideaki case ICMPV6_MGM_REPORT: 35356db6f0eaSFelix Fietkau src = eth_hdr(skb)->h_source; 3536fc2af6c7SFernando Luis Vázquez Cao BR_INPUT_SKB_CB(skb)->mrouters_only = 1; 3537adc47037SNikolay Aleksandrov err = br_ip6_multicast_add_group(brmctx, pmctx, &mld->mld_mca, 3538adc47037SNikolay Aleksandrov vid, src, true); 353908b202b6SYOSHIFUJI Hideaki break; 354008b202b6SYOSHIFUJI Hideaki case ICMPV6_MLD2_REPORT: 3541adc47037SNikolay Aleksandrov err = br_ip6_multicast_mld2_report(brmctx, pmctx, skb, vid); 354208b202b6SYOSHIFUJI Hideaki break; 354308b202b6SYOSHIFUJI Hideaki case ICMPV6_MGM_QUERY: 3544adc47037SNikolay Aleksandrov err = br_ip6_multicast_query(brmctx, pmctx, skb, vid); 354508b202b6SYOSHIFUJI Hideaki break; 354608b202b6SYOSHIFUJI Hideaki case ICMPV6_MGM_REDUCTION: 35476db6f0eaSFelix Fietkau src = eth_hdr(skb)->h_source; 3548adc47037SNikolay Aleksandrov br_ip6_multicast_leave_group(brmctx, pmctx, &mld->mld_mca, vid, 3549adc47037SNikolay Aleksandrov src); 35509afd85c9SLinus Lüssing break; 355108b202b6SYOSHIFUJI Hideaki } 355208b202b6SYOSHIFUJI Hideaki 3553adc47037SNikolay Aleksandrov br_multicast_count(brmctx->br, p, skb, BR_INPUT_SKB_CB(skb)->igmp, 35541080ab95SNikolay Aleksandrov BR_MCAST_DIR_RX); 35551080ab95SNikolay Aleksandrov 355608b202b6SYOSHIFUJI Hideaki return err; 355708b202b6SYOSHIFUJI Hideaki } 355808b202b6SYOSHIFUJI Hideaki #endif 355908b202b6SYOSHIFUJI Hideaki 3560f4b7002aSNikolay Aleksandrov int br_multicast_rcv(struct net_bridge_mcast **brmctx, 3561f4b7002aSNikolay Aleksandrov struct net_bridge_mcast_port **pmctx, 3562f4b7002aSNikolay Aleksandrov struct net_bridge_vlan *vlan, 356306499098SVlad Yasevich struct sk_buff *skb, u16 vid) 3564eb1d1641SHerbert Xu { 35651080ab95SNikolay Aleksandrov int ret = 0; 35661080ab95SNikolay Aleksandrov 35671fafc7a9SYOSHIFUJI Hideaki / 吉藤英明 BR_INPUT_SKB_CB(skb)->igmp = 0; 35681fafc7a9SYOSHIFUJI Hideaki / 吉藤英明 BR_INPUT_SKB_CB(skb)->mrouters_only = 0; 35691fafc7a9SYOSHIFUJI Hideaki / 吉藤英明 3570f4b7002aSNikolay Aleksandrov if (!br_opt_get((*brmctx)->br, BROPT_MULTICAST_ENABLED)) 3571eb1d1641SHerbert Xu return 0; 3572eb1d1641SHerbert Xu 3573f4b7002aSNikolay Aleksandrov if (br_opt_get((*brmctx)->br, BROPT_MCAST_VLAN_SNOOPING_ENABLED) && vlan) { 3574f4b7002aSNikolay Aleksandrov const struct net_bridge_vlan *masterv; 3575f4b7002aSNikolay Aleksandrov 3576f4b7002aSNikolay Aleksandrov /* the vlan has the master flag set only when transmitting 3577f4b7002aSNikolay Aleksandrov * through the bridge device 3578f4b7002aSNikolay Aleksandrov */ 3579f4b7002aSNikolay Aleksandrov if (br_vlan_is_master(vlan)) { 3580f4b7002aSNikolay Aleksandrov masterv = vlan; 3581f4b7002aSNikolay Aleksandrov *brmctx = &vlan->br_mcast_ctx; 3582f4b7002aSNikolay Aleksandrov *pmctx = NULL; 3583f4b7002aSNikolay Aleksandrov } else { 3584f4b7002aSNikolay Aleksandrov masterv = vlan->brvlan; 3585f4b7002aSNikolay Aleksandrov *brmctx = &vlan->brvlan->br_mcast_ctx; 3586f4b7002aSNikolay Aleksandrov *pmctx = &vlan->port_mcast_ctx; 3587f4b7002aSNikolay Aleksandrov } 3588f4b7002aSNikolay Aleksandrov 3589f4b7002aSNikolay Aleksandrov if (!(masterv->priv_flags & BR_VLFLAG_GLOBAL_MCAST_ENABLED)) 3590f4b7002aSNikolay Aleksandrov return 0; 3591f4b7002aSNikolay Aleksandrov } 3592f4b7002aSNikolay Aleksandrov 3593eb1d1641SHerbert Xu switch (skb->protocol) { 3594eb1d1641SHerbert Xu case htons(ETH_P_IP): 3595f4b7002aSNikolay Aleksandrov ret = br_multicast_ipv4_rcv(*brmctx, *pmctx, skb, vid); 35961080ab95SNikolay Aleksandrov break; 3597dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 359808b202b6SYOSHIFUJI Hideaki case htons(ETH_P_IPV6): 3599f4b7002aSNikolay Aleksandrov ret = br_multicast_ipv6_rcv(*brmctx, *pmctx, skb, vid); 36001080ab95SNikolay Aleksandrov break; 360108b202b6SYOSHIFUJI Hideaki #endif 3602eb1d1641SHerbert Xu } 3603eb1d1641SHerbert Xu 36041080ab95SNikolay Aleksandrov return ret; 3605eb1d1641SHerbert Xu } 3606eb1d1641SHerbert Xu 3607adc47037SNikolay Aleksandrov static void br_multicast_query_expired(struct net_bridge_mcast *brmctx, 36082cd41431SLinus Lüssing struct bridge_mcast_own_query *query, 36092cd41431SLinus Lüssing struct bridge_mcast_querier *querier) 3610cc0fdd80SLinus Lüssing { 3611adc47037SNikolay Aleksandrov spin_lock(&brmctx->br->multicast_lock); 3612adc47037SNikolay Aleksandrov if (query->startup_sent < brmctx->multicast_startup_query_count) 3613cc0fdd80SLinus Lüssing query->startup_sent++; 3614cc0fdd80SLinus Lüssing 361571d9f614SEric Dumazet RCU_INIT_POINTER(querier->port, NULL); 3616adc47037SNikolay Aleksandrov br_multicast_send_query(brmctx, NULL, query); 3617adc47037SNikolay Aleksandrov spin_unlock(&brmctx->br->multicast_lock); 3618cc0fdd80SLinus Lüssing } 3619cc0fdd80SLinus Lüssing 362088c1f37fSAllen Pais static void br_ip4_multicast_query_expired(struct timer_list *t) 3621eb1d1641SHerbert Xu { 3622d3d065c0SNikolay Aleksandrov struct net_bridge_mcast *brmctx = from_timer(brmctx, t, 3623d3d065c0SNikolay Aleksandrov ip4_own_query.timer); 3624eb1d1641SHerbert Xu 3625adc47037SNikolay Aleksandrov br_multicast_query_expired(brmctx, &brmctx->ip4_own_query, 3626d3d065c0SNikolay Aleksandrov &brmctx->ip4_querier); 3627eb1d1641SHerbert Xu } 3628eb1d1641SHerbert Xu 3629cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 363088c1f37fSAllen Pais static void br_ip6_multicast_query_expired(struct timer_list *t) 3631cc0fdd80SLinus Lüssing { 3632d3d065c0SNikolay Aleksandrov struct net_bridge_mcast *brmctx = from_timer(brmctx, t, 3633d3d065c0SNikolay Aleksandrov ip6_own_query.timer); 3634cc0fdd80SLinus Lüssing 3635adc47037SNikolay Aleksandrov br_multicast_query_expired(brmctx, &brmctx->ip6_own_query, 3636d3d065c0SNikolay Aleksandrov &brmctx->ip6_querier); 3637cc0fdd80SLinus Lüssing } 3638cc0fdd80SLinus Lüssing #endif 3639cc0fdd80SLinus Lüssing 3640e12cec65SNikolay Aleksandrov static void br_multicast_gc_work(struct work_struct *work) 36418b671779SNikolay Aleksandrov { 36428b671779SNikolay Aleksandrov struct net_bridge *br = container_of(work, struct net_bridge, 3643e12cec65SNikolay Aleksandrov mcast_gc_work); 36448b671779SNikolay Aleksandrov HLIST_HEAD(deleted_head); 36458b671779SNikolay Aleksandrov 36468b671779SNikolay Aleksandrov spin_lock_bh(&br->multicast_lock); 3647e12cec65SNikolay Aleksandrov hlist_move_list(&br->mcast_gc_list, &deleted_head); 36488b671779SNikolay Aleksandrov spin_unlock_bh(&br->multicast_lock); 36498b671779SNikolay Aleksandrov 3650e12cec65SNikolay Aleksandrov br_multicast_gc(&deleted_head); 36518b671779SNikolay Aleksandrov } 36528b671779SNikolay Aleksandrov 3653613d61dbSNikolay Aleksandrov void br_multicast_ctx_init(struct net_bridge *br, 3654613d61dbSNikolay Aleksandrov struct net_bridge_vlan *vlan, 3655613d61dbSNikolay Aleksandrov struct net_bridge_mcast *brmctx) 3656613d61dbSNikolay Aleksandrov { 3657613d61dbSNikolay Aleksandrov brmctx->br = br; 3658613d61dbSNikolay Aleksandrov brmctx->vlan = vlan; 3659613d61dbSNikolay Aleksandrov brmctx->multicast_router = MDB_RTR_TYPE_TEMP_QUERY; 3660613d61dbSNikolay Aleksandrov brmctx->multicast_last_member_count = 2; 3661613d61dbSNikolay Aleksandrov brmctx->multicast_startup_query_count = 2; 3662613d61dbSNikolay Aleksandrov 3663613d61dbSNikolay Aleksandrov brmctx->multicast_last_member_interval = HZ; 3664613d61dbSNikolay Aleksandrov brmctx->multicast_query_response_interval = 10 * HZ; 3665613d61dbSNikolay Aleksandrov brmctx->multicast_startup_query_interval = 125 * HZ / 4; 3666613d61dbSNikolay Aleksandrov brmctx->multicast_query_interval = 125 * HZ; 3667613d61dbSNikolay Aleksandrov brmctx->multicast_querier_interval = 255 * HZ; 3668613d61dbSNikolay Aleksandrov brmctx->multicast_membership_interval = 260 * HZ; 3669613d61dbSNikolay Aleksandrov 3670613d61dbSNikolay Aleksandrov brmctx->ip4_other_query.delay_time = 0; 3671613d61dbSNikolay Aleksandrov brmctx->ip4_querier.port = NULL; 3672613d61dbSNikolay Aleksandrov brmctx->multicast_igmp_version = 2; 3673613d61dbSNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6) 3674613d61dbSNikolay Aleksandrov brmctx->multicast_mld_version = 1; 3675613d61dbSNikolay Aleksandrov brmctx->ip6_other_query.delay_time = 0; 3676613d61dbSNikolay Aleksandrov brmctx->ip6_querier.port = NULL; 3677613d61dbSNikolay Aleksandrov #endif 3678613d61dbSNikolay Aleksandrov 3679613d61dbSNikolay Aleksandrov timer_setup(&brmctx->ip4_mc_router_timer, 3680613d61dbSNikolay Aleksandrov br_ip4_multicast_local_router_expired, 0); 3681613d61dbSNikolay Aleksandrov timer_setup(&brmctx->ip4_other_query.timer, 3682613d61dbSNikolay Aleksandrov br_ip4_multicast_querier_expired, 0); 3683613d61dbSNikolay Aleksandrov timer_setup(&brmctx->ip4_own_query.timer, 3684613d61dbSNikolay Aleksandrov br_ip4_multicast_query_expired, 0); 3685613d61dbSNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6) 3686613d61dbSNikolay Aleksandrov timer_setup(&brmctx->ip6_mc_router_timer, 3687613d61dbSNikolay Aleksandrov br_ip6_multicast_local_router_expired, 0); 3688613d61dbSNikolay Aleksandrov timer_setup(&brmctx->ip6_other_query.timer, 3689613d61dbSNikolay Aleksandrov br_ip6_multicast_querier_expired, 0); 3690613d61dbSNikolay Aleksandrov timer_setup(&brmctx->ip6_own_query.timer, 3691613d61dbSNikolay Aleksandrov br_ip6_multicast_query_expired, 0); 3692613d61dbSNikolay Aleksandrov #endif 3693613d61dbSNikolay Aleksandrov } 3694613d61dbSNikolay Aleksandrov 3695613d61dbSNikolay Aleksandrov void br_multicast_ctx_deinit(struct net_bridge_mcast *brmctx) 3696613d61dbSNikolay Aleksandrov { 3697613d61dbSNikolay Aleksandrov __br_multicast_stop(brmctx); 3698613d61dbSNikolay Aleksandrov } 3699613d61dbSNikolay Aleksandrov 3700eb1d1641SHerbert Xu void br_multicast_init(struct net_bridge *br) 3701eb1d1641SHerbert Xu { 3702d08c6bc0SNikolay Aleksandrov br->hash_max = BR_MULTICAST_DEFAULT_HASH_MAX; 3703eb1d1641SHerbert Xu 3704613d61dbSNikolay Aleksandrov br_multicast_ctx_init(br, NULL, &br->multicast_ctx); 3705eb1d1641SHerbert Xu 37066919622aSIdo Schimmel br_opt_toggle(br, BROPT_MULTICAST_ENABLED, true); 3707675779adSNikolay Aleksandrov br_opt_toggle(br, BROPT_HAS_IPV6_ADDR, true); 3708b00589afSLinus Lüssing 3709eb1d1641SHerbert Xu spin_lock_init(&br->multicast_lock); 371019e3a9c9SNikolay Aleksandrov INIT_HLIST_HEAD(&br->mdb_list); 3711e12cec65SNikolay Aleksandrov INIT_HLIST_HEAD(&br->mcast_gc_list); 3712e12cec65SNikolay Aleksandrov INIT_WORK(&br->mcast_gc_work, br_multicast_gc_work); 3713eb1d1641SHerbert Xu } 3714eb1d1641SHerbert Xu 37154effd28cSLinus Lüssing static void br_ip4_multicast_join_snoopers(struct net_bridge *br) 37164effd28cSLinus Lüssing { 37174effd28cSLinus Lüssing struct in_device *in_dev = in_dev_get(br->dev); 37184effd28cSLinus Lüssing 37194effd28cSLinus Lüssing if (!in_dev) 37204effd28cSLinus Lüssing return; 37214effd28cSLinus Lüssing 37229fb20801SFlorian Fainelli __ip_mc_inc_group(in_dev, htonl(INADDR_ALLSNOOPERS_GROUP), GFP_ATOMIC); 37234effd28cSLinus Lüssing in_dev_put(in_dev); 37244effd28cSLinus Lüssing } 37254effd28cSLinus Lüssing 37264effd28cSLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 37274effd28cSLinus Lüssing static void br_ip6_multicast_join_snoopers(struct net_bridge *br) 37284effd28cSLinus Lüssing { 37294effd28cSLinus Lüssing struct in6_addr addr; 37304effd28cSLinus Lüssing 37314effd28cSLinus Lüssing ipv6_addr_set(&addr, htonl(0xff020000), 0, 0, htonl(0x6a)); 37324effd28cSLinus Lüssing ipv6_dev_mc_inc(br->dev, &addr); 37334effd28cSLinus Lüssing } 37344effd28cSLinus Lüssing #else 37354effd28cSLinus Lüssing static inline void br_ip6_multicast_join_snoopers(struct net_bridge *br) 37364effd28cSLinus Lüssing { 37374effd28cSLinus Lüssing } 37384effd28cSLinus Lüssing #endif 37394effd28cSLinus Lüssing 3740851d0a73SJoseph Huang void br_multicast_join_snoopers(struct net_bridge *br) 37414effd28cSLinus Lüssing { 37424effd28cSLinus Lüssing br_ip4_multicast_join_snoopers(br); 37434effd28cSLinus Lüssing br_ip6_multicast_join_snoopers(br); 37444effd28cSLinus Lüssing } 37454effd28cSLinus Lüssing 37464effd28cSLinus Lüssing static void br_ip4_multicast_leave_snoopers(struct net_bridge *br) 37474effd28cSLinus Lüssing { 37484effd28cSLinus Lüssing struct in_device *in_dev = in_dev_get(br->dev); 37494effd28cSLinus Lüssing 37504effd28cSLinus Lüssing if (WARN_ON(!in_dev)) 37514effd28cSLinus Lüssing return; 37524effd28cSLinus Lüssing 37539fb20801SFlorian Fainelli __ip_mc_dec_group(in_dev, htonl(INADDR_ALLSNOOPERS_GROUP), GFP_ATOMIC); 37544effd28cSLinus Lüssing in_dev_put(in_dev); 37554effd28cSLinus Lüssing } 37564effd28cSLinus Lüssing 37574effd28cSLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 37584effd28cSLinus Lüssing static void br_ip6_multicast_leave_snoopers(struct net_bridge *br) 37594effd28cSLinus Lüssing { 37604effd28cSLinus Lüssing struct in6_addr addr; 37614effd28cSLinus Lüssing 37624effd28cSLinus Lüssing ipv6_addr_set(&addr, htonl(0xff020000), 0, 0, htonl(0x6a)); 37634effd28cSLinus Lüssing ipv6_dev_mc_dec(br->dev, &addr); 37644effd28cSLinus Lüssing } 37654effd28cSLinus Lüssing #else 37664effd28cSLinus Lüssing static inline void br_ip6_multicast_leave_snoopers(struct net_bridge *br) 37674effd28cSLinus Lüssing { 37684effd28cSLinus Lüssing } 37694effd28cSLinus Lüssing #endif 37704effd28cSLinus Lüssing 3771851d0a73SJoseph Huang void br_multicast_leave_snoopers(struct net_bridge *br) 37724effd28cSLinus Lüssing { 37734effd28cSLinus Lüssing br_ip4_multicast_leave_snoopers(br); 37744effd28cSLinus Lüssing br_ip6_multicast_leave_snoopers(br); 37754effd28cSLinus Lüssing } 37764effd28cSLinus Lüssing 37777b54aaafSNikolay Aleksandrov static void __br_multicast_open_query(struct net_bridge *br, 377890010b36SLinus Lüssing struct bridge_mcast_own_query *query) 3779eb1d1641SHerbert Xu { 3780cc0fdd80SLinus Lüssing query->startup_sent = 0; 3781eb1d1641SHerbert Xu 378213cefad2SNikolay Aleksandrov if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) 3783eb1d1641SHerbert Xu return; 3784eb1d1641SHerbert Xu 3785cc0fdd80SLinus Lüssing mod_timer(&query->timer, jiffies); 3786cc0fdd80SLinus Lüssing } 3787cc0fdd80SLinus Lüssing 37887b54aaafSNikolay Aleksandrov static void __br_multicast_open(struct net_bridge_mcast *brmctx) 37897b54aaafSNikolay Aleksandrov { 37907b54aaafSNikolay Aleksandrov __br_multicast_open_query(brmctx->br, &brmctx->ip4_own_query); 37917b54aaafSNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6) 37927b54aaafSNikolay Aleksandrov __br_multicast_open_query(brmctx->br, &brmctx->ip6_own_query); 37937b54aaafSNikolay Aleksandrov #endif 37947b54aaafSNikolay Aleksandrov } 37957b54aaafSNikolay Aleksandrov 3796cc0fdd80SLinus Lüssing void br_multicast_open(struct net_bridge *br) 3797cc0fdd80SLinus Lüssing { 3798f4b7002aSNikolay Aleksandrov ASSERT_RTNL(); 3799f4b7002aSNikolay Aleksandrov 3800f4b7002aSNikolay Aleksandrov if (br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) { 38017b54aaafSNikolay Aleksandrov struct net_bridge_vlan_group *vg; 38027b54aaafSNikolay Aleksandrov struct net_bridge_vlan *vlan; 38037b54aaafSNikolay Aleksandrov 38047b54aaafSNikolay Aleksandrov vg = br_vlan_group(br); 38057b54aaafSNikolay Aleksandrov if (vg) { 38067b54aaafSNikolay Aleksandrov list_for_each_entry(vlan, &vg->vlan_list, vlist) { 38077b54aaafSNikolay Aleksandrov struct net_bridge_mcast *brmctx; 38087b54aaafSNikolay Aleksandrov 38097b54aaafSNikolay Aleksandrov brmctx = &vlan->br_mcast_ctx; 38107b54aaafSNikolay Aleksandrov if (br_vlan_is_brentry(vlan) && 38117b54aaafSNikolay Aleksandrov !br_multicast_ctx_vlan_disabled(brmctx)) 38127b54aaafSNikolay Aleksandrov __br_multicast_open(&vlan->br_mcast_ctx); 38137b54aaafSNikolay Aleksandrov } 38147b54aaafSNikolay Aleksandrov } 3815f4b7002aSNikolay Aleksandrov } 38167b54aaafSNikolay Aleksandrov 38177b54aaafSNikolay Aleksandrov __br_multicast_open(&br->multicast_ctx); 3818eb1d1641SHerbert Xu } 3819eb1d1641SHerbert Xu 3820613d61dbSNikolay Aleksandrov static void __br_multicast_stop(struct net_bridge_mcast *brmctx) 3821613d61dbSNikolay Aleksandrov { 3822613d61dbSNikolay Aleksandrov del_timer_sync(&brmctx->ip4_mc_router_timer); 3823613d61dbSNikolay Aleksandrov del_timer_sync(&brmctx->ip4_other_query.timer); 3824613d61dbSNikolay Aleksandrov del_timer_sync(&brmctx->ip4_own_query.timer); 3825613d61dbSNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6) 3826613d61dbSNikolay Aleksandrov del_timer_sync(&brmctx->ip6_mc_router_timer); 3827613d61dbSNikolay Aleksandrov del_timer_sync(&brmctx->ip6_other_query.timer); 3828613d61dbSNikolay Aleksandrov del_timer_sync(&brmctx->ip6_own_query.timer); 3829613d61dbSNikolay Aleksandrov #endif 3830613d61dbSNikolay Aleksandrov } 3831613d61dbSNikolay Aleksandrov 38327b54aaafSNikolay Aleksandrov void br_multicast_toggle_one_vlan(struct net_bridge_vlan *vlan, bool on) 38337b54aaafSNikolay Aleksandrov { 38347b54aaafSNikolay Aleksandrov struct net_bridge *br; 38357b54aaafSNikolay Aleksandrov 38367b54aaafSNikolay Aleksandrov /* it's okay to check for the flag without the multicast lock because it 38377b54aaafSNikolay Aleksandrov * can only change under RTNL -> multicast_lock, we need the latter to 38387b54aaafSNikolay Aleksandrov * sync with timers and packets 38397b54aaafSNikolay Aleksandrov */ 38407b54aaafSNikolay Aleksandrov if (on == !!(vlan->priv_flags & BR_VLFLAG_MCAST_ENABLED)) 38417b54aaafSNikolay Aleksandrov return; 38427b54aaafSNikolay Aleksandrov 38437b54aaafSNikolay Aleksandrov if (br_vlan_is_master(vlan)) { 38447b54aaafSNikolay Aleksandrov br = vlan->br; 38457b54aaafSNikolay Aleksandrov 38467b54aaafSNikolay Aleksandrov if (!br_vlan_is_brentry(vlan) || 38477b54aaafSNikolay Aleksandrov (on && 38487b54aaafSNikolay Aleksandrov br_multicast_ctx_vlan_global_disabled(&vlan->br_mcast_ctx))) 38497b54aaafSNikolay Aleksandrov return; 38507b54aaafSNikolay Aleksandrov 38517b54aaafSNikolay Aleksandrov spin_lock_bh(&br->multicast_lock); 38527b54aaafSNikolay Aleksandrov vlan->priv_flags ^= BR_VLFLAG_MCAST_ENABLED; 38537b54aaafSNikolay Aleksandrov spin_unlock_bh(&br->multicast_lock); 38547b54aaafSNikolay Aleksandrov 38557b54aaafSNikolay Aleksandrov if (on) 38567b54aaafSNikolay Aleksandrov __br_multicast_open(&vlan->br_mcast_ctx); 38577b54aaafSNikolay Aleksandrov else 38587b54aaafSNikolay Aleksandrov __br_multicast_stop(&vlan->br_mcast_ctx); 38597b54aaafSNikolay Aleksandrov } else { 38607b54aaafSNikolay Aleksandrov struct net_bridge_mcast *brmctx; 38617b54aaafSNikolay Aleksandrov 38627b54aaafSNikolay Aleksandrov brmctx = br_multicast_port_ctx_get_global(&vlan->port_mcast_ctx); 38637b54aaafSNikolay Aleksandrov if (on && br_multicast_ctx_vlan_global_disabled(brmctx)) 38647b54aaafSNikolay Aleksandrov return; 38657b54aaafSNikolay Aleksandrov 38667b54aaafSNikolay Aleksandrov br = vlan->port->br; 38677b54aaafSNikolay Aleksandrov spin_lock_bh(&br->multicast_lock); 38687b54aaafSNikolay Aleksandrov vlan->priv_flags ^= BR_VLFLAG_MCAST_ENABLED; 38697b54aaafSNikolay Aleksandrov if (on) 38707b54aaafSNikolay Aleksandrov __br_multicast_enable_port_ctx(&vlan->port_mcast_ctx); 38717b54aaafSNikolay Aleksandrov else 38727b54aaafSNikolay Aleksandrov __br_multicast_disable_port_ctx(&vlan->port_mcast_ctx); 38737b54aaafSNikolay Aleksandrov spin_unlock_bh(&br->multicast_lock); 38747b54aaafSNikolay Aleksandrov } 38757b54aaafSNikolay Aleksandrov } 38767b54aaafSNikolay Aleksandrov 3877f4b7002aSNikolay Aleksandrov void br_multicast_toggle_vlan(struct net_bridge_vlan *vlan, bool on) 3878f4b7002aSNikolay Aleksandrov { 3879f4b7002aSNikolay Aleksandrov struct net_bridge_port *p; 3880f4b7002aSNikolay Aleksandrov 3881f4b7002aSNikolay Aleksandrov if (WARN_ON_ONCE(!br_vlan_is_master(vlan))) 3882f4b7002aSNikolay Aleksandrov return; 3883f4b7002aSNikolay Aleksandrov 3884f4b7002aSNikolay Aleksandrov list_for_each_entry(p, &vlan->br->port_list, list) { 3885f4b7002aSNikolay Aleksandrov struct net_bridge_vlan *vport; 3886f4b7002aSNikolay Aleksandrov 3887f4b7002aSNikolay Aleksandrov vport = br_vlan_find(nbp_vlan_group(p), vlan->vid); 3888f4b7002aSNikolay Aleksandrov if (!vport) 3889f4b7002aSNikolay Aleksandrov continue; 3890f4b7002aSNikolay Aleksandrov br_multicast_toggle_one_vlan(vport, on); 3891f4b7002aSNikolay Aleksandrov } 3892f4b7002aSNikolay Aleksandrov } 3893f4b7002aSNikolay Aleksandrov 3894f4b7002aSNikolay Aleksandrov int br_multicast_toggle_vlan_snooping(struct net_bridge *br, bool on, 3895f4b7002aSNikolay Aleksandrov struct netlink_ext_ack *extack) 3896eb1d1641SHerbert Xu { 38977b54aaafSNikolay Aleksandrov struct net_bridge_vlan_group *vg; 38987b54aaafSNikolay Aleksandrov struct net_bridge_vlan *vlan; 3899f4b7002aSNikolay Aleksandrov struct net_bridge_port *p; 39007b54aaafSNikolay Aleksandrov 3901f4b7002aSNikolay Aleksandrov if (br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED) == on) 3902f4b7002aSNikolay Aleksandrov return 0; 3903f4b7002aSNikolay Aleksandrov 3904f4b7002aSNikolay Aleksandrov if (on && !br_opt_get(br, BROPT_VLAN_ENABLED)) { 3905f4b7002aSNikolay Aleksandrov NL_SET_ERR_MSG_MOD(extack, "Cannot enable multicast vlan snooping with vlan filtering disabled"); 3906f4b7002aSNikolay Aleksandrov return -EINVAL; 3907f4b7002aSNikolay Aleksandrov } 3908f4b7002aSNikolay Aleksandrov 3909f4b7002aSNikolay Aleksandrov vg = br_vlan_group(br); 3910f4b7002aSNikolay Aleksandrov if (!vg) 3911f4b7002aSNikolay Aleksandrov return 0; 3912f4b7002aSNikolay Aleksandrov 3913f4b7002aSNikolay Aleksandrov br_opt_toggle(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED, on); 3914f4b7002aSNikolay Aleksandrov 3915f4b7002aSNikolay Aleksandrov /* disable/enable non-vlan mcast contexts based on vlan snooping */ 3916f4b7002aSNikolay Aleksandrov if (on) 3917f4b7002aSNikolay Aleksandrov __br_multicast_stop(&br->multicast_ctx); 3918f4b7002aSNikolay Aleksandrov else 3919f4b7002aSNikolay Aleksandrov __br_multicast_open(&br->multicast_ctx); 3920f4b7002aSNikolay Aleksandrov list_for_each_entry(p, &br->port_list, list) { 3921f4b7002aSNikolay Aleksandrov if (on) 3922f4b7002aSNikolay Aleksandrov br_multicast_disable_port(p); 3923f4b7002aSNikolay Aleksandrov else 3924f4b7002aSNikolay Aleksandrov br_multicast_enable_port(p); 3925f4b7002aSNikolay Aleksandrov } 3926f4b7002aSNikolay Aleksandrov 3927f4b7002aSNikolay Aleksandrov list_for_each_entry(vlan, &vg->vlan_list, vlist) 3928f4b7002aSNikolay Aleksandrov br_multicast_toggle_vlan(vlan, on); 3929f4b7002aSNikolay Aleksandrov 3930f4b7002aSNikolay Aleksandrov return 0; 3931f4b7002aSNikolay Aleksandrov } 3932f4b7002aSNikolay Aleksandrov 3933f4b7002aSNikolay Aleksandrov void br_multicast_stop(struct net_bridge *br) 3934f4b7002aSNikolay Aleksandrov { 39357b54aaafSNikolay Aleksandrov ASSERT_RTNL(); 39367b54aaafSNikolay Aleksandrov 3937f4b7002aSNikolay Aleksandrov if (br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) { 3938f4b7002aSNikolay Aleksandrov struct net_bridge_vlan_group *vg; 3939f4b7002aSNikolay Aleksandrov struct net_bridge_vlan *vlan; 3940f4b7002aSNikolay Aleksandrov 39417b54aaafSNikolay Aleksandrov vg = br_vlan_group(br); 39427b54aaafSNikolay Aleksandrov if (vg) { 39437b54aaafSNikolay Aleksandrov list_for_each_entry(vlan, &vg->vlan_list, vlist) { 39447b54aaafSNikolay Aleksandrov struct net_bridge_mcast *brmctx; 39457b54aaafSNikolay Aleksandrov 39467b54aaafSNikolay Aleksandrov brmctx = &vlan->br_mcast_ctx; 39477b54aaafSNikolay Aleksandrov if (br_vlan_is_brentry(vlan) && 39487b54aaafSNikolay Aleksandrov !br_multicast_ctx_vlan_disabled(brmctx)) 39497b54aaafSNikolay Aleksandrov __br_multicast_stop(&vlan->br_mcast_ctx); 39507b54aaafSNikolay Aleksandrov } 39517b54aaafSNikolay Aleksandrov } 3952f4b7002aSNikolay Aleksandrov } 39537b54aaafSNikolay Aleksandrov 3954613d61dbSNikolay Aleksandrov __br_multicast_stop(&br->multicast_ctx); 3955e10177abSSatish Ashok } 3956e10177abSSatish Ashok 3957e10177abSSatish Ashok void br_multicast_dev_del(struct net_bridge *br) 3958e10177abSSatish Ashok { 3959e10177abSSatish Ashok struct net_bridge_mdb_entry *mp; 39608b671779SNikolay Aleksandrov HLIST_HEAD(deleted_head); 396119e3a9c9SNikolay Aleksandrov struct hlist_node *tmp; 3962eb1d1641SHerbert Xu 3963eb1d1641SHerbert Xu spin_lock_bh(&br->multicast_lock); 3964e12cec65SNikolay Aleksandrov hlist_for_each_entry_safe(mp, tmp, &br->mdb_list, mdb_node) 3965e12cec65SNikolay Aleksandrov br_multicast_del_mdb_entry(mp); 3966e12cec65SNikolay Aleksandrov hlist_move_list(&br->mcast_gc_list, &deleted_head); 3967eb1d1641SHerbert Xu spin_unlock_bh(&br->multicast_lock); 396819e3a9c9SNikolay Aleksandrov 3969613d61dbSNikolay Aleksandrov br_multicast_ctx_deinit(&br->multicast_ctx); 3970e12cec65SNikolay Aleksandrov br_multicast_gc(&deleted_head); 3971e12cec65SNikolay Aleksandrov cancel_work_sync(&br->mcast_gc_work); 39728b671779SNikolay Aleksandrov 39734329596cSNikolay Aleksandrov rcu_barrier(); 3974eb1d1641SHerbert Xu } 39750909e117SHerbert Xu 39760909e117SHerbert Xu int br_multicast_set_router(struct net_bridge *br, unsigned long val) 39770909e117SHerbert Xu { 3978d3d065c0SNikolay Aleksandrov struct net_bridge_mcast *brmctx = &br->multicast_ctx; 39796ae4ae8eSLinus Lüssing int err = -EINVAL; 39800909e117SHerbert Xu 39810909e117SHerbert Xu spin_lock_bh(&br->multicast_lock); 39820909e117SHerbert Xu 39830909e117SHerbert Xu switch (val) { 39847f0aec7aSNikolay Aleksandrov case MDB_RTR_TYPE_DISABLED: 39857f0aec7aSNikolay Aleksandrov case MDB_RTR_TYPE_PERM: 398677041420SYotam Gigi br_mc_router_state_change(br, val == MDB_RTR_TYPE_PERM); 3987d3d065c0SNikolay Aleksandrov del_timer(&brmctx->ip4_mc_router_timer); 3988a3c02e76SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 3989d3d065c0SNikolay Aleksandrov del_timer(&brmctx->ip6_mc_router_timer); 3990a3c02e76SLinus Lüssing #endif 3991d3d065c0SNikolay Aleksandrov brmctx->multicast_router = val; 399277041420SYotam Gigi err = 0; 399377041420SYotam Gigi break; 39947f0aec7aSNikolay Aleksandrov case MDB_RTR_TYPE_TEMP_QUERY: 3995d3d065c0SNikolay Aleksandrov if (brmctx->multicast_router != MDB_RTR_TYPE_TEMP_QUERY) 399677041420SYotam Gigi br_mc_router_state_change(br, false); 3997d3d065c0SNikolay Aleksandrov brmctx->multicast_router = val; 39980909e117SHerbert Xu err = 0; 39990909e117SHerbert Xu break; 40000909e117SHerbert Xu } 40010909e117SHerbert Xu 40020909e117SHerbert Xu spin_unlock_bh(&br->multicast_lock); 40030909e117SHerbert Xu 40040909e117SHerbert Xu return err; 40050909e117SHerbert Xu } 40060909e117SHerbert Xu 4007ed2d3597SLinus Lüssing static void 4008adc47037SNikolay Aleksandrov br_multicast_rport_del_notify(struct net_bridge_mcast_port *pmctx, bool deleted) 40097f0aec7aSNikolay Aleksandrov { 4010ed2d3597SLinus Lüssing if (!deleted) 40117f0aec7aSNikolay Aleksandrov return; 4012ed2d3597SLinus Lüssing 4013a3c02e76SLinus Lüssing /* For backwards compatibility for now, only notify if there is 4014a3c02e76SLinus Lüssing * no multicast router anymore for both IPv4 and IPv6. 4015a3c02e76SLinus Lüssing */ 4016adc47037SNikolay Aleksandrov if (!hlist_unhashed(&pmctx->ip4_rlist)) 4017a3c02e76SLinus Lüssing return; 4018a3c02e76SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 4019adc47037SNikolay Aleksandrov if (!hlist_unhashed(&pmctx->ip6_rlist)) 4020a3c02e76SLinus Lüssing return; 4021a3c02e76SLinus Lüssing #endif 4022a3c02e76SLinus Lüssing 4023adc47037SNikolay Aleksandrov br_rtr_notify(pmctx->port->br->dev, pmctx->port, RTM_DELMDB); 4024adc47037SNikolay Aleksandrov br_port_mc_router_state_change(pmctx->port, false); 4025f12e7d95SNogah Frankel 4026f12e7d95SNogah Frankel /* don't allow timer refresh */ 4027adc47037SNikolay Aleksandrov if (pmctx->multicast_router == MDB_RTR_TYPE_TEMP) 4028adc47037SNikolay Aleksandrov pmctx->multicast_router = MDB_RTR_TYPE_TEMP_QUERY; 40297f0aec7aSNikolay Aleksandrov } 40307f0aec7aSNikolay Aleksandrov 40310909e117SHerbert Xu int br_multicast_set_port_router(struct net_bridge_port *p, unsigned long val) 40320909e117SHerbert Xu { 4033d3d065c0SNikolay Aleksandrov struct net_bridge_mcast *brmctx = &p->br->multicast_ctx; 4034adc47037SNikolay Aleksandrov struct net_bridge_mcast_port *pmctx = &p->multicast_ctx; 4035a55d8246SNikolay Aleksandrov unsigned long now = jiffies; 40366ae4ae8eSLinus Lüssing int err = -EINVAL; 4037ed2d3597SLinus Lüssing bool del = false; 40380909e117SHerbert Xu 4039d3d065c0SNikolay Aleksandrov spin_lock(&p->br->multicast_lock); 4040adc47037SNikolay Aleksandrov if (pmctx->multicast_router == val) { 4041a55d8246SNikolay Aleksandrov /* Refresh the temp router port timer */ 4042adc47037SNikolay Aleksandrov if (pmctx->multicast_router == MDB_RTR_TYPE_TEMP) { 4043adc47037SNikolay Aleksandrov mod_timer(&pmctx->ip4_mc_router_timer, 4044d3d065c0SNikolay Aleksandrov now + brmctx->multicast_querier_interval); 4045a3c02e76SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 4046adc47037SNikolay Aleksandrov mod_timer(&pmctx->ip6_mc_router_timer, 4047d3d065c0SNikolay Aleksandrov now + brmctx->multicast_querier_interval); 4048a3c02e76SLinus Lüssing #endif 4049a3c02e76SLinus Lüssing } 40504950cfd1SNikolay Aleksandrov err = 0; 40514950cfd1SNikolay Aleksandrov goto unlock; 40524950cfd1SNikolay Aleksandrov } 40530909e117SHerbert Xu switch (val) { 40547f0aec7aSNikolay Aleksandrov case MDB_RTR_TYPE_DISABLED: 4055adc47037SNikolay Aleksandrov pmctx->multicast_router = MDB_RTR_TYPE_DISABLED; 4056adc47037SNikolay Aleksandrov del |= br_ip4_multicast_rport_del(pmctx); 4057adc47037SNikolay Aleksandrov del_timer(&pmctx->ip4_mc_router_timer); 4058adc47037SNikolay Aleksandrov del |= br_ip6_multicast_rport_del(pmctx); 4059a3c02e76SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 4060adc47037SNikolay Aleksandrov del_timer(&pmctx->ip6_mc_router_timer); 4061a3c02e76SLinus Lüssing #endif 4062adc47037SNikolay Aleksandrov br_multicast_rport_del_notify(pmctx, del); 40630909e117SHerbert Xu break; 40647f0aec7aSNikolay Aleksandrov case MDB_RTR_TYPE_TEMP_QUERY: 4065adc47037SNikolay Aleksandrov pmctx->multicast_router = MDB_RTR_TYPE_TEMP_QUERY; 4066adc47037SNikolay Aleksandrov del |= br_ip4_multicast_rport_del(pmctx); 4067adc47037SNikolay Aleksandrov del |= br_ip6_multicast_rport_del(pmctx); 4068adc47037SNikolay Aleksandrov br_multicast_rport_del_notify(pmctx, del); 40697f0aec7aSNikolay Aleksandrov break; 40707f0aec7aSNikolay Aleksandrov case MDB_RTR_TYPE_PERM: 4071adc47037SNikolay Aleksandrov pmctx->multicast_router = MDB_RTR_TYPE_PERM; 4072adc47037SNikolay Aleksandrov del_timer(&pmctx->ip4_mc_router_timer); 4073adc47037SNikolay Aleksandrov br_ip4_multicast_add_router(brmctx, pmctx); 4074a3c02e76SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 4075adc47037SNikolay Aleksandrov del_timer(&pmctx->ip6_mc_router_timer); 4076a3c02e76SLinus Lüssing #endif 4077adc47037SNikolay Aleksandrov br_ip6_multicast_add_router(brmctx, pmctx); 40780909e117SHerbert Xu break; 4079a55d8246SNikolay Aleksandrov case MDB_RTR_TYPE_TEMP: 4080adc47037SNikolay Aleksandrov pmctx->multicast_router = MDB_RTR_TYPE_TEMP; 4081adc47037SNikolay Aleksandrov br_ip4_multicast_mark_router(brmctx, pmctx); 4082adc47037SNikolay Aleksandrov br_ip6_multicast_mark_router(brmctx, pmctx); 4083a55d8246SNikolay Aleksandrov break; 40847f0aec7aSNikolay Aleksandrov default: 40857f0aec7aSNikolay Aleksandrov goto unlock; 40860909e117SHerbert Xu } 40877f0aec7aSNikolay Aleksandrov err = 0; 40887f0aec7aSNikolay Aleksandrov unlock: 4089d3d065c0SNikolay Aleksandrov spin_unlock(&p->br->multicast_lock); 40900909e117SHerbert Xu 40910909e117SHerbert Xu return err; 40920909e117SHerbert Xu } 4093561f1103SHerbert Xu 4094adc47037SNikolay Aleksandrov static void br_multicast_start_querier(struct net_bridge_mcast *brmctx, 409590010b36SLinus Lüssing struct bridge_mcast_own_query *query) 4096561f1103SHerbert Xu { 4097561f1103SHerbert Xu struct net_bridge_port *port; 409874857216SHerbert Xu 40997b54aaafSNikolay Aleksandrov __br_multicast_open_query(brmctx->br, query); 410074857216SHerbert Xu 4101c5b493ceSNikolay Aleksandrov rcu_read_lock(); 4102adc47037SNikolay Aleksandrov list_for_each_entry_rcu(port, &brmctx->br->port_list, list) { 410374857216SHerbert Xu if (port->state == BR_STATE_DISABLED || 410474857216SHerbert Xu port->state == BR_STATE_BLOCKING) 410574857216SHerbert Xu continue; 410674857216SHerbert Xu 4107adc47037SNikolay Aleksandrov if (query == &brmctx->ip4_own_query) 41089632233eSNikolay Aleksandrov br_multicast_enable(&port->multicast_ctx.ip4_own_query); 4109cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 4110cc0fdd80SLinus Lüssing else 41119632233eSNikolay Aleksandrov br_multicast_enable(&port->multicast_ctx.ip6_own_query); 4112cc0fdd80SLinus Lüssing #endif 411374857216SHerbert Xu } 4114c5b493ceSNikolay Aleksandrov rcu_read_unlock(); 411574857216SHerbert Xu } 411674857216SHerbert Xu 4117ae1ea84bSFlorian Fainelli int br_multicast_toggle(struct net_bridge *br, unsigned long val, 4118ae1ea84bSFlorian Fainelli struct netlink_ext_ack *extack) 411974857216SHerbert Xu { 41207cb3f921SNikolay Aleksandrov struct net_bridge_port *port; 4121851d0a73SJoseph Huang bool change_snoopers = false; 4122ae1ea84bSFlorian Fainelli int err = 0; 4123561f1103SHerbert Xu 4124ef5e0d82SAndrey Vagin spin_lock_bh(&br->multicast_lock); 412513cefad2SNikolay Aleksandrov if (!!br_opt_get(br, BROPT_MULTICAST_ENABLED) == !!val) 4126561f1103SHerbert Xu goto unlock; 4127561f1103SHerbert Xu 4128ae1ea84bSFlorian Fainelli err = br_mc_disabled_update(br->dev, val, extack); 4129ae1ea84bSFlorian Fainelli if (err == -EOPNOTSUPP) 4130ae1ea84bSFlorian Fainelli err = 0; 4131ae1ea84bSFlorian Fainelli if (err) 4132ae1ea84bSFlorian Fainelli goto unlock; 4133ae1ea84bSFlorian Fainelli 413413cefad2SNikolay Aleksandrov br_opt_toggle(br, BROPT_MULTICAST_ENABLED, !!val); 41354effd28cSLinus Lüssing if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) { 4136851d0a73SJoseph Huang change_snoopers = true; 4137561f1103SHerbert Xu goto unlock; 41384effd28cSLinus Lüssing } 4139561f1103SHerbert Xu 41403a7fda06SHerbert Xu if (!netif_running(br->dev)) 41413a7fda06SHerbert Xu goto unlock; 41423a7fda06SHerbert Xu 41437cb3f921SNikolay Aleksandrov br_multicast_open(br); 41447cb3f921SNikolay Aleksandrov list_for_each_entry(port, &br->port_list, list) 4145adc47037SNikolay Aleksandrov __br_multicast_enable_port_ctx(&port->multicast_ctx); 4146561f1103SHerbert Xu 4147851d0a73SJoseph Huang change_snoopers = true; 4148851d0a73SJoseph Huang 4149561f1103SHerbert Xu unlock: 4150ef5e0d82SAndrey Vagin spin_unlock_bh(&br->multicast_lock); 4151561f1103SHerbert Xu 4152851d0a73SJoseph Huang /* br_multicast_join_snoopers has the potential to cause 4153851d0a73SJoseph Huang * an MLD Report/Leave to be delivered to br_multicast_rcv, 4154851d0a73SJoseph Huang * which would in turn call br_multicast_add_group, which would 4155851d0a73SJoseph Huang * attempt to acquire multicast_lock. This function should be 4156851d0a73SJoseph Huang * called after the lock has been released to avoid deadlocks on 4157851d0a73SJoseph Huang * multicast_lock. 4158851d0a73SJoseph Huang * 4159851d0a73SJoseph Huang * br_multicast_leave_snoopers does not have the problem since 4160851d0a73SJoseph Huang * br_multicast_rcv first checks BROPT_MULTICAST_ENABLED, and 4161851d0a73SJoseph Huang * returns without calling br_multicast_ipv4/6_rcv if it's not 4162851d0a73SJoseph Huang * enabled. Moved both functions out just for symmetry. 4163851d0a73SJoseph Huang */ 4164851d0a73SJoseph Huang if (change_snoopers) { 4165851d0a73SJoseph Huang if (br_opt_get(br, BROPT_MULTICAST_ENABLED)) 4166851d0a73SJoseph Huang br_multicast_join_snoopers(br); 4167851d0a73SJoseph Huang else 4168851d0a73SJoseph Huang br_multicast_leave_snoopers(br); 4169851d0a73SJoseph Huang } 4170851d0a73SJoseph Huang 4171ae1ea84bSFlorian Fainelli return err; 4172561f1103SHerbert Xu } 4173b195167fSHerbert Xu 41749341b988SIdo Schimmel bool br_multicast_enabled(const struct net_device *dev) 41759341b988SIdo Schimmel { 41769341b988SIdo Schimmel struct net_bridge *br = netdev_priv(dev); 41779341b988SIdo Schimmel 417813cefad2SNikolay Aleksandrov return !!br_opt_get(br, BROPT_MULTICAST_ENABLED); 41799341b988SIdo Schimmel } 41809341b988SIdo Schimmel EXPORT_SYMBOL_GPL(br_multicast_enabled); 41819341b988SIdo Schimmel 41820912bda4SYotam Gigi bool br_multicast_router(const struct net_device *dev) 41830912bda4SYotam Gigi { 41840912bda4SYotam Gigi struct net_bridge *br = netdev_priv(dev); 41850912bda4SYotam Gigi bool is_router; 41860912bda4SYotam Gigi 41870912bda4SYotam Gigi spin_lock_bh(&br->multicast_lock); 4188adc47037SNikolay Aleksandrov is_router = br_multicast_is_router(&br->multicast_ctx, NULL); 41890912bda4SYotam Gigi spin_unlock_bh(&br->multicast_lock); 41900912bda4SYotam Gigi return is_router; 41910912bda4SYotam Gigi } 41920912bda4SYotam Gigi EXPORT_SYMBOL_GPL(br_multicast_router); 41930912bda4SYotam Gigi 4194c5c23260SHerbert Xu int br_multicast_set_querier(struct net_bridge *br, unsigned long val) 4195c5c23260SHerbert Xu { 4196d3d065c0SNikolay Aleksandrov struct net_bridge_mcast *brmctx = &br->multicast_ctx; 4197b00589afSLinus Lüssing unsigned long max_delay; 4198b00589afSLinus Lüssing 4199c5c23260SHerbert Xu val = !!val; 4200c5c23260SHerbert Xu 4201c5c23260SHerbert Xu spin_lock_bh(&br->multicast_lock); 4202675779adSNikolay Aleksandrov if (br_opt_get(br, BROPT_MULTICAST_QUERIER) == val) 4203c5c23260SHerbert Xu goto unlock; 4204c5c23260SHerbert Xu 4205675779adSNikolay Aleksandrov br_opt_toggle(br, BROPT_MULTICAST_QUERIER, !!val); 4206b00589afSLinus Lüssing if (!val) 4207b00589afSLinus Lüssing goto unlock; 4208b00589afSLinus Lüssing 4209d3d065c0SNikolay Aleksandrov max_delay = brmctx->multicast_query_response_interval; 4210b00589afSLinus Lüssing 4211d3d065c0SNikolay Aleksandrov if (!timer_pending(&brmctx->ip4_other_query.timer)) 4212d3d065c0SNikolay Aleksandrov brmctx->ip4_other_query.delay_time = jiffies + max_delay; 4213cc0fdd80SLinus Lüssing 4214adc47037SNikolay Aleksandrov br_multicast_start_querier(brmctx, &brmctx->ip4_own_query); 4215cc0fdd80SLinus Lüssing 4216cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 4217d3d065c0SNikolay Aleksandrov if (!timer_pending(&brmctx->ip6_other_query.timer)) 4218d3d065c0SNikolay Aleksandrov brmctx->ip6_other_query.delay_time = jiffies + max_delay; 4219cc0fdd80SLinus Lüssing 4220adc47037SNikolay Aleksandrov br_multicast_start_querier(brmctx, &brmctx->ip6_own_query); 4221cc0fdd80SLinus Lüssing #endif 4222c5c23260SHerbert Xu 4223c5c23260SHerbert Xu unlock: 4224c5c23260SHerbert Xu spin_unlock_bh(&br->multicast_lock); 4225c5c23260SHerbert Xu 4226c5c23260SHerbert Xu return 0; 4227c5c23260SHerbert Xu } 4228c5c23260SHerbert Xu 42295e923585SNikolay Aleksandrov int br_multicast_set_igmp_version(struct net_bridge *br, unsigned long val) 42305e923585SNikolay Aleksandrov { 42315e923585SNikolay Aleksandrov /* Currently we support only version 2 and 3 */ 42325e923585SNikolay Aleksandrov switch (val) { 42335e923585SNikolay Aleksandrov case 2: 42345e923585SNikolay Aleksandrov case 3: 42355e923585SNikolay Aleksandrov break; 42365e923585SNikolay Aleksandrov default: 42375e923585SNikolay Aleksandrov return -EINVAL; 42385e923585SNikolay Aleksandrov } 42395e923585SNikolay Aleksandrov 42405e923585SNikolay Aleksandrov spin_lock_bh(&br->multicast_lock); 4241d3d065c0SNikolay Aleksandrov br->multicast_ctx.multicast_igmp_version = val; 42425e923585SNikolay Aleksandrov spin_unlock_bh(&br->multicast_lock); 42435e923585SNikolay Aleksandrov 42445e923585SNikolay Aleksandrov return 0; 42455e923585SNikolay Aleksandrov } 42465e923585SNikolay Aleksandrov 4247aa2ae3e7SNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6) 4248aa2ae3e7SNikolay Aleksandrov int br_multicast_set_mld_version(struct net_bridge *br, unsigned long val) 4249aa2ae3e7SNikolay Aleksandrov { 4250aa2ae3e7SNikolay Aleksandrov /* Currently we support version 1 and 2 */ 4251aa2ae3e7SNikolay Aleksandrov switch (val) { 4252aa2ae3e7SNikolay Aleksandrov case 1: 4253aa2ae3e7SNikolay Aleksandrov case 2: 4254aa2ae3e7SNikolay Aleksandrov break; 4255aa2ae3e7SNikolay Aleksandrov default: 4256aa2ae3e7SNikolay Aleksandrov return -EINVAL; 4257aa2ae3e7SNikolay Aleksandrov } 4258aa2ae3e7SNikolay Aleksandrov 4259aa2ae3e7SNikolay Aleksandrov spin_lock_bh(&br->multicast_lock); 4260d3d065c0SNikolay Aleksandrov br->multicast_ctx.multicast_mld_version = val; 4261aa2ae3e7SNikolay Aleksandrov spin_unlock_bh(&br->multicast_lock); 4262aa2ae3e7SNikolay Aleksandrov 4263aa2ae3e7SNikolay Aleksandrov return 0; 4264aa2ae3e7SNikolay Aleksandrov } 4265aa2ae3e7SNikolay Aleksandrov #endif 4266aa2ae3e7SNikolay Aleksandrov 426707f8ac4aSLinus Lüssing /** 426807f8ac4aSLinus Lüssing * br_multicast_list_adjacent - Returns snooped multicast addresses 426907f8ac4aSLinus Lüssing * @dev: The bridge port adjacent to which to retrieve addresses 427007f8ac4aSLinus Lüssing * @br_ip_list: The list to store found, snooped multicast IP addresses in 427107f8ac4aSLinus Lüssing * 427207f8ac4aSLinus Lüssing * Creates a list of IP addresses (struct br_ip_list) sensed by the multicast 427307f8ac4aSLinus Lüssing * snooping feature on all bridge ports of dev's bridge device, excluding 427407f8ac4aSLinus Lüssing * the addresses from dev itself. 427507f8ac4aSLinus Lüssing * 427607f8ac4aSLinus Lüssing * Returns the number of items added to br_ip_list. 427707f8ac4aSLinus Lüssing * 427807f8ac4aSLinus Lüssing * Notes: 427907f8ac4aSLinus Lüssing * - br_ip_list needs to be initialized by caller 428007f8ac4aSLinus Lüssing * - br_ip_list might contain duplicates in the end 428107f8ac4aSLinus Lüssing * (needs to be taken care of by caller) 428207f8ac4aSLinus Lüssing * - br_ip_list needs to be freed by caller 428307f8ac4aSLinus Lüssing */ 428407f8ac4aSLinus Lüssing int br_multicast_list_adjacent(struct net_device *dev, 428507f8ac4aSLinus Lüssing struct list_head *br_ip_list) 428607f8ac4aSLinus Lüssing { 428707f8ac4aSLinus Lüssing struct net_bridge *br; 428807f8ac4aSLinus Lüssing struct net_bridge_port *port; 428907f8ac4aSLinus Lüssing struct net_bridge_port_group *group; 429007f8ac4aSLinus Lüssing struct br_ip_list *entry; 429107f8ac4aSLinus Lüssing int count = 0; 429207f8ac4aSLinus Lüssing 429307f8ac4aSLinus Lüssing rcu_read_lock(); 429435f861e3SJulian Wiedmann if (!br_ip_list || !netif_is_bridge_port(dev)) 429507f8ac4aSLinus Lüssing goto unlock; 429607f8ac4aSLinus Lüssing 429707f8ac4aSLinus Lüssing port = br_port_get_rcu(dev); 429807f8ac4aSLinus Lüssing if (!port || !port->br) 429907f8ac4aSLinus Lüssing goto unlock; 430007f8ac4aSLinus Lüssing 430107f8ac4aSLinus Lüssing br = port->br; 430207f8ac4aSLinus Lüssing 430307f8ac4aSLinus Lüssing list_for_each_entry_rcu(port, &br->port_list, list) { 430407f8ac4aSLinus Lüssing if (!port->dev || port->dev == dev) 430507f8ac4aSLinus Lüssing continue; 430607f8ac4aSLinus Lüssing 430707f8ac4aSLinus Lüssing hlist_for_each_entry_rcu(group, &port->mglist, mglist) { 430807f8ac4aSLinus Lüssing entry = kmalloc(sizeof(*entry), GFP_ATOMIC); 430907f8ac4aSLinus Lüssing if (!entry) 431007f8ac4aSLinus Lüssing goto unlock; 431107f8ac4aSLinus Lüssing 4312085b53c8SNikolay Aleksandrov entry->addr = group->key.addr; 431307f8ac4aSLinus Lüssing list_add(&entry->list, br_ip_list); 431407f8ac4aSLinus Lüssing count++; 431507f8ac4aSLinus Lüssing } 431607f8ac4aSLinus Lüssing } 431707f8ac4aSLinus Lüssing 431807f8ac4aSLinus Lüssing unlock: 431907f8ac4aSLinus Lüssing rcu_read_unlock(); 432007f8ac4aSLinus Lüssing return count; 432107f8ac4aSLinus Lüssing } 432207f8ac4aSLinus Lüssing EXPORT_SYMBOL_GPL(br_multicast_list_adjacent); 43232cd41431SLinus Lüssing 43242cd41431SLinus Lüssing /** 4325c34963e2SLinus Lüssing * br_multicast_has_querier_anywhere - Checks for a querier on a bridge 4326c34963e2SLinus Lüssing * @dev: The bridge port providing the bridge on which to check for a querier 4327c34963e2SLinus Lüssing * @proto: The protocol family to check for: IGMP -> ETH_P_IP, MLD -> ETH_P_IPV6 4328c34963e2SLinus Lüssing * 4329c34963e2SLinus Lüssing * Checks whether the given interface has a bridge on top and if so returns 4330c34963e2SLinus Lüssing * true if a valid querier exists anywhere on the bridged link layer. 4331c34963e2SLinus Lüssing * Otherwise returns false. 4332c34963e2SLinus Lüssing */ 4333c34963e2SLinus Lüssing bool br_multicast_has_querier_anywhere(struct net_device *dev, int proto) 4334c34963e2SLinus Lüssing { 4335c34963e2SLinus Lüssing struct net_bridge *br; 4336c34963e2SLinus Lüssing struct net_bridge_port *port; 4337c34963e2SLinus Lüssing struct ethhdr eth; 4338c34963e2SLinus Lüssing bool ret = false; 4339c34963e2SLinus Lüssing 4340c34963e2SLinus Lüssing rcu_read_lock(); 434135f861e3SJulian Wiedmann if (!netif_is_bridge_port(dev)) 4342c34963e2SLinus Lüssing goto unlock; 4343c34963e2SLinus Lüssing 4344c34963e2SLinus Lüssing port = br_port_get_rcu(dev); 4345c34963e2SLinus Lüssing if (!port || !port->br) 4346c34963e2SLinus Lüssing goto unlock; 4347c34963e2SLinus Lüssing 4348c34963e2SLinus Lüssing br = port->br; 4349c34963e2SLinus Lüssing 4350c34963e2SLinus Lüssing memset(ð, 0, sizeof(eth)); 4351c34963e2SLinus Lüssing eth.h_proto = htons(proto); 4352c34963e2SLinus Lüssing 4353adc47037SNikolay Aleksandrov ret = br_multicast_querier_exists(&br->multicast_ctx, ð, NULL); 4354c34963e2SLinus Lüssing 4355c34963e2SLinus Lüssing unlock: 4356c34963e2SLinus Lüssing rcu_read_unlock(); 4357c34963e2SLinus Lüssing return ret; 4358c34963e2SLinus Lüssing } 4359c34963e2SLinus Lüssing EXPORT_SYMBOL_GPL(br_multicast_has_querier_anywhere); 4360c34963e2SLinus Lüssing 4361c34963e2SLinus Lüssing /** 43622cd41431SLinus Lüssing * br_multicast_has_querier_adjacent - Checks for a querier behind a bridge port 43632cd41431SLinus Lüssing * @dev: The bridge port adjacent to which to check for a querier 43642cd41431SLinus Lüssing * @proto: The protocol family to check for: IGMP -> ETH_P_IP, MLD -> ETH_P_IPV6 43652cd41431SLinus Lüssing * 43662cd41431SLinus Lüssing * Checks whether the given interface has a bridge on top and if so returns 43672cd41431SLinus Lüssing * true if a selected querier is behind one of the other ports of this 43682cd41431SLinus Lüssing * bridge. Otherwise returns false. 43692cd41431SLinus Lüssing */ 43702cd41431SLinus Lüssing bool br_multicast_has_querier_adjacent(struct net_device *dev, int proto) 43712cd41431SLinus Lüssing { 4372d3d065c0SNikolay Aleksandrov struct net_bridge_mcast *brmctx; 43732cd41431SLinus Lüssing struct net_bridge *br; 43742cd41431SLinus Lüssing struct net_bridge_port *port; 43752cd41431SLinus Lüssing bool ret = false; 43762cd41431SLinus Lüssing 43772cd41431SLinus Lüssing rcu_read_lock(); 437835f861e3SJulian Wiedmann if (!netif_is_bridge_port(dev)) 43792cd41431SLinus Lüssing goto unlock; 43802cd41431SLinus Lüssing 43812cd41431SLinus Lüssing port = br_port_get_rcu(dev); 43822cd41431SLinus Lüssing if (!port || !port->br) 43832cd41431SLinus Lüssing goto unlock; 43842cd41431SLinus Lüssing 43852cd41431SLinus Lüssing br = port->br; 4386d3d065c0SNikolay Aleksandrov brmctx = &br->multicast_ctx; 43872cd41431SLinus Lüssing 43882cd41431SLinus Lüssing switch (proto) { 43892cd41431SLinus Lüssing case ETH_P_IP: 4390d3d065c0SNikolay Aleksandrov if (!timer_pending(&brmctx->ip4_other_query.timer) || 4391d3d065c0SNikolay Aleksandrov rcu_dereference(brmctx->ip4_querier.port) == port) 43922cd41431SLinus Lüssing goto unlock; 43932cd41431SLinus Lüssing break; 43943993c4e1SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 43952cd41431SLinus Lüssing case ETH_P_IPV6: 4396d3d065c0SNikolay Aleksandrov if (!timer_pending(&brmctx->ip6_other_query.timer) || 4397d3d065c0SNikolay Aleksandrov rcu_dereference(brmctx->ip6_querier.port) == port) 43982cd41431SLinus Lüssing goto unlock; 43992cd41431SLinus Lüssing break; 44003993c4e1SLinus Lüssing #endif 44012cd41431SLinus Lüssing default: 44022cd41431SLinus Lüssing goto unlock; 44032cd41431SLinus Lüssing } 44042cd41431SLinus Lüssing 44052cd41431SLinus Lüssing ret = true; 44062cd41431SLinus Lüssing unlock: 44072cd41431SLinus Lüssing rcu_read_unlock(); 44082cd41431SLinus Lüssing return ret; 44092cd41431SLinus Lüssing } 44102cd41431SLinus Lüssing EXPORT_SYMBOL_GPL(br_multicast_has_querier_adjacent); 44111080ab95SNikolay Aleksandrov 44123b85f9baSLinus Lüssing /** 44133b85f9baSLinus Lüssing * br_multicast_has_router_adjacent - Checks for a router behind a bridge port 44143b85f9baSLinus Lüssing * @dev: The bridge port adjacent to which to check for a multicast router 44153b85f9baSLinus Lüssing * @proto: The protocol family to check for: IGMP -> ETH_P_IP, MLD -> ETH_P_IPV6 44163b85f9baSLinus Lüssing * 44173b85f9baSLinus Lüssing * Checks whether the given interface has a bridge on top and if so returns 44183b85f9baSLinus Lüssing * true if a multicast router is behind one of the other ports of this 44193b85f9baSLinus Lüssing * bridge. Otherwise returns false. 44203b85f9baSLinus Lüssing */ 44213b85f9baSLinus Lüssing bool br_multicast_has_router_adjacent(struct net_device *dev, int proto) 44223b85f9baSLinus Lüssing { 44239632233eSNikolay Aleksandrov struct net_bridge_mcast_port *pmctx; 4424d3d065c0SNikolay Aleksandrov struct net_bridge_mcast *brmctx; 44259632233eSNikolay Aleksandrov struct net_bridge_port *port; 44263b85f9baSLinus Lüssing bool ret = false; 44273b85f9baSLinus Lüssing 44283b85f9baSLinus Lüssing rcu_read_lock(); 44293b85f9baSLinus Lüssing port = br_port_get_check_rcu(dev); 44303b85f9baSLinus Lüssing if (!port) 44313b85f9baSLinus Lüssing goto unlock; 44323b85f9baSLinus Lüssing 4433d3d065c0SNikolay Aleksandrov brmctx = &port->br->multicast_ctx; 44343b85f9baSLinus Lüssing switch (proto) { 44353b85f9baSLinus Lüssing case ETH_P_IP: 4436d3d065c0SNikolay Aleksandrov hlist_for_each_entry_rcu(pmctx, &brmctx->ip4_mc_router_list, 44373b85f9baSLinus Lüssing ip4_rlist) { 44389632233eSNikolay Aleksandrov if (pmctx->port == port) 44393b85f9baSLinus Lüssing continue; 44403b85f9baSLinus Lüssing 44413b85f9baSLinus Lüssing ret = true; 44423b85f9baSLinus Lüssing goto unlock; 44433b85f9baSLinus Lüssing } 44443b85f9baSLinus Lüssing break; 44453b85f9baSLinus Lüssing #if IS_ENABLED(CONFIG_IPV6) 44463b85f9baSLinus Lüssing case ETH_P_IPV6: 4447d3d065c0SNikolay Aleksandrov hlist_for_each_entry_rcu(pmctx, &brmctx->ip6_mc_router_list, 44483b85f9baSLinus Lüssing ip6_rlist) { 44499632233eSNikolay Aleksandrov if (pmctx->port == port) 44503b85f9baSLinus Lüssing continue; 44513b85f9baSLinus Lüssing 44523b85f9baSLinus Lüssing ret = true; 44533b85f9baSLinus Lüssing goto unlock; 44543b85f9baSLinus Lüssing } 44553b85f9baSLinus Lüssing break; 44563b85f9baSLinus Lüssing #endif 44573b85f9baSLinus Lüssing default: 44583b85f9baSLinus Lüssing /* when compiled without IPv6 support, be conservative and 44593b85f9baSLinus Lüssing * always assume presence of an IPv6 multicast router 44603b85f9baSLinus Lüssing */ 44613b85f9baSLinus Lüssing ret = true; 44623b85f9baSLinus Lüssing } 44633b85f9baSLinus Lüssing 44643b85f9baSLinus Lüssing unlock: 44653b85f9baSLinus Lüssing rcu_read_unlock(); 44663b85f9baSLinus Lüssing return ret; 44673b85f9baSLinus Lüssing } 44683b85f9baSLinus Lüssing EXPORT_SYMBOL_GPL(br_multicast_has_router_adjacent); 44693b85f9baSLinus Lüssing 44701080ab95SNikolay Aleksandrov static void br_mcast_stats_add(struct bridge_mcast_stats __percpu *stats, 4471a65056ecSNikolay Aleksandrov const struct sk_buff *skb, u8 type, u8 dir) 44721080ab95SNikolay Aleksandrov { 44731080ab95SNikolay Aleksandrov struct bridge_mcast_stats *pstats = this_cpu_ptr(stats); 4474a65056ecSNikolay Aleksandrov __be16 proto = skb->protocol; 4475a65056ecSNikolay Aleksandrov unsigned int t_len; 44761080ab95SNikolay Aleksandrov 44771080ab95SNikolay Aleksandrov u64_stats_update_begin(&pstats->syncp); 44781080ab95SNikolay Aleksandrov switch (proto) { 44791080ab95SNikolay Aleksandrov case htons(ETH_P_IP): 4480a65056ecSNikolay Aleksandrov t_len = ntohs(ip_hdr(skb)->tot_len) - ip_hdrlen(skb); 44811080ab95SNikolay Aleksandrov switch (type) { 44821080ab95SNikolay Aleksandrov case IGMP_HOST_MEMBERSHIP_REPORT: 44831080ab95SNikolay Aleksandrov pstats->mstats.igmp_v1reports[dir]++; 44841080ab95SNikolay Aleksandrov break; 44851080ab95SNikolay Aleksandrov case IGMPV2_HOST_MEMBERSHIP_REPORT: 44861080ab95SNikolay Aleksandrov pstats->mstats.igmp_v2reports[dir]++; 44871080ab95SNikolay Aleksandrov break; 44881080ab95SNikolay Aleksandrov case IGMPV3_HOST_MEMBERSHIP_REPORT: 44891080ab95SNikolay Aleksandrov pstats->mstats.igmp_v3reports[dir]++; 44901080ab95SNikolay Aleksandrov break; 44911080ab95SNikolay Aleksandrov case IGMP_HOST_MEMBERSHIP_QUERY: 4492a65056ecSNikolay Aleksandrov if (t_len != sizeof(struct igmphdr)) { 4493a65056ecSNikolay Aleksandrov pstats->mstats.igmp_v3queries[dir]++; 4494a65056ecSNikolay Aleksandrov } else { 4495a65056ecSNikolay Aleksandrov unsigned int offset = skb_transport_offset(skb); 4496a65056ecSNikolay Aleksandrov struct igmphdr *ih, _ihdr; 4497a65056ecSNikolay Aleksandrov 4498a65056ecSNikolay Aleksandrov ih = skb_header_pointer(skb, offset, 4499a65056ecSNikolay Aleksandrov sizeof(_ihdr), &_ihdr); 4500a65056ecSNikolay Aleksandrov if (!ih) 4501a65056ecSNikolay Aleksandrov break; 4502a65056ecSNikolay Aleksandrov if (!ih->code) 4503a65056ecSNikolay Aleksandrov pstats->mstats.igmp_v1queries[dir]++; 4504a65056ecSNikolay Aleksandrov else 4505a65056ecSNikolay Aleksandrov pstats->mstats.igmp_v2queries[dir]++; 4506a65056ecSNikolay Aleksandrov } 45071080ab95SNikolay Aleksandrov break; 45081080ab95SNikolay Aleksandrov case IGMP_HOST_LEAVE_MESSAGE: 45091080ab95SNikolay Aleksandrov pstats->mstats.igmp_leaves[dir]++; 45101080ab95SNikolay Aleksandrov break; 45111080ab95SNikolay Aleksandrov } 45121080ab95SNikolay Aleksandrov break; 45131080ab95SNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6) 45141080ab95SNikolay Aleksandrov case htons(ETH_P_IPV6): 4515a65056ecSNikolay Aleksandrov t_len = ntohs(ipv6_hdr(skb)->payload_len) + 4516a65056ecSNikolay Aleksandrov sizeof(struct ipv6hdr); 4517a65056ecSNikolay Aleksandrov t_len -= skb_network_header_len(skb); 45181080ab95SNikolay Aleksandrov switch (type) { 45191080ab95SNikolay Aleksandrov case ICMPV6_MGM_REPORT: 45201080ab95SNikolay Aleksandrov pstats->mstats.mld_v1reports[dir]++; 45211080ab95SNikolay Aleksandrov break; 45221080ab95SNikolay Aleksandrov case ICMPV6_MLD2_REPORT: 45231080ab95SNikolay Aleksandrov pstats->mstats.mld_v2reports[dir]++; 45241080ab95SNikolay Aleksandrov break; 45251080ab95SNikolay Aleksandrov case ICMPV6_MGM_QUERY: 4526a65056ecSNikolay Aleksandrov if (t_len != sizeof(struct mld_msg)) 4527a65056ecSNikolay Aleksandrov pstats->mstats.mld_v2queries[dir]++; 4528a65056ecSNikolay Aleksandrov else 4529a65056ecSNikolay Aleksandrov pstats->mstats.mld_v1queries[dir]++; 45301080ab95SNikolay Aleksandrov break; 45311080ab95SNikolay Aleksandrov case ICMPV6_MGM_REDUCTION: 45321080ab95SNikolay Aleksandrov pstats->mstats.mld_leaves[dir]++; 45331080ab95SNikolay Aleksandrov break; 45341080ab95SNikolay Aleksandrov } 45351080ab95SNikolay Aleksandrov break; 45361080ab95SNikolay Aleksandrov #endif /* CONFIG_IPV6 */ 45371080ab95SNikolay Aleksandrov } 45381080ab95SNikolay Aleksandrov u64_stats_update_end(&pstats->syncp); 45391080ab95SNikolay Aleksandrov } 45401080ab95SNikolay Aleksandrov 4541adc47037SNikolay Aleksandrov void br_multicast_count(struct net_bridge *br, 4542adc47037SNikolay Aleksandrov const struct net_bridge_port *p, 4543a65056ecSNikolay Aleksandrov const struct sk_buff *skb, u8 type, u8 dir) 45441080ab95SNikolay Aleksandrov { 45451080ab95SNikolay Aleksandrov struct bridge_mcast_stats __percpu *stats; 45461080ab95SNikolay Aleksandrov 45471080ab95SNikolay Aleksandrov /* if multicast_disabled is true then igmp type can't be set */ 4548675779adSNikolay Aleksandrov if (!type || !br_opt_get(br, BROPT_MULTICAST_STATS_ENABLED)) 45491080ab95SNikolay Aleksandrov return; 45501080ab95SNikolay Aleksandrov 45511080ab95SNikolay Aleksandrov if (p) 45521080ab95SNikolay Aleksandrov stats = p->mcast_stats; 45531080ab95SNikolay Aleksandrov else 45541080ab95SNikolay Aleksandrov stats = br->mcast_stats; 45551080ab95SNikolay Aleksandrov if (WARN_ON(!stats)) 45561080ab95SNikolay Aleksandrov return; 45571080ab95SNikolay Aleksandrov 4558a65056ecSNikolay Aleksandrov br_mcast_stats_add(stats, skb, type, dir); 45591080ab95SNikolay Aleksandrov } 45601080ab95SNikolay Aleksandrov 45611080ab95SNikolay Aleksandrov int br_multicast_init_stats(struct net_bridge *br) 45621080ab95SNikolay Aleksandrov { 45631080ab95SNikolay Aleksandrov br->mcast_stats = netdev_alloc_pcpu_stats(struct bridge_mcast_stats); 45641080ab95SNikolay Aleksandrov if (!br->mcast_stats) 45651080ab95SNikolay Aleksandrov return -ENOMEM; 45661080ab95SNikolay Aleksandrov 45671080ab95SNikolay Aleksandrov return 0; 45681080ab95SNikolay Aleksandrov } 45691080ab95SNikolay Aleksandrov 4570b6fe0440SIdo Schimmel void br_multicast_uninit_stats(struct net_bridge *br) 4571b6fe0440SIdo Schimmel { 4572b6fe0440SIdo Schimmel free_percpu(br->mcast_stats); 4573b6fe0440SIdo Schimmel } 4574b6fe0440SIdo Schimmel 4575b3b6a84cSArnd Bergmann /* noinline for https://bugs.llvm.org/show_bug.cgi?id=45802#c9 */ 4576b3b6a84cSArnd Bergmann static noinline_for_stack void mcast_stats_add_dir(u64 *dst, u64 *src) 45771080ab95SNikolay Aleksandrov { 45781080ab95SNikolay Aleksandrov dst[BR_MCAST_DIR_RX] += src[BR_MCAST_DIR_RX]; 45791080ab95SNikolay Aleksandrov dst[BR_MCAST_DIR_TX] += src[BR_MCAST_DIR_TX]; 45801080ab95SNikolay Aleksandrov } 45811080ab95SNikolay Aleksandrov 45821080ab95SNikolay Aleksandrov void br_multicast_get_stats(const struct net_bridge *br, 45831080ab95SNikolay Aleksandrov const struct net_bridge_port *p, 45841080ab95SNikolay Aleksandrov struct br_mcast_stats *dest) 45851080ab95SNikolay Aleksandrov { 45861080ab95SNikolay Aleksandrov struct bridge_mcast_stats __percpu *stats; 45871080ab95SNikolay Aleksandrov struct br_mcast_stats tdst; 45881080ab95SNikolay Aleksandrov int i; 45891080ab95SNikolay Aleksandrov 45901080ab95SNikolay Aleksandrov memset(dest, 0, sizeof(*dest)); 45911080ab95SNikolay Aleksandrov if (p) 45921080ab95SNikolay Aleksandrov stats = p->mcast_stats; 45931080ab95SNikolay Aleksandrov else 45941080ab95SNikolay Aleksandrov stats = br->mcast_stats; 45951080ab95SNikolay Aleksandrov if (WARN_ON(!stats)) 45961080ab95SNikolay Aleksandrov return; 45971080ab95SNikolay Aleksandrov 45981080ab95SNikolay Aleksandrov memset(&tdst, 0, sizeof(tdst)); 45991080ab95SNikolay Aleksandrov for_each_possible_cpu(i) { 46001080ab95SNikolay Aleksandrov struct bridge_mcast_stats *cpu_stats = per_cpu_ptr(stats, i); 46011080ab95SNikolay Aleksandrov struct br_mcast_stats temp; 46021080ab95SNikolay Aleksandrov unsigned int start; 46031080ab95SNikolay Aleksandrov 46041080ab95SNikolay Aleksandrov do { 46051080ab95SNikolay Aleksandrov start = u64_stats_fetch_begin_irq(&cpu_stats->syncp); 46061080ab95SNikolay Aleksandrov memcpy(&temp, &cpu_stats->mstats, sizeof(temp)); 46071080ab95SNikolay Aleksandrov } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start)); 46081080ab95SNikolay Aleksandrov 4609a65056ecSNikolay Aleksandrov mcast_stats_add_dir(tdst.igmp_v1queries, temp.igmp_v1queries); 4610a65056ecSNikolay Aleksandrov mcast_stats_add_dir(tdst.igmp_v2queries, temp.igmp_v2queries); 4611a65056ecSNikolay Aleksandrov mcast_stats_add_dir(tdst.igmp_v3queries, temp.igmp_v3queries); 46121080ab95SNikolay Aleksandrov mcast_stats_add_dir(tdst.igmp_leaves, temp.igmp_leaves); 46131080ab95SNikolay Aleksandrov mcast_stats_add_dir(tdst.igmp_v1reports, temp.igmp_v1reports); 46141080ab95SNikolay Aleksandrov mcast_stats_add_dir(tdst.igmp_v2reports, temp.igmp_v2reports); 46151080ab95SNikolay Aleksandrov mcast_stats_add_dir(tdst.igmp_v3reports, temp.igmp_v3reports); 46161080ab95SNikolay Aleksandrov tdst.igmp_parse_errors += temp.igmp_parse_errors; 46171080ab95SNikolay Aleksandrov 4618a65056ecSNikolay Aleksandrov mcast_stats_add_dir(tdst.mld_v1queries, temp.mld_v1queries); 4619a65056ecSNikolay Aleksandrov mcast_stats_add_dir(tdst.mld_v2queries, temp.mld_v2queries); 46201080ab95SNikolay Aleksandrov mcast_stats_add_dir(tdst.mld_leaves, temp.mld_leaves); 46211080ab95SNikolay Aleksandrov mcast_stats_add_dir(tdst.mld_v1reports, temp.mld_v1reports); 46221080ab95SNikolay Aleksandrov mcast_stats_add_dir(tdst.mld_v2reports, temp.mld_v2reports); 46231080ab95SNikolay Aleksandrov tdst.mld_parse_errors += temp.mld_parse_errors; 46241080ab95SNikolay Aleksandrov } 46251080ab95SNikolay Aleksandrov memcpy(dest, &tdst, sizeof(*dest)); 46261080ab95SNikolay Aleksandrov } 462719e3a9c9SNikolay Aleksandrov 462819e3a9c9SNikolay Aleksandrov int br_mdb_hash_init(struct net_bridge *br) 462919e3a9c9SNikolay Aleksandrov { 4630085b53c8SNikolay Aleksandrov int err; 4631085b53c8SNikolay Aleksandrov 4632085b53c8SNikolay Aleksandrov err = rhashtable_init(&br->sg_port_tbl, &br_sg_port_rht_params); 4633085b53c8SNikolay Aleksandrov if (err) 4634085b53c8SNikolay Aleksandrov return err; 4635085b53c8SNikolay Aleksandrov 4636085b53c8SNikolay Aleksandrov err = rhashtable_init(&br->mdb_hash_tbl, &br_mdb_rht_params); 4637085b53c8SNikolay Aleksandrov if (err) { 4638085b53c8SNikolay Aleksandrov rhashtable_destroy(&br->sg_port_tbl); 4639085b53c8SNikolay Aleksandrov return err; 4640085b53c8SNikolay Aleksandrov } 4641085b53c8SNikolay Aleksandrov 4642085b53c8SNikolay Aleksandrov return 0; 464319e3a9c9SNikolay Aleksandrov } 464419e3a9c9SNikolay Aleksandrov 464519e3a9c9SNikolay Aleksandrov void br_mdb_hash_fini(struct net_bridge *br) 464619e3a9c9SNikolay Aleksandrov { 4647085b53c8SNikolay Aleksandrov rhashtable_destroy(&br->sg_port_tbl); 464819e3a9c9SNikolay Aleksandrov rhashtable_destroy(&br->mdb_hash_tbl); 464919e3a9c9SNikolay Aleksandrov } 4650