Merge tag 'linux-kselftest-kunit-5.15-rc1' of git://git.kernel.org/pub/scm/linux...
[linux-2.6-microblaze.git] / net / bridge / br_vlan.c
index a08e9f1..19f65ab 100644 (file)
@@ -190,6 +190,8 @@ static void br_vlan_put_master(struct net_bridge_vlan *masterv)
                rhashtable_remove_fast(&vg->vlan_hash,
                                       &masterv->vnode, br_vlan_rht_params);
                __vlan_del_list(masterv);
+               br_multicast_toggle_one_vlan(masterv, false);
+               br_multicast_ctx_deinit(&masterv->br_mcast_ctx);
                call_rcu(&masterv->rcu, br_master_vlan_rcu_free);
        }
 }
@@ -280,10 +282,13 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags,
                } else {
                        v->stats = masterv->stats;
                }
+               br_multicast_port_ctx_init(p, v, &v->port_mcast_ctx);
        } else {
                err = br_switchdev_port_vlan_add(dev, v->vid, flags, extack);
                if (err && err != -EOPNOTSUPP)
                        goto out;
+               br_multicast_ctx_init(br, v, &v->br_mcast_ctx);
+               v->priv_flags |= BR_VLFLAG_GLOBAL_MCAST_ENABLED;
        }
 
        /* Add the dev mac and count the vlan only if it's usable */
@@ -306,6 +311,7 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags,
 
        __vlan_add_list(v);
        __vlan_add_flags(v, flags);
+       br_multicast_toggle_one_vlan(v, true);
 
        if (p)
                nbp_vlan_set_vlan_dev_state(p, v->vid);
@@ -374,6 +380,8 @@ static int __vlan_del(struct net_bridge_vlan *v)
                                       br_vlan_rht_params);
                __vlan_del_list(v);
                nbp_vlan_set_vlan_dev_state(p, v->vid);
+               br_multicast_toggle_one_vlan(v, false);
+               br_multicast_port_ctx_deinit(&v->port_mcast_ctx);
                call_rcu(&v->rcu, nbp_vlan_rcu_free);
        }
 
@@ -457,7 +465,15 @@ struct sk_buff *br_handle_vlan(struct net_bridge *br,
                u64_stats_update_end(&stats->syncp);
        }
 
