[Bridge] [RFC PATCH v2] net: bridge: igmp: Extend IGMP query to be per vlan

Horatiu Vultur horatiu.vultur at microchip.com
Wed Jan 13 17:02:29 UTC 2021


The 01/13/2021 14:15, Nikolay Aleksandrov wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
> 
> On 12/01/2021 15:59, Horatiu Vultur wrote:
> > Based on the comments of the previous version, we started to work on a
> > new version, so it would be possible to enable/disable queries per vlan.
> > This is still work in progress and there are plenty of things that are
> > not implemented and tested:
> > - ipv6 support
> > - the fast path needs to be improved
> > - currently it is possible only to enable/disable the queries per vlan,
> >   all the other configurations are global
> > - toggling vlan_filtering is not tested
> > - remove duplicated information
> > - etc...
> >
> > But there are few things that are working like:
> > - sending queries per vlan
> > - stop sending queries if there is a better querier per vlan
> > - when ports are added/removed from vlan
> > - etc...
> >
> > We were wondering if this what you had in mind when you proposed to have
> > this per vlan? Or we are completely off? Or we should fix some of the
> > issues that I mentioned, before you can see more clearly the direction?
> >
> > Signed-off-by: Horatiu Vultur <horatiu.vultur at microchip.com>
> > ---
> >  include/uapi/linux/if_link.h |   1 +
> >  net/bridge/br_device.c       |   2 +-
> >  net/bridge/br_input.c        |   2 +-
> >  net/bridge/br_multicast.c    | 505 ++++++++++++++++++++++++++++++-----
> >  net/bridge/br_netlink.c      |   9 +-
> >  net/bridge/br_private.h      |  90 ++++++-
> >  net/bridge/br_sysfs_br.c     |  31 ++-
> >  net/bridge/br_vlan.c         |   3 +
> >  8 files changed, 560 insertions(+), 83 deletions(-)
> >
> 

Hi Nik,

> Hi Horatiu,
> No, unfortunately not even close.

At least is good that I didn't continue in this direction :)

> We already have per-port per-vlan and global per-vlan
> contexts which are also linked together for each vlan, those must be used for any vlan
> configuration and state. The problem is that you'd have to mix igmp and vlan code and
> those two live under two different kconfig options, and worse rely on different locks, so
> extra care must be taken.

Point taken.

> Any vlan lookups must use the vlan hashes, (almost) _no_ linear
> walks or new lists are needed (the exception is obviously port going down where a walk
> over port's vlans is needed).

Yes, I was thinking about this vlan lookups that need to be change to
use the hashes but I thought I can do it later.

> In almost all contexts below a vlan lookup has already been
> done by the input functions, the result of that lookup must be saved and re-used. The
> vlan options API needs to be used for configuring vlans (per-vlan mcast options), unfortunately
> I still haven't upstreamed the iproute2 part, so you might have to do that as well.
> Obviously with all of the above the current default situation must not change unless the
> user configures it so. If you don't need this asap, I'll probably get to it in two months
> after EHT and the new bridge flush api, even we are still carrying an out-of-tree patch
> for this which someone (not from cumulus) tried to upstream a few years back, but it also has
> wrong design in general. :)

Definetly this is not asap. But I would like to try to have another look
at this to see if I get closer to what you are looking for :)

