]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
net: bridge: vlan: add support for dumping global vlan options
authorNikolay Aleksandrov <nikolay@nvidia.com>
Mon, 19 Jul 2021 17:06:35 +0000 (20:06 +0300)
committerDavid S. Miller <davem@davemloft.net>
Tue, 20 Jul 2021 12:41:20 +0000 (05:41 -0700)
Add a new vlan options dump flag which causes only global vlan options
to be dumped. The dumps are done only with bridge devices, ports are
ignored. They support vlan compression if the options in sequential
vlans are equal (currently always true).

Signed-off-by: Nikolay Aleksandrov <nikolay@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/uapi/linux/if_bridge.h
net/bridge/br_private.h
net/bridge/br_vlan.c
net/bridge/br_vlan_options.c

index 4ed57d1a5d890a161a0c7d2733dae3f252bdf588..946ccf33dc53dd5cc18efc807230fcc0cd1301be 100644 (file)
@@ -479,6 +479,7 @@ enum {
 
 /* flags used in BRIDGE_VLANDB_DUMP_FLAGS attribute to affect dumps */
 #define BRIDGE_VLANDB_DUMPF_STATS      (1 << 0) /* Include stats in the dump */
+#define BRIDGE_VLANDB_DUMPF_GLOBAL     (1 << 1) /* Dump global vlan options only */
 
 /* Bridge vlan RTM attributes
  * [BRIDGE_VLANDB_ENTRY] = {
index 6a6ce233a99977b74b21dfff25a9960a47ec6fa4..a19dbd63d6703dab09d52ad8b74c9dd0dcaeb164 100644 (file)
@@ -1596,6 +1596,10 @@ int br_vlan_rtm_process_global_options(struct net_device *dev,
                                       const struct nlattr *attr,
                                       int cmd,
                                       struct netlink_ext_ack *extack);
+bool br_vlan_global_opts_can_enter_range(const struct net_bridge_vlan *v_curr,
+                                        const struct net_bridge_vlan *r_end);
+bool br_vlan_global_opts_fill(struct sk_buff *skb, u16 vid, u16 vid_range,
+                             const struct net_bridge_vlan *v_opts);
 
 /* vlan state manipulation helpers using *_ONCE to annotate lock-free access */
 static inline u8 br_vlan_get_state(const struct net_bridge_vlan *v)
index dcb5acf783d22f5c0bdbb902f11a76efc38da0bc..e66b004df7637626c74e79f39332d7ca2c092d9f 100644 (file)
@@ -1919,6 +1919,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];
@@ -1937,6 +1938,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;
@@ -1959,7 +1964,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++;
@@ -1972,8 +1977,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))
+                               continue;
+                       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,
@@ -1995,11 +2013,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;
 
index a7d5a2334207321143ff57b259893737e38e74e3..f290f514054714ddc53889a74e18b770379d97e7 100644 (file)
@@ -259,6 +259,37 @@ int br_vlan_process_options(const struct net_bridge *br,
        return err;
 }
 
+bool br_vlan_global_opts_can_enter_range(const struct net_bridge_vlan *v_curr,
+                                        const struct net_bridge_vlan *r_end)
+{
+       return v_curr->vid - r_end->vid == 1;
+}
+
+bool br_vlan_global_opts_fill(struct sk_buff *skb, u16 vid, u16 vid_range,
+                             const struct net_bridge_vlan *v_opts)
+{
+       struct nlattr *nest;
+
+       nest = nla_nest_start(skb, BRIDGE_VLANDB_GLOBAL_OPTIONS);
+       if (!nest)
+               return false;
+
+       if (nla_put_u16(skb, BRIDGE_VLANDB_GOPTS_ID, vid))
+               goto out_err;
+
+       if (vid_range && vid < vid_range &&
+           nla_put_u16(skb, BRIDGE_VLANDB_GOPTS_RANGE, vid_range))
+               goto out_err;
+
+       nla_nest_end(skb, nest);
+
+       return true;
+
+out_err:
+       nla_nest_cancel(skb, nest);
+       return false;
+}
+
 static int br_vlan_process_global_one_opts(const struct net_bridge *br,
                                           struct net_bridge_vlan_group *vg,
                                           struct net_bridge_vlan *v,