-       if (v->flags & BRIDGE_VLAN_INFO_UNTAGGED)
+       /* If the skb will be sent using forwarding offload, the assumption is
+        * that the switchdev will inject the packet into hardware together
+        * with the bridge VLAN, so that it can be forwarded according to that
+        * VLAN. The switchdev should deal with popping the VLAN header in
+        * hardware on each egress port as appropriate. So only strip the VLAN
+        * header if forwarding offload is not being used.
+        */
+       if (v->flags & BRIDGE_VLAN_INFO_UNTAGGED &&
+           !br_switchdev_frame_uses_tx_fwd_offload(skb))
                __vlan_hwaccel_clear_tag(skb);
 
        if (p && (p->flags & BR_VLAN_TUNNEL) &&
@@ -473,7 +489,8 @@ out:
 static bool __allowed_ingress(const struct net_bridge *br,
                              struct net_bridge_vlan_group *vg,
                              struct sk_buff *skb, u16 *vid,
-                             u8 *state)
+                             u8 *state,
+                             struct net_bridge_vlan **vlan)
 {
        struct pcpu_sw_netstats *stats;
        struct net_bridge_vlan *v;
@@ -538,8 +555,9 @@ static bool __allowed_ingress(const struct net_bridge *br,
                         */
                        skb->vlan_tci |= pvid;
 
-               /* if stats are disabled we can avoid the lookup */
-               if (!br_opt_get(br, BROPT_VLAN_STATS_ENABLED)) {
+               /* if snooping and stats are disabled we can avoid the lookup */
+               if (!br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED) &&
+                   !br_opt_get(br, BROPT_VLAN_STATS_ENABLED)) {
                        if (*state == BR_STATE_FORWARDING) {
                                *state = br_vlan_get_pvid_state(vg);
                                return br_vlan_state_allowed(*state, true);
@@ -566,6 +584,8 @@ static bool __allowed_ingress(const struct net_bridge *br,
                u64_stats_update_end(&stats->syncp);
        }
 
+       *vlan = v;
+
        return true;
 
 drop:
@@ -575,17 +595,19 @@ drop:
 
 bool br_allowed_ingress(const struct net_bridge *br,
                        struct net_bridge_vlan_group *vg, struct sk_buff *skb,
-                       u16 *vid, u8 *state)
+                       u16 *vid, u8 *state,
+                       struct net_bridge_vlan **vlan)
 {
        /* If VLAN filtering is disabled on the bridge, all packets are
         * permitted.
         */
+       *vlan = NULL;
        if (!br_opt_get(br, BROPT_VLAN_ENABLED)) {
                BR_INPUT_SKB_CB(skb)->vlan_filtered = false;
                return true;
        }
 
-       return __allowed_ingress(br, vg, skb, vid, state);
+       return __allowed_ingress(br, vg, skb, vid, state, vlan);
 }
 
 /* Called under RCU. */
@@ -672,6 +694,7 @@ static int br_vlan_add_existing(struct net_bridge *br,
                vlan->flags |= BRIDGE_VLAN_INFO_BRENTRY;
                vg->num_vlans++;
                *changed = true;
+               br_multicast_toggle_one_vlan(vlan, true);
        }
 
        if (__vlan_add_flags(vlan, flags))
@@ -818,14 +841,21 @@ int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val,
        if (br_opt_get(br, BROPT_VLAN_ENABLED) == !!val)
                return 0;
 
+       br_opt_toggle(br, BROPT_VLAN_ENABLED, !!val);
+
        err = switchdev_port_attr_set(br->dev, &attr, extack);
-       if (err && err != -EOPNOTSUPP)
+       if (err && err != -EOPNOTSUPP) {
+               br_opt_toggle(br, BROPT_VLAN_ENABLED, !val);
                return err;
+       }
 
-       br_opt_toggle(br, BROPT_VLAN_ENABLED, !!val);
        br_manage_promisc(br);
        recalculate_group_addr(br);
        br_recalculate_fwd_mask(br);
+       if (!val && br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) {
+               br_info(br, "vlan filtering disabled, automatically disabling multicast vlan snooping\n");
+               br_multicast_toggle_vlan_snooping(br, false, NULL);
+       }
 
        return 0;
 }
@@ -1420,6 +1450,33 @@ int br_vlan_get_info(const struct net_device *dev, u16 vid,
 }
 EXPORT_SYMBOL_GPL(br_vlan_get_info);
 
+int br_vlan_get_info_rcu(const struct net_device *dev, u16 vid,
+                        struct bridge_vlan_info *p_vinfo)
+{
+       struct net_bridge_vlan_group *vg;
+       struct net_bridge_vlan *v;
+       struct net_bridge_port *p;
+
+       p = br_port_get_check_rcu(dev);
+       if (p)
+               vg = nbp_vlan_group_rcu(p);
+       else if (netif_is_bridge_master(dev))
+               vg = br_vlan_group_rcu(netdev_priv(dev));
+       else
+               return -EINVAL;
+
+       v = br_vlan_find(vg, vid);
+       if (!v)
+               return -ENOENT;
+
+       p_vinfo->vid = vid;
+       p_vinfo->flags = v->flags;
+       if (vid == br_get_pvid(vg))
+               p_vinfo->flags |= BRIDGE_VLAN_INFO_PVID;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(br_vlan_get_info_rcu);
+
 static int br_vlan_is_bind_vlan_dev(const struct net_device *dev)
 {
        return is_vlan_dev(dev) &&
@@ -1838,6 +1895,9 @@ int br_vlan_replay(struct net_device *br_dev, struct net_device *dev,
 
        ASSERT_RTNL();
 
+       if (!nb)
+               return 0;
+
        if (!netif_is_bridge_master(br_dev))
                return -EINVAL;
 
@@ -1884,7 +1944,6 @@ int br_vlan_replay(struct net_device *br_dev, struct net_device *dev,
 
        return err;
 }
-EXPORT_SYMBOL_GPL(br_vlan_replay);
 
 /* check if v_curr can enter a range ending in range_end */
 bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr,
@@ -1901,6 +1960,7 @@ static int br_vlan_dump_dev(const struct net_device *dev,
                            u32 dump_flags)
 {
        struct net_bridge_vlan *v, *range_start = NULL, *range_end = NULL;
+       bool dump_global = !!(dump_flags & BRIDGE_VLANDB_DUMPF_GLOBAL);
        bool dump_stats = !!(dump_flags & BRIDGE_VLANDB_DUMPF_STATS);
        struct net_bridge_vlan_group *vg;
        int idx = 0, s_idx = cb->args[1];
@@ -1919,6 +1979,10 @@ static int br_vlan_dump_dev(const struct net_device *dev,
                vg = br_vlan_group_rcu(br);
                p = NULL;
        } else {
+               /* global options are dumped only for bridge devices */
+               if (dump_global)
+                       return 0;
+
                p = br_port_get_rcu(dev);
                if (WARN_ON(!p))
                        return -EINVAL;
@@ -1941,7 +2005,7 @@ static int br_vlan_dump_dev(const struct net_device *dev,
 
        /* idx must stay at range's beginning until it is filled in */
        list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
-               if (!br_vlan_should_use(v))
+               if (!dump_global && !br_vlan_should_use(v))
                        continue;
                if (idx < s_idx) {
                        idx++;
@@ -1954,8 +2018,21 @@ static int br_vlan_dump_dev(const struct net_device *dev,
                        continue;
                }
 
-               if (dump_stats || v->vid == pvid ||
-                   !br_vlan_can_enter_range(v, range_end)) {
+               if (dump_global) {
+                       if (br_vlan_global_opts_can_enter_range(v, range_end))
+                               goto update_end;
+                       if (!br_vlan_global_opts_fill(skb, range_start->vid,
+                                                     range_end->vid,
+                                                     range_start)) {
+                               err = -EMSGSIZE;
+                               break;
+                       }
+                       /* advance number of filled vlans */
+                       idx += range_end->vid - range_start->vid + 1;
+
+                       range_start = v;
+               } else if (dump_stats || v->vid == pvid ||
+                          !br_vlan_can_enter_range(v, range_end)) {
                        u16 vlan_flags = br_vlan_flags(range_start, pvid);
 
                        if (!br_vlan_fill_vids(skb, range_start->vid,
@@ -1969,6 +2046,7 @@ static int br_vlan_dump_dev(const struct net_device *dev,
 
                        range_start = v;
                }
+update_end:
                range_end = v;
        }
 
@@ -1977,11 +2055,18 @@ static int br_vlan_dump_dev(const struct net_device *dev,
         * - last vlan (range_start == range_end, not in range)
         * - last vlan range (range_start != range_end, in range)
         */
-       if (!err && range_start &&
-           !br_vlan_fill_vids(skb, range_start->vid, range_end->vid,
-                              range_start, br_vlan_flags(range_start, pvid),
-                              dump_stats))
-               err = -EMSGSIZE;
+       if (!err && range_start) {
+               if (dump_global &&
+                   !br_vlan_global_opts_fill(skb, range_start->vid,
+                                             range_end->vid, range_start))
+                       err = -EMSGSIZE;
+               else if (!dump_global &&
+                        !br_vlan_fill_vids(skb, range_start->vid,
+                                           range_end->vid, range_start,
+                                           br_vlan_flags(range_start, pvid),
+                                           dump_stats))
+                       err = -EMSGSIZE;
+       }
 
        cb->args[1] = err ? idx : 0;
 
@@ -2051,6 +2136,7 @@ static const struct nla_policy br_vlan_db_policy[BRIDGE_VLANDB_ENTRY_MAX + 1] =
        [BRIDGE_VLANDB_ENTRY_RANGE]     = { .type = NLA_U16 },
        [BRIDGE_VLANDB_ENTRY_STATE]     = { .type = NLA_U8 },
        [BRIDGE_VLANDB_ENTRY_TUNNEL_INFO] = { .type = NLA_NESTED },
+       [BRIDGE_VLANDB_ENTRY_MCAST_ROUTER]      = { .type = NLA_U8 },
 };
 
 static int br_vlan_rtm_process_one(struct net_device *dev,
@@ -2185,12 +2271,22 @@ static int br_vlan_rtm_process(struct sk_buff *skb, struct nlmsghdr *nlh,
        }
 
        nlmsg_for_each_attr(attr, nlh, sizeof(*bvm), rem) {
-               if (nla_type(attr) != BRIDGE_VLANDB_ENTRY)
+               switch (nla_type(attr)) {
+               case BRIDGE_VLANDB_ENTRY:
+                       err = br_vlan_rtm_process_one(dev, attr,
+                                                     nlh->nlmsg_type,
+                                                     extack);
+                       break;
+               case BRIDGE_VLANDB_GLOBAL_OPTIONS:
+                       err = br_vlan_rtm_process_global_options(dev, attr,
+                                                                nlh->nlmsg_type,
+                                                                extack);
+                       break;
+               default:
                        continue;
+               }
 
                vlans++;
-               err = br_vlan_rtm_process_one(dev, attr, nlh->nlmsg_type,
-                                             extack);
                if (err)
                        break;
        }