> 
> Thanks,
>  Nik
> 
> > diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
> > index 82708c6db432..11ec1d45c24e 100644
> > --- a/include/uapi/linux/if_link.h
> > +++ b/include/uapi/linux/if_link.h
> > @@ -472,6 +472,7 @@ enum {
> >       IFLA_BR_MCAST_MLD_VERSION,
> >       IFLA_BR_VLAN_STATS_PER_PORT,
> >       IFLA_BR_MULTI_BOOLOPT,
> > +     IFLA_BR_MCAST_QUERIER_VID,
> >       __IFLA_BR_MAX,
> >  };
> >
> > diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
> > index 3f2f06b4dd27..aca4e8074a8f 100644
> > --- a/net/bridge/br_device.c
> > +++ b/net/bridge/br_device.c
> > @@ -89,7 +89,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
> >
> >               mdst = br_mdb_get(br, skb, vid);
> >               if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) &&
> > -                 br_multicast_querier_exists(br, eth_hdr(skb), mdst))
> > +                 br_multicast_querier_exists(br, eth_hdr(skb), mdst, vid))
> >                       br_multicast_flood(mdst, skb, false, true);
> >               else
> >                       br_flood(br, skb, BR_PKT_MULTICAST, false, true);
> > diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
> > index 222285d9dae2..03e445af6c1f 100644
> > --- a/net/bridge/br_input.c
> > +++ b/net/bridge/br_input.c
> > @@ -130,7 +130,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
> >       case BR_PKT_MULTICAST:
> >               mdst = br_mdb_get(br, skb, vid);
> >               if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) &&
> > -                 br_multicast_querier_exists(br, eth_hdr(skb), mdst)) {
> > +                 br_multicast_querier_exists(br, eth_hdr(skb), mdst, vid)) {
> >                       if ((mdst && mdst->host_joined) ||
> >                           br_multicast_is_router(br)) {
> >                               local_rcv = true;
> > diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
> > index 257ac4e25f6d..b4fac25101e4 100644
> > --- a/net/bridge/br_multicast.c
> > +++ b/net/bridge/br_multicast.c
> > @@ -48,8 +48,11 @@ static const struct rhashtable_params br_sg_port_rht_params = {
> >       .automatic_shrinking = true,
> >  };
> >
> > +static void br_ip4_multicast_query_expired(struct timer_list *t);
> > +static void br_ip4_multicast_querier_expired(struct timer_list *t);
> >  static void br_multicast_start_querier(struct net_bridge *br,
> > -                                    struct bridge_mcast_own_query *query);
> > +                                    struct bridge_mcast_own_query *query,
> > +                                    u16 vid);
> >  static void br_multicast_add_router(struct net_bridge *br,
> >                                   struct net_bridge_port *port);
> >  static void br_ip4_multicast_leave_group(struct net_bridge *br,
> > @@ -87,6 +90,112 @@ br_sg_port_find(struct net_bridge *br,
> >                                     br_sg_port_rht_params);
> >  }
> >
> > +static void br_mcast_del_other_query(struct bridge_mcast_other_query *query)
> > +{
> > +     del_timer_sync(&query->timer);
> > +     list_del(&query->list);
> > +     kfree(query);
> > +}
> > +
> > +static struct bridge_mcast_other_query *
> > +br_mcast_add_other_query(struct list_head *list, u16 vid,
> > +                      void (*callback)(struct timer_list *t))
> > +{
> > +     struct bridge_mcast_other_query *query;
> > +
> > +     query = kzalloc(sizeof(*query), GFP_KERNEL);
> > +     if (!query)
> > +             return NULL;
> > +
> > +     query->vid = vid;
> > +     timer_setup(&query->timer, callback, 0);
> > +
> > +     list_add(&query->list, list);
> > +
> > +     return query;
> > +}
> > +
> > +static void br_mcast_del_own_query(struct bridge_mcast_own_query *query)
> > +{
> > +     del_timer_sync(&query->timer);
> > +     list_del(&query->list);
> > +     kfree(query);
> > +}
> > +
> > +static struct bridge_mcast_own_query *
> > +br_mcast_add_own_query(struct list_head *list, u16 vid,
> > +                    void (*callback)(struct timer_list *t))
> > +{
> > +     struct bridge_mcast_own_query *query;
> > +
> > +     query = kzalloc(sizeof(*query), GFP_KERNEL);
> > +     if (!query)
> > +             return NULL;
> > +
> > +     query->vid = vid;
> > +     timer_setup(&query->timer, callback, 0);
> > +
> > +     list_add(&query->list, list);
> > +
> > +     return query;
> > +}
> > +
> > +static void br_mcast_add_queries(struct net_bridge *br, u16 vid)
> > +{
> > +     struct bridge_mcast_other_query *other;
> > +     struct bridge_mcast_own_query *own;
> > +
> > +     own = br_mcast_find_own_query(&br->ip4_own_queries, vid);
> > +     if (!own) {
> > +             own = br_mcast_add_own_query(&br->ip4_own_queries, vid,
> > +                                          br_ip4_multicast_query_expired);
> > +             own->ip4 = true;
> > +             own->br = br;
> > +     }
> > +
> > +     other = br_mcast_find_other_query(&br->ip4_other_queries, vid);
> > +     if (!other) {
> > +             other = br_mcast_add_other_query(&br->ip4_other_queries, vid,
> > +                                              br_ip4_multicast_querier_expired);
> > +             other->br = br;
> > +     }
> > +}
> > +
> > +struct bridge_mcast_own_query *
> > +br_mcast_find_own_query(struct list_head *list, u16 vid)
> > +{
> > +     struct bridge_mcast_own_query *query = NULL;
> > +
> > +     list_for_each_entry(query, list, list)
> > +             if (query->vid == vid)
> > +                     return query;
> > +
> > +     return NULL;
> > +}
> > +
> > +struct bridge_mcast_other_query *
> > +br_mcast_find_other_query(struct list_head *list, u16 vid)
> > +{
> > +     struct bridge_mcast_other_query *query = NULL;
> > +
> > +     list_for_each_entry(query, list, list)
> > +             if (query->vid == vid)
> > +                     return query;
> > +
> > +     return NULL;
> > +}
> > +
> > +bool br_mcast_exist_own_query(struct net_bridge *br)
> > +{
> > +     struct bridge_mcast_own_query *query = NULL;
> > +
> > +     list_for_each_entry(query, &br->ip4_own_queries, list)
> > +             if (query->enabled)
> > +                     return true;
> > +
> > +     return false;
> > +}
> > +
> >  static struct net_bridge_mdb_entry *br_mdb_ip_get_rcu(struct net_bridge *br,
> >                                                     struct br_ip *dst)
> >  {
> > @@ -688,7 +797,8 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge *br,
> >                                                   __be32 ip_dst, __be32 group,
> >                                                   bool with_srcs, bool over_lmqt,
> >                                                   u8 sflag, u8 *igmp_type,
> > -                                                 bool *need_rexmit)
> > +                                                 bool *need_rexmit,
> > +                                                 u16 vid)
> >  {
> >       struct net_bridge_port *p = pg ? pg->key.port : NULL;
> >       struct net_bridge_group_src *ent;
> > @@ -724,6 +834,9 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge *br,
> >       }
> >
> >       pkt_size = sizeof(*eth) + sizeof(*iph) + 4 + igmp_hdr_size;
> > +     if (br_vlan_enabled(br->dev) && vid != 0)
> > +             pkt_size += 4;
> > +
> >       if ((p && pkt_size > p->dev->mtu) ||
> >           pkt_size > br->dev->mtu)
> >               return NULL;
> > @@ -732,6 +845,9 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge *br,
> >       if (!skb)
> >               goto out;
> >
> > +     if (br_vlan_enabled(br->dev) && vid != 0)
> > +             __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
> > +
> >       skb->protocol = htons(ETH_P_IP);
> >
> >       skb_reset_mac_header(skb);
> > @@ -1008,7 +1124,7 @@ static struct sk_buff *br_multicast_alloc_query(struct net_bridge *br,
> >                                                   ip4_dst, group->dst.ip4,
> >                                                   with_srcs, over_lmqt,
> >                                                   sflag, igmp_type,
> > -                                                 need_rexmit);
> > +                                                 need_rexmit, group->vid);
> >  #if IS_ENABLED(CONFIG_IPV6)
> >       case htons(ETH_P_IPV6): {
> >               struct in6_addr ip6_dst;
> > @@ -1398,7 +1514,7 @@ static void br_multicast_querier_expired(struct net_bridge *br,
> >       if (!netif_running(br->dev) || !br_opt_get(br, BROPT_MULTICAST_ENABLED))
> >               goto out;
> >
> > -     br_multicast_start_querier(br, query);
> > +     br_multicast_start_querier(br, query, query->vid);
> >
> >  out:
> >       spin_unlock(&br->multicast_lock);
> > @@ -1406,9 +1522,14 @@ static void br_multicast_querier_expired(struct net_bridge *br,
> >
> >  static void br_ip4_multicast_querier_expired(struct timer_list *t)
> >  {
> > -     struct net_bridge *br = from_timer(br, t, ip4_other_query.timer);
> > +     struct bridge_mcast_other_query *other_query =
> > +             from_timer(other_query, t, timer);
> > +     struct net_bridge *br = other_query->br;
> > +     struct bridge_mcast_own_query *query;
> >
> > -     br_multicast_querier_expired(br, &br->ip4_own_query);
> > +     list_for_each_entry(query, &br->ip4_own_queries, list)
> > +             if (query->enabled && query->vid == other_query->vid)
> > +                     br_multicast_querier_expired(br, query);
> >  }
> >
> >  #if IS_ENABLED(CONFIG_IPV6)
> > @@ -1477,19 +1598,22 @@ static void br_multicast_send_query(struct net_bridge *br,
> >                                   struct bridge_mcast_own_query *own_query)
> >  {
> >       struct bridge_mcast_other_query *other_query = NULL;
> > +     struct net_bridge_vlan_group *vg;
> >       struct br_ip br_group;
> >       unsigned long time;
> >
> >       if (!netif_running(br->dev) ||
> > -         !br_opt_get(br, BROPT_MULTICAST_ENABLED) ||
> > -         !br_opt_get(br, BROPT_MULTICAST_QUERIER))
> > +         !br_opt_get(br, BROPT_MULTICAST_ENABLED))
> >               return;
> >
> > -     memset(&br_group.dst, 0, sizeof(br_group.dst));
> > +     if (!own_query->enabled)
> > +             return;
> >
> > -     if (port ? (own_query == &port->ip4_own_query) :
> > -                (own_query == &br->ip4_own_query)) {
> > -             other_query = &br->ip4_other_query;
> > +     memset(&br_group, 0, sizeof(br_group));
> > +
> > +     if (own_query->ip4) {
> > +             other_query = br_mcast_find_other_query(&br->ip4_other_queries,
> > +                                                     own_query->vid);
> >               br_group.proto = htons(ETH_P_IP);
> >  #if IS_ENABLED(CONFIG_IPV6)
> >       } else {
> > @@ -1501,6 +1625,12 @@ static void br_multicast_send_query(struct net_bridge *br,
> >       if (!other_query || timer_pending(&other_query->timer))
> >               return;
> >
> > +     br_group.vid = own_query->vid;
> > +
> > +     vg =  port ? nbp_vlan_group(port) : br_vlan_group(br);
> > +     if (vg->pvid == own_query->vid)
> > +             br_group.vid = 0;
> > +
> >       __br_multicast_send_query(br, port, NULL, NULL, &br_group, false, 0,
> >                                 NULL);
> >
> > @@ -1533,9 +1663,10 @@ br_multicast_port_query_expired(struct net_bridge_port *port,
> >
> >  static void br_ip4_multicast_port_query_expired(struct timer_list *t)
> >  {
> > -     struct net_bridge_port *port = from_timer(port, t, ip4_own_query.timer);
> > +     struct bridge_mcast_own_query *query = from_timer(query, t, timer);
> > +     struct net_bridge_port *port = query->port;
> >
> > -     br_multicast_port_query_expired(port, &port->ip4_own_query);
> > +     br_multicast_port_query_expired(port, query);
> >  }
> >
> >  #if IS_ENABLED(CONFIG_IPV6)
> > @@ -1551,17 +1682,23 @@ static void br_multicast_port_group_rexmit(struct timer_list *t)
> >  {
> >       struct net_bridge_port_group *pg = from_timer(pg, t, rexmit_timer);
> >       struct bridge_mcast_other_query *other_query = NULL;
> > +     struct bridge_mcast_own_query *own_query = NULL;
> >       struct net_bridge *br = pg->key.port->br;
> > +     u16 vid = pg->key.addr.vid;
> >       bool need_rexmit = false;
> >
> >       spin_lock(&br->multicast_lock);
> > +     own_query = br_mcast_find_own_query(&pg->key.port->ip4_own_queries,
> > +                                         vid);
> > +
> >       if (!netif_running(br->dev) || hlist_unhashed(&pg->mglist) ||
> >           !br_opt_get(br, BROPT_MULTICAST_ENABLED) ||
> > -         !br_opt_get(br, BROPT_MULTICAST_QUERIER))
> > +         !own_query || !own_query->enabled)
> >               goto out;
> >
> >       if (pg->key.addr.proto == htons(ETH_P_IP))
> > -             other_query = &br->ip4_other_query;
> > +             other_query = br_mcast_find_other_query(&br->ip4_other_queries,
> > +                                                     vid);
> >  #if IS_ENABLED(CONFIG_IPV6)
> >       else
> >               other_query = &br->ip6_other_query;
> > @@ -1603,8 +1740,7 @@ int br_multicast_add_port(struct net_bridge_port *port)
> >
> >       timer_setup(&port->multicast_router_timer,
> >                   br_multicast_router_expired, 0);
> > -     timer_setup(&port->ip4_own_query.timer,
> > -                 br_ip4_multicast_port_query_expired, 0);
> > +     INIT_LIST_HEAD(&port->ip4_own_queries);
> >  #if IS_ENABLED(CONFIG_IPV6)
> >       timer_setup(&port->ip6_own_query.timer,
> >                   br_ip6_multicast_port_query_expired, 0);
> > @@ -1621,6 +1757,7 @@ int br_multicast_add_port(struct net_bridge_port *port)
> >
> >  void br_multicast_del_port(struct net_bridge_port *port)
> >  {
> > +     struct bridge_mcast_own_query *query, *tmp;
> >       struct net_bridge *br = port->br;
> >       struct net_bridge_port_group *pg;
> >       HLIST_HEAD(deleted_head);
> > @@ -1635,6 +1772,9 @@ void br_multicast_del_port(struct net_bridge_port *port)
> >       br_multicast_gc(&deleted_head);
> >       del_timer_sync(&port->multicast_router_timer);
> >       free_percpu(port->mcast_stats);
> > +
> > +     list_for_each_entry_safe(query, tmp, &port->ip4_own_queries, list)
> > +             br_mcast_del_own_query(query);
> >  }
> >
> >  static void br_multicast_enable(struct bridge_mcast_own_query *query)
> > @@ -1646,14 +1786,49 @@ static void br_multicast_enable(struct bridge_mcast_own_query *query)
> >               mod_timer(&query->timer, jiffies);
> >  }
> >
> > +static void br_multicast_disable(struct bridge_mcast_own_query *query)
> > +{
> > +     del_timer_sync(&query->timer);
> > +}
> > +
> >  static void __br_multicast_enable_port(struct net_bridge_port *port)
> >  {
> > +     struct bridge_mcast_own_query *query;
> >       struct net_bridge *br = port->br;
> >
> >       if (!br_opt_get(br, BROPT_MULTICAST_ENABLED) || !netif_running(br->dev))
> >               return;
> >
> > -     br_multicast_enable(&port->ip4_own_query);
> > +     list_for_each_entry(query, &br->ip4_own_queries, list) {
> > +             struct bridge_mcast_own_query *port_query;
> > +             struct net_bridge_vlan_group *vg;
> > +
> > +             if (!query->enabled)
> > +                     continue;
> > +
> > +             if (br_vlan_enabled(br->dev)) {
> > +                     vg = nbp_vlan_group(port);
> > +                     if (!vg || (vg && !br_vlan_find(vg, query->vid)))
> > +                             continue;
> > +             }
> > +
> > +             port_query = br_mcast_find_own_query(&port->ip4_own_queries,
> > +                                                  query->vid);
> > +             if (!port_query) {
> > +                     port_query = br_mcast_add_own_query(&port->ip4_own_queries,
> > +                                                         query->vid,
> > +                                                         br_ip4_multicast_port_query_expired);
> > +                     if (!port_query)
> > +                             continue;
> > +
> > +                     port_query->port = port;
> > +             }
> > +
> > +             if (query->ip4) {
> > +                     port_query->ip4 = true;
> > +                     br_multicast_enable(port_query);
> > +             }
> > +     }
> >  #if IS_ENABLED(CONFIG_IPV6)
> >       br_multicast_enable(&port->ip6_own_query);
> >  #endif
> > @@ -1673,6 +1848,7 @@ void br_multicast_enable_port(struct net_bridge_port *port)
> >
> >  void br_multicast_disable_port(struct net_bridge_port *port)
> >  {
> > +     struct bridge_mcast_own_query *query;
> >       struct net_bridge *br = port->br;
> >       struct net_bridge_port_group *pg;
> >       struct hlist_node *n;
> > @@ -1685,7 +1861,8 @@ void br_multicast_disable_port(struct net_bridge_port *port)
> >       __del_port_router(port);
> >
> >       del_timer(&port->multicast_router_timer);
> > -     del_timer(&port->ip4_own_query.timer);
> > +     list_for_each_entry(query, &port->ip4_own_queries, list)
> > +             del_timer(&query->timer);
> >  #if IS_ENABLED(CONFIG_IPV6)
> >       del_timer(&port->ip6_own_query.timer);
> >  #endif
> > @@ -1717,17 +1894,23 @@ static void __grp_src_mod_timer(struct net_bridge_group_src *src,
> >  static void __grp_src_query_marked_and_rexmit(struct net_bridge_port_group *pg)
> >  {
> >       struct bridge_mcast_other_query *other_query = NULL;
> > +     struct bridge_mcast_own_query *own_query = NULL;
> >       struct net_bridge *br = pg->key.port->br;
> >       u32 lmqc = br->multicast_last_member_count;
> >       unsigned long lmqt, lmi, now = jiffies;
> >       struct net_bridge_group_src *ent;
> > +     u16 vid = pg->key.addr.vid;
> > +
> > +     own_query = br_mcast_find_own_query(&pg->key.port->ip4_own_queries,
> > +                                         vid);
> >
> >       if (!netif_running(br->dev) ||
> >           !br_opt_get(br, BROPT_MULTICAST_ENABLED))
> >               return;
> >
> >       if (pg->key.addr.proto == htons(ETH_P_IP))
> > -             other_query = &br->ip4_other_query;
> > +             other_query = br_mcast_find_other_query(&br->ip4_other_queries,
> > +                                                     vid);
> >  #if IS_ENABLED(CONFIG_IPV6)
> >       else
> >               other_query = &br->ip6_other_query;
> > @@ -1738,7 +1921,7 @@ static void __grp_src_query_marked_and_rexmit(struct net_bridge_port_group *pg)
> >               if (ent->flags & BR_SGRP_F_SEND) {
> >                       ent->flags &= ~BR_SGRP_F_SEND;
> >                       if (ent->timer.expires > lmqt) {
> > -                             if (br_opt_get(br, BROPT_MULTICAST_QUERIER) &&
> > +                             if (own_query && own_query->enabled &&
> >                                   other_query &&
> >                                   !timer_pending(&other_query->timer))
> >                                       ent->src_query_rexmit_cnt = lmqc;
> > @@ -1747,7 +1930,7 @@ static void __grp_src_query_marked_and_rexmit(struct net_bridge_port_group *pg)
> >               }
> >       }
> >
> > -     if (!br_opt_get(br, BROPT_MULTICAST_QUERIER) ||
> > +     if (!own_query || !own_query->enabled ||
> >           !other_query || timer_pending(&other_query->timer))
> >               return;
> >
> > @@ -1763,21 +1946,27 @@ static void __grp_src_query_marked_and_rexmit(struct net_bridge_port_group *pg)
> >  static void __grp_send_query_and_rexmit(struct net_bridge_port_group *pg)
> >  {
> >       struct bridge_mcast_other_query *other_query = NULL;
> > +     struct bridge_mcast_own_query *own_query = NULL;
> >       struct net_bridge *br = pg->key.port->br;
> >       unsigned long now = jiffies, lmi;
> > +     u16 vid = pg->key.addr.vid;
> >
> >       if (!netif_running(br->dev) ||
> >           !br_opt_get(br, BROPT_MULTICAST_ENABLED))
> >               return;
> >
> > +     own_query = br_mcast_find_own_query(&pg->key.port->ip4_own_queries,
> > +                                         vid);
> > +
> >       if (pg->key.addr.proto == htons(ETH_P_IP))
> > -             other_query = &br->ip4_other_query;
> > +             other_query = br_mcast_find_other_query(&br->ip4_other_queries,
> > +                                                     vid);
> >  #if IS_ENABLED(CONFIG_IPV6)
> >       else
> >               other_query = &br->ip6_other_query;
> >  #endif
> >
> > -     if (br_opt_get(br, BROPT_MULTICAST_QUERIER) &&
> > +     if (own_query && own_query->enabled &&
> >           other_query && !timer_pending(&other_query->timer)) {
> >               lmi = now + br->multicast_last_member_interval;
> >               pg->grp_query_rexmit_cnt = br->multicast_last_member_count - 1;
> > @@ -2484,10 +2673,12 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br,
> >
> >  static bool br_ip4_multicast_select_querier(struct net_bridge *br,
> >                                           struct net_bridge_port *port,
> > -                                         __be32 saddr)
> > +                                         __be32 saddr,
> > +                                         struct bridge_mcast_own_query *own,
> > +                                         struct bridge_mcast_other_query *other)
> >  {
> > -     if (!timer_pending(&br->ip4_own_query.timer) &&
> > -         !timer_pending(&br->ip4_other_query.timer))
> > +     if (own && !timer_pending(&own->timer) &&
> > +         !timer_pending(&other->timer))
> >               goto update;
> >
> >       if (!br->ip4_querier.addr.src.ip4)
> > @@ -2533,11 +2724,14 @@ static bool br_ip6_multicast_select_querier(struct net_bridge *br,
> >
> >  static bool br_multicast_select_querier(struct net_bridge *br,
> >                                       struct net_bridge_port *port,
> > -                                     struct br_ip *saddr)
> > +                                     struct br_ip *saddr,
> > +                                     struct bridge_mcast_own_query *query,
> > +                                     struct bridge_mcast_other_query *other_query)
> >  {
> >       switch (saddr->proto) {
> >       case htons(ETH_P_IP):
> > -             return br_ip4_multicast_select_querier(br, port, saddr->src.ip4);
> > +             return br_ip4_multicast_select_querier(br, port, saddr->src.ip4,
> > +                                                    query, other_query);
> >  #if IS_ENABLED(CONFIG_IPV6)
> >       case htons(ETH_P_IPV6):
> >               return br_ip6_multicast_select_querier(br, port, &saddr->src.ip6);
> > @@ -2628,9 +2822,10 @@ static void br_multicast_query_received(struct net_bridge *br,
> >                                       struct net_bridge_port *port,
> >                                       struct bridge_mcast_other_query *query,
> >                                       struct br_ip *saddr,
> > -                                     unsigned long max_delay)
> > +                                     unsigned long max_delay,
> > +                                     struct bridge_mcast_own_query *own_query)
> >  {
> > -     if (!br_multicast_select_querier(br, port, saddr))
> > +     if (!br_multicast_select_querier(br, port, saddr, own_query, query))
> >               return;
> >
> >       br_multicast_update_query_timer(br, query, max_delay);
> > @@ -2643,6 +2838,8 @@ static void br_ip4_multicast_query(struct net_bridge *br,
> >                                  u16 vid)
> >  {
> >       unsigned int transport_len = ip_transport_len(skb);
> > +     struct bridge_mcast_other_query *other_query;
> > +     struct bridge_mcast_own_query *own_query;
> >       const struct iphdr *iph = ip_hdr(skb);
> >       struct igmphdr *ih = igmp_hdr(skb);
> >       struct net_bridge_mdb_entry *mp;
> > @@ -2684,8 +2881,13 @@ static void br_ip4_multicast_query(struct net_bridge *br,
> >               saddr.proto = htons(ETH_P_IP);
> >               saddr.src.ip4 = iph->saddr;
> >
> > -             br_multicast_query_received(br, port, &br->ip4_other_query,
> > -                                         &saddr, max_delay);
> > +             br_mcast_add_queries(br, vid);
> > +
> > +             own_query = br_mcast_find_own_query(&br->ip4_own_queries, vid);
> > +             other_query = br_mcast_find_other_query(&br->ip4_other_queries,
> > +                                                     vid);
> > +             br_multicast_query_received(br, port, other_query, &saddr,
> > +                                         max_delay, own_query);
> >               goto out;
> >       }
> >
> > @@ -2773,7 +2975,7 @@ static int br_ip6_multicast_query(struct net_bridge *br,
> >               saddr.src.ip6 = ipv6_hdr(skb)->saddr;
> >
> >               br_multicast_query_received(br, port, &br->ip6_other_query,
> > -                                         &saddr, max_delay);
> > +                                         &saddr, max_delay, NULL);
> >               goto out;
> >       } else if (!group) {
> >               goto out;
> > @@ -2850,7 +3052,7 @@ br_multicast_leave_group(struct net_bridge *br,
> >       if (timer_pending(&other_query->timer))
> >               goto out;
> >
> > -     if (br_opt_get(br, BROPT_MULTICAST_QUERIER)) {
> > +     if (own_query && own_query->enabled) {
> >               __br_multicast_send_query(br, port, NULL, NULL, &mp->addr,
> >                                         false, 0, NULL);
> >
> > @@ -2916,21 +3118,26 @@ static void br_ip4_multicast_leave_group(struct net_bridge *br,
> >                                        __u16 vid,
> >                                        const unsigned char *src)
> >  {
> > -     struct br_ip br_group;
> > +     struct bridge_mcast_other_query *other_query;
> >       struct bridge_mcast_own_query *own_query;
> > +     struct br_ip br_group;
> >
> >       if (ipv4_is_local_multicast(group))
> >               return;
> >
> > -     own_query = port ? &port->ip4_own_query : &br->ip4_own_query;
> > +     if (port)
> > +             own_query = br_mcast_find_own_query(&port->ip4_own_queries, vid);
> > +     else
> > +             own_query = br_mcast_find_own_query(&br->ip4_own_queries, vid);
> >
> >       memset(&br_group, 0, sizeof(br_group));
> >       br_group.dst.ip4 = group;
> >       br_group.proto = htons(ETH_P_IP);
> >       br_group.vid = vid;
> >
> > -     br_multicast_leave_group(br, port, &br_group, &br->ip4_other_query,
> > -                              own_query, src);
> > +     other_query = br_mcast_find_other_query(&br->ip4_other_queries, vid);
> > +     br_multicast_leave_group(br, port, &br_group, other_query, own_query,
> > +                              src);
> >  }
> >
> >  #if IS_ENABLED(CONFIG_IPV6)
> > @@ -3195,9 +3402,10 @@ static void br_multicast_query_expired(struct net_bridge *br,
> >
> >  static void br_ip4_multicast_query_expired(struct timer_list *t)
> >  {
> > -     struct net_bridge *br = from_timer(br, t, ip4_own_query.timer);
> > +     struct bridge_mcast_own_query *query = from_timer(query, t, timer);
> > +     struct net_bridge *br = query->br;
> >
> > -     br_multicast_query_expired(br, &br->ip4_own_query, &br->ip4_querier);
> > +     br_multicast_query_expired(br, query, &br->ip4_querier);
> >  }
> >
> >  #if IS_ENABLED(CONFIG_IPV6)
> > @@ -3237,7 +3445,6 @@ void br_multicast_init(struct net_bridge *br)
> >       br->multicast_querier_interval = 255 * HZ;
> >       br->multicast_membership_interval = 260 * HZ;
> >
> > -     br->ip4_other_query.delay_time = 0;
> >       br->ip4_querier.port = NULL;
> >       br->multicast_igmp_version = 2;
> >  #if IS_ENABLED(CONFIG_IPV6)
> > @@ -3251,10 +3458,8 @@ void br_multicast_init(struct net_bridge *br)
> >       spin_lock_init(&br->multicast_lock);
> >       timer_setup(&br->multicast_router_timer,
> >                   br_multicast_local_router_expired, 0);
> > -     timer_setup(&br->ip4_other_query.timer,
> > -                 br_ip4_multicast_querier_expired, 0);
> > -     timer_setup(&br->ip4_own_query.timer,
> > -                 br_ip4_multicast_query_expired, 0);
> > +     INIT_LIST_HEAD(&br->ip4_other_queries);
> > +     INIT_LIST_HEAD(&br->ip4_own_queries);
> >  #if IS_ENABLED(CONFIG_IPV6)
> >       timer_setup(&br->ip6_other_query.timer,
> >                   br_ip6_multicast_querier_expired, 0);
> > @@ -3341,7 +3546,10 @@ static void __br_multicast_open(struct net_bridge *br,
> >
> >  void br_multicast_open(struct net_bridge *br)
> >  {
> > -     __br_multicast_open(br, &br->ip4_own_query);
> > +     struct bridge_mcast_own_query *query;
> > +
> > +     list_for_each_entry(query, &br->ip4_own_queries, list)
> > +             __br_multicast_open(br, query);
> >  #if IS_ENABLED(CONFIG_IPV6)
> >       __br_multicast_open(br, &br->ip6_own_query);
> >  #endif
> > @@ -3349,9 +3557,14 @@ void br_multicast_open(struct net_bridge *br)
> >
> >  void br_multicast_stop(struct net_bridge *br)
> >  {
> > +     struct bridge_mcast_other_query *other_query;
> > +     struct bridge_mcast_own_query *query;
> > +
> >       del_timer_sync(&br->multicast_router_timer);
> > -     del_timer_sync(&br->ip4_other_query.timer);
> > -     del_timer_sync(&br->ip4_own_query.timer);
> > +     list_for_each_entry(other_query, &br->ip4_other_queries, list)
> > +             del_timer_sync(&other_query->timer);
> > +     list_for_each_entry(query, &br->ip4_own_queries, list)
> > +             del_timer_sync(&query->timer);
> >  #if IS_ENABLED(CONFIG_IPV6)
> >       del_timer_sync(&br->ip6_other_query.timer);
> >       del_timer_sync(&br->ip6_own_query.timer);
> > @@ -3461,11 +3674,20 @@ int br_multicast_set_port_router(struct net_bridge_port *p, unsigned long val)
> >  }
> >
> >  static void br_multicast_start_querier(struct net_bridge *br,
> > -                                    struct bridge_mcast_own_query *query)
> > +                                    struct bridge_mcast_own_query *query,
> > +                                    u16 vid)
> >  {
> > +     struct bridge_mcast_own_query *port_query;
> > +     struct net_bridge_vlan_group *vg;
> >       struct net_bridge_port *port;
> >
> > -     __br_multicast_open(br, query);
> > +     if (br_vlan_enabled(br->dev)) {
> > +             vg = br_vlan_group(br);
> > +             if (vg && br_vlan_find(vg, vid))
> > +                     __br_multicast_open(br, query);
> > +     } else {
> > +             __br_multicast_open(br, query);
> > +     }
> >
> >       rcu_read_lock();
> >       list_for_each_entry_rcu(port, &br->port_list, list) {
> > @@ -3473,11 +3695,66 @@ static void br_multicast_start_querier(struct net_bridge *br,
> >                   port->state == BR_STATE_BLOCKING)
> >                       continue;
> >
> > -             if (query == &br->ip4_own_query)
> > -                     br_multicast_enable(&port->ip4_own_query);
> > +             if (br_vlan_enabled(br->dev)) {
> > +                     vg = nbp_vlan_group(port);
> > +                     if (!vg || (vg && !br_vlan_find(vg, vid)))
> > +                             continue;
> > +             }
> > +
> > +             port_query = br_mcast_find_own_query(&port->ip4_own_queries,
> > +                                                  vid);
> > +             if (!port_query)
> > +                     continue;
> > +
> > +             port_query->enabled = true;
> > +
> > +             if (query->ip4) {
> > +                     port_query->ip4 = true;
> > +                     br_multicast_enable(port_query);
> > +             }
> >  #if IS_ENABLED(CONFIG_IPV6)
> > -             else
> > +             else {
> >                       br_multicast_enable(&port->ip6_own_query);
> > +             }
> > +#endif
> > +     }
> > +     rcu_read_unlock();
> > +}
> > +
> > +static void br_multicast_stop_querier(struct net_bridge *br,
> > +                                   struct bridge_mcast_own_query *query,
> > +                                   u16 vid)
> > +{
> > +     struct bridge_mcast_own_query *port_query;
> > +     struct net_bridge_vlan_group *vg;
> > +     struct net_bridge_port *port;
> > +
> > +     query->enabled = false;
> > +
> > +     rcu_read_lock();
> > +     list_for_each_entry_rcu(port, &br->port_list, list) {
> > +             if (port->state == BR_STATE_DISABLED ||
> > +                 port->state == BR_STATE_BLOCKING)
> > +                     continue;
> > +
> > +             if (br_vlan_enabled(br->dev)) {
> > +                     vg = nbp_vlan_group(port);
> > +                     if (!vg || (vg && !br_vlan_find(vg, vid)))
> > +                             continue;
> > +             }
> > +
> > +             port_query = br_mcast_find_own_query(&port->ip4_own_queries,
> > +                                                  vid);
> > +             if (!port_query)
> > +                     continue;
> > +
> > +             port_query->enabled = false;
> > +
> > +             if (query->ip4)
> > +                     br_multicast_disable(port_query);
> > +#if IS_ENABLED(CONFIG_IPV6)
> > +             else
> > +                     br_multicast_disable(&port->ip6_own_query);
> >  #endif
> >       }
> >       rcu_read_unlock();
> > @@ -3553,32 +3830,55 @@ bool br_multicast_router(const struct net_device *dev)
> >  }
> >  EXPORT_SYMBOL_GPL(br_multicast_router);
> >
> > -int br_multicast_set_querier(struct net_bridge *br, unsigned long val)
> > +int br_multicast_set_querier(struct net_bridge *br, unsigned long val, u16 vid)
> >  {
> > +     struct bridge_mcast_other_query *other_query;
> > +     struct bridge_mcast_own_query *query;
> > +     struct net_bridge_vlan_group *vg;
> >       unsigned long max_delay;
> >
> >       val = !!val;
> >
> > +     if (vid == 0) {
> > +             vg = br_vlan_group(br);
> > +             if (vg)
> > +                     vid = vg->pvid;
> > +     }
> > +
> >       spin_lock_bh(&br->multicast_lock);
> > -     if (br_opt_get(br, BROPT_MULTICAST_QUERIER) == val)
> > +     query = br_mcast_find_own_query(&br->ip4_own_queries, vid);
> > +     if (!query) {
> > +             if (br_vlan_enabled(br->dev))
> > +                     goto unlock;
> > +
> > +             br_mcast_add_queries(br, vid);
> > +     }
> > +
> > +     other_query = br_mcast_find_other_query(&br->ip4_other_queries, vid);
> > +     if (!other_query)
> >               goto unlock;
> >
> > -     br_opt_toggle(br, BROPT_MULTICAST_QUERIER, !!val);
> > -     if (!val)
> > +     if (!val && query) {
> > +             br_multicast_stop_querier(br, query, vid);
> >               goto unlock;
> > +     }
> >
> > -     max_delay = br->multicast_query_response_interval;
> > +     if (val & query->enabled)
> > +             goto unlock;
> >
> > -     if (!timer_pending(&br->ip4_other_query.timer))
> > -             br->ip4_other_query.delay_time = jiffies + max_delay;
> > +     query->enabled = true;
> >
> > -     br_multicast_start_querier(br, &br->ip4_own_query);
> > +     max_delay = br->multicast_query_response_interval;
> > +     if (!timer_pending(&other_query->timer))
> > +             other_query->delay_time = jiffies + max_delay;
> > +
> > +     br_multicast_start_querier(br, query, vid);
> >
> >  #if IS_ENABLED(CONFIG_IPV6)
> >       if (!timer_pending(&br->ip6_other_query.timer))
> >               br->ip6_other_query.delay_time = jiffies + max_delay;
> >
> > -     br_multicast_start_querier(br, &br->ip6_own_query);
> > +     br_multicast_start_querier(br, &br->ip6_own_query, vid);
> >  #endif
> >
> >  unlock:
> > @@ -3587,6 +3887,79 @@ int br_multicast_set_querier(struct net_bridge *br, unsigned long val)
> >       return 0;
> >  }
> >
> > +void br_multicast_vlan_add(struct net_bridge_vlan *v)
> > +{
> > +     struct bridge_mcast_own_query *query, *port_query;
> > +     struct net_bridge_port *p;
> > +     struct net_bridge *br;
> > +
> > +     if (br_vlan_is_master(v)) {
> > +             br_mcast_add_queries(v->br, v->vid);
> > +             return;
> > +     }
> > +
> > +     p = v->port;
> > +     br = p->br;
> > +
> > +     query = br_mcast_find_own_query(&br->ip4_own_queries, v->vid);
> > +
> > +     port_query = br_mcast_add_own_query(&p->ip4_own_queries,
> > +                                         v->vid,
> > +                                         br_ip4_multicast_port_query_expired);
> > +     if (!port_query)
> > +             return;
> > +
> > +     port_query->port = p;
> > +     port_query->ip4 = true;
> > +
> > +     if (query->enabled) {
> > +             port_query->enabled = true;
> > +             br_multicast_enable(port_query);
> > +     }
> > +}
> > +
> > +void br_multicast_vlan_del(struct net_bridge_vlan *v)
> > +{
> > +     struct bridge_mcast_other_query *other_query, *other_tmp;
> > +     struct bridge_mcast_own_query *query, *tmp;
> > +     struct net_bridge_port *p;
> > +     struct net_bridge *br;
> > +
> > +     if (br_vlan_is_master(v)) {
> > +             br = v->br;
> > +
> > +             list_for_each_entry_safe(other_query, other_tmp,
> > +                                      &br->ip4_other_queries, list)
> > +                     if (other_query->vid == v->vid)
> > +                             br_mcast_del_other_query(other_query);
> > +
> > +             list_for_each_entry_safe(query, tmp, &br->ip4_own_queries, list)
> > +                     if (query->vid == v->vid)
> > +                             br_mcast_del_own_query(query);
> > +
> > +             return;
> > +     }
> > +
> > +     p = v->port;
> > +
> > +     list_for_each_entry_safe(query, tmp, &p->ip4_own_queries, list) {
> > +             if (query->vid == v->vid)
> > +                     br_mcast_del_own_query(query);
> > +     }
> > +}
> > +
> > +void br_multicast_vlan_toggle(struct net_bridge *br, bool on)
> > +{
> > +     struct bridge_mcast_own_query *query;
> > +
> > +     list_for_each_entry(query, &br->ip4_own_queries, list) {
> > +             if (!on)
> > +                     br_multicast_stop_querier(br, query, query->vid);
> > +             else
> > +                     br_multicast_start_querier(br, query, query->vid);
> > +     }
> > +}
> > +
> >  int br_multicast_set_igmp_version(struct net_bridge *br, unsigned long val)
> >  {
> >       /* Currently we support only version 2 and 3 */
> > @@ -3711,7 +4084,7 @@ bool br_multicast_has_querier_anywhere(struct net_device *dev, int proto)
> >       memset(&eth, 0, sizeof(eth));
> >       eth.h_proto = htons(proto);
> >
> > -     ret = br_multicast_querier_exists(br, &eth, NULL);
> > +     ret = br_multicast_any_querier_exists(br, &eth);
> >
> >  unlock:
> >       rcu_read_unlock();
> > @@ -3746,7 +4119,7 @@ bool br_multicast_has_querier_adjacent(struct net_device *dev, int proto)
> >
> >       switch (proto) {
> >       case ETH_P_IP:
> > -             if (!timer_pending(&br->ip4_other_query.timer) ||
> > +             if (!br_multicast_any_querier_adjacent(br) ||
> >                   rcu_dereference(br->ip4_querier.port) == port)
> >                       goto unlock;
> >               break;
> > diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
> > index 49700ce0e919..d32f4c185364 100644
> > --- a/net/bridge/br_netlink.c
> > +++ b/net/bridge/br_netlink.c
> > @@ -1186,6 +1186,7 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = {
> >       [IFLA_BR_VLAN_STATS_PER_PORT] = { .type = NLA_U8 },
> >       [IFLA_BR_MULTI_BOOLOPT] =
> >               NLA_POLICY_EXACT_LEN(sizeof(struct br_boolopt_multi)),
> > +     [IFLA_BR_MCAST_QUERIER_VID] = { .type = NLA_U16 },
> >  };
> >
> >  static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
> > @@ -1193,6 +1194,7 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
> >                        struct netlink_ext_ack *extack)
> >  {
> >       struct net_bridge *br = netdev_priv(brdev);
> > +     u16 vid = 0;
> >       int err;
> >
> >       if (!data)
> > @@ -1204,6 +1206,9 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
> >                       return err;
> >       }
> >
> > +     if (data[IFLA_BR_MCAST_QUERIER_VID])
> > +             vid = nla_get_u16(data[IFLA_BR_MCAST_QUERIER_VID]);
> > +
> >       if (data[IFLA_BR_HELLO_TIME]) {
> >               err = br_set_hello_time(br, nla_get_u32(data[IFLA_BR_HELLO_TIME]));
> >               if (err)
> > @@ -1333,7 +1338,7 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
> >       if (data[IFLA_BR_MCAST_QUERIER]) {
> >               u8 mcast_querier = nla_get_u8(data[IFLA_BR_MCAST_QUERIER]);
> >
> > -             err = br_multicast_set_querier(br, mcast_querier);
> > +             err = br_multicast_set_querier(br, mcast_querier, vid);
> >               if (err)
> >                       return err;
> >       }
> > @@ -1596,7 +1601,7 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
> >           nla_put_u8(skb, IFLA_BR_MCAST_QUERY_USE_IFADDR,
> >                      br_opt_get(br, BROPT_MULTICAST_QUERY_USE_IFADDR)) ||
> >           nla_put_u8(skb, IFLA_BR_MCAST_QUERIER,
> > -                    br_opt_get(br, BROPT_MULTICAST_QUERIER)) ||
> > +                    br_mcast_exist_own_query(br)) ||
> >           nla_put_u8(skb, IFLA_BR_MCAST_STATS_ENABLED,
> >                      br_opt_get(br, BROPT_MULTICAST_STATS_ENABLED)) ||
> >           nla_put_u32(skb, IFLA_BR_MCAST_HASH_ELASTICITY, RHT_ELASTICITY) ||
> > diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> > index d62c6e1af64a..84f597f542b1 100644
> > --- a/net/bridge/br_private.h
> > +++ b/net/bridge/br_private.h
> > @@ -66,14 +66,24 @@ struct mac_addr {
> >  #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
> >  /* our own querier */
> >  struct bridge_mcast_own_query {
> > +     struct list_head        list;
> >       struct timer_list       timer;
> >       u32                     startup_sent;
> > +     struct net_bridge_port  *port;
> > +     struct net_bridge       *br;
> > +     bool                    ip4;
> > +     u16                     vid;
> > +     bool                    enabled;
> >  };
> >
> >  /* other querier */
> >  struct bridge_mcast_other_query {
> > +     struct list_head                list;
> >       struct timer_list               timer;
> >       unsigned long                   delay_time;
> > +     struct net_bridge               *br;
> > +     bool                            ip4;
> > +     u16                             vid;
> >  };
> >
> >  /* selected querier */
> > @@ -304,7 +314,7 @@ struct net_bridge_port {
> >       struct rcu_head                 rcu;
> >
> >  #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
> > -     struct bridge_mcast_own_query   ip4_own_query;
> > +     struct list_head                ip4_own_queries;
> >  #if IS_ENABLED(CONFIG_IPV6)
> >       struct bridge_mcast_own_query   ip6_own_query;
> >  #endif /* IS_ENABLED(CONFIG_IPV6) */
> > @@ -448,8 +458,8 @@ struct net_bridge {
> >       struct hlist_head               router_list;
> >
> >       struct timer_list               multicast_router_timer;
> > -     struct bridge_mcast_other_query ip4_other_query;
> > -     struct bridge_mcast_own_query   ip4_own_query;
> > +     struct list_head                ip4_other_queries;
> > +     struct list_head                ip4_own_queries;
> >       struct bridge_mcast_querier     ip4_querier;
> >       struct bridge_mcast_stats       __percpu *mcast_stats;
> >  #if IS_ENABLED(CONFIG_IPV6)
> > @@ -788,6 +798,9 @@ int br_ioctl_deviceless_stub(struct net *net, unsigned int cmd,
> >
> >  /* br_multicast.c */
> >  #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
> > +void br_multicast_vlan_add(struct net_bridge_vlan *v);
> > +void br_multicast_vlan_del(struct net_bridge_vlan *v);
> > +void br_multicast_vlan_toggle(struct net_bridge *br, bool on);
> >  int br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port,
> >                    struct sk_buff *skb, u16 vid);
> >  struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br,
> > @@ -807,7 +820,7 @@ void br_multicast_flood(struct net_bridge_mdb_entry *mdst,
> >  int br_multicast_set_router(struct net_bridge *br, unsigned long val);
> >  int br_multicast_set_port_router(struct net_bridge_port *p, unsigned long val);
> >  int br_multicast_toggle(struct net_bridge *br, unsigned long val);
> > -int br_multicast_set_querier(struct net_bridge *br, unsigned long val);
> > +int br_multicast_set_querier(struct net_bridge *br, unsigned long val, u16 vid);
> >  int br_multicast_set_hash_max(struct net_bridge *br, unsigned long val);
> >  int br_multicast_set_igmp_version(struct net_bridge *br, unsigned long val);
> >  #if IS_ENABLED(CONFIG_IPV6)
> > @@ -846,6 +859,11 @@ void br_multicast_star_g_handle_mode(struct net_bridge_port_group *pg,
> >                                    u8 filter_mode);
> >  void br_multicast_sg_add_exclude_ports(struct net_bridge_mdb_entry *star_mp,
> >                                      struct net_bridge_port_group *sg);
> > +struct bridge_mcast_other_query *
> > +br_mcast_find_other_query(struct list_head *list, u16 vid);
> > +struct bridge_mcast_own_query *
> > +br_mcast_find_own_query(struct list_head *list, u16 vid);
> > +bool br_mcast_exist_own_query(struct net_bridge *br);
> >
> >  static inline bool br_group_is_l2(const struct br_ip *group)
> >  {
> > @@ -865,11 +883,15 @@ static inline bool br_multicast_is_router(struct net_bridge *br)
> >  static inline bool
> >  __br_multicast_querier_exists(struct net_bridge *br,
> >                               struct bridge_mcast_other_query *querier,
> > -                             const bool is_ipv6)
> > +                             const bool is_ipv6,
> > +                             u16 vid)
> >  {
> > +     struct bridge_mcast_own_query *query;
> >       bool own_querier_enabled;
> >
> > -     if (br_opt_get(br, BROPT_MULTICAST_QUERIER)) {
> > +     query = br_mcast_find_own_query(&br->ip4_own_queries, vid);
> > +
> > +     if (query && query->enabled) {
> >               if (is_ipv6 && !br_opt_get(br, BROPT_HAS_IPV6_ADDR))
> >                       own_querier_enabled = false;
> >               else
> > @@ -878,28 +900,62 @@ __br_multicast_querier_exists(struct net_bridge *br,
> >               own_querier_enabled = false;
> >       }
> >
> > +     if (!querier)
> > +             return own_querier_enabled;
> > +
> >       return time_is_before_jiffies(querier->delay_time) &&
> >              (own_querier_enabled || timer_pending(&querier->timer));
> >  }
> >
> >  static inline bool br_multicast_querier_exists(struct net_bridge *br,
> >                                              struct ethhdr *eth,
> > -                                            const struct net_bridge_mdb_entry *mdb)
> > +                                            const struct net_bridge_mdb_entry *mdb,
> > +                                            u16 vid)
> >  {
> > +     struct bridge_mcast_other_query *query =
> > +             br_mcast_find_other_query(&br->ip4_other_queries, vid);
> > +
> >       switch (eth->h_proto) {
> >       case (htons(ETH_P_IP)):
> > -             return __br_multicast_querier_exists(br,
> > -                     &br->ip4_other_query, false);
> > +             return __br_multicast_querier_exists(br, query, false, vid);
> >  #if IS_ENABLED(CONFIG_IPV6)
> >       case (htons(ETH_P_IPV6)):
> >               return __br_multicast_querier_exists(br,
> > -                     &br->ip6_other_query, true);
> > +                     &br->ip6_other_query, true, vid);
> >  #endif
> >       default:
> >               return !!mdb && br_group_is_l2(&mdb->addr);
> >       }
> >  }
> >
> > +static inline bool br_multicast_any_querier_exists(struct net_bridge *br,
> > +                                                struct ethhdr *eth)
> > +{
> > +     struct bridge_mcast_other_query *query;
> > +
> > +     list_for_each_entry(query, &br->ip4_other_queries, list) {
> > +             if (!timer_pending(&query->timer))
> > +                     continue;
> > +
> > +             if (br_multicast_querier_exists(br, eth, NULL, query->vid))
> > +                     return true;
> > +     }
> > +
> > +     return false;
> > +}
> > +
> > +static inline bool br_multicast_any_querier_adjacent(struct net_bridge *br)
> > +{
> > +     struct bridge_mcast_other_query *query;
> > +
> > +     list_for_each_entry(query, &br->ip4_other_queries, list) {
> > +             if (timer_pending(&query->timer))
> > +                     return true;
> > +     }
> > +
> > +     return false;
> > +}
> > +
> >  static inline bool br_multicast_is_star_g(const struct br_ip *ip)
> >  {
> >       switch (ip->proto) {
> > @@ -1015,7 +1071,19 @@ static inline bool br_multicast_is_router(struct net_bridge *br)
> >
> >  static inline bool br_multicast_querier_exists(struct net_bridge *br,
> >                                              struct ethhdr *eth,
> > -                                            const struct net_bridge_mdb_entry *mdb)
> > +                                            const struct net_bridge_mdb_entry *mdb,
> > +                                            u16 vid)
> > +{
> > +     return false;
> > +}
> > +
> > +static inline bool br_multicast_any_querier_exists(struct net_bridge *br,
> > +                                                struct ethhdr *eth)
> > +{
> > +     return false;
> > +}
> > +
> > +static inline bool br_multicast_any_querier_adjacent(struct net_bridge *br)
> >  {
> >       return false;
> >  }
> > diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c
> > index 7db06e3f642a..23bf6a065d78 100644
> > --- a/net/bridge/br_sysfs_br.c
> > +++ b/net/bridge/br_sysfs_br.c
> > @@ -51,6 +51,33 @@ static ssize_t store_bridge_parm(struct device *d,
> >       return err ? err : len;
> >  }
> >
> > +static ssize_t store_bridge_parm2(struct device *d,
> > +                               const char *buf, size_t len,
> > +                               int (*set)(struct net_bridge *, unsigned long, u16))
> > +{
> > +     struct net_bridge *br = to_bridge(d);
> > +     char *endp;
> > +     unsigned long val;
> > +     int err;
> > +
> > +     if (!ns_capable(dev_net(br->dev)->user_ns, CAP_NET_ADMIN))
> > +             return -EPERM;
> > +
> > +     val = simple_strtoul(buf, &endp, 0);
> > +     if (endp == buf)
> > +             return -EINVAL;
> > +
> > +     if (!rtnl_trylock())
> > +             return restart_syscall();
> > +
> > +     err = (*set)(br, val, 0);
> > +     if (!err)
> > +             netdev_state_change(br->dev);
> > +     rtnl_unlock();
> > +
> > +     return err ? err : len;
> > +}
> > +
> >
> >  static ssize_t forward_delay_show(struct device *d,
> >                                 struct device_attribute *attr, char *buf)
> > @@ -404,14 +431,14 @@ static ssize_t multicast_querier_show(struct device *d,
> >                                     char *buf)
> >  {
> >       struct net_bridge *br = to_bridge(d);
> > -     return sprintf(buf, "%d\n", br_opt_get(br, BROPT_MULTICAST_QUERIER));
> > +     return sprintf(buf, "%d\n", br_mcast_exist_own_query(br));
> >  }
> >
> >  static ssize_t multicast_querier_store(struct device *d,
> >                                      struct device_attribute *attr,
> >                                      const char *buf, size_t len)
> >  {
> > -     return store_bridge_parm(d, buf, len, br_multicast_set_querier);
> > +     return store_bridge_parm2(d, buf, len, br_multicast_set_querier);
> >  }
> >  static DEVICE_ATTR_RW(multicast_querier);
> >
> > diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
> > index 701cad646b20..2e0b544a3560 100644
> > --- a/net/bridge/br_vlan.c
> > +++ b/net/bridge/br_vlan.c
> > @@ -308,6 +308,7 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags,
> >
> >       __vlan_add_list(v);
> >       __vlan_add_flags(v, flags);
> > +     br_multicast_vlan_add(v);
> >
> >       if (p)
> >               nbp_vlan_set_vlan_dev_state(p, v->vid);
> > @@ -353,6 +354,7 @@ static int __vlan_del(struct net_bridge_vlan *v)
> >               masterv = v->brvlan;
> >       }
> >
> > +     br_multicast_vlan_del(v);
> >       __vlan_delete_pvid(vg, v->vid);
> >       if (p) {
> >               err = __vlan_vid_del(p->dev, p->br, v);
> > @@ -827,6 +829,7 @@ int __br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
> >       br_manage_promisc(br);
> >       recalculate_group_addr(br);
> >       br_recalculate_fwd_mask(br);
> > +     br_multicast_vlan_toggle(br, !!val);
> >
> >       return 0;
> >  }
> >
> 

-- 
/Horatiu


More information about the Bridge mailing list