xref: /openbmc/linux/net/bridge/br_vlan_options.c (revision a5d29ae2)
1 // SPDX-License-Identifier: GPL-2.0-only
2 // Copyright (c) 2020, Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
3 #include <linux/kernel.h>
4 #include <linux/netdevice.h>
5 #include <linux/rtnetlink.h>
6 #include <linux/slab.h>
7 
8 #include "br_private.h"
9 
10 /* check if the options between two vlans are equal */
11 bool br_vlan_opts_eq(const struct net_bridge_vlan *v1,
12 		     const struct net_bridge_vlan *v2)
13 {
14 	return true;
15 }
16 
17 bool br_vlan_opts_fill(struct sk_buff *skb, const struct net_bridge_vlan *v)
18 {
19 	return true;
20 }
21 
22 size_t br_vlan_opts_nl_size(void)
23 {
24 	return 0;
25 }
26 
27 static int br_vlan_process_one_opts(const struct net_bridge *br,
28 				    const struct net_bridge_port *p,
29 				    struct net_bridge_vlan_group *vg,
30 				    struct net_bridge_vlan *v,
31 				    struct nlattr **tb,
32 				    bool *changed,
33 				    struct netlink_ext_ack *extack)
34 {
35 	*changed = false;
36 	return 0;
37 }
38 
39 int br_vlan_process_options(const struct net_bridge *br,
40 			    const struct net_bridge_port *p,
41 			    struct net_bridge_vlan *range_start,
42 			    struct net_bridge_vlan *range_end,
43 			    struct nlattr **tb,
44 			    struct netlink_ext_ack *extack)
45 {
46 	struct net_bridge_vlan *v, *curr_start = NULL, *curr_end = NULL;
47 	struct net_bridge_vlan_group *vg;
48 	int vid, err = 0;
49 	u16 pvid;
50 
51 	if (p)
52 		vg = nbp_vlan_group(p);
53 	else
54 		vg = br_vlan_group(br);
55 
56 	if (!range_start || !br_vlan_should_use(range_start)) {
57 		NL_SET_ERR_MSG_MOD(extack, "Vlan range start doesn't exist, can't process options");
58 		return -ENOENT;
59 	}
60 	if (!range_end || !br_vlan_should_use(range_end)) {
61 		NL_SET_ERR_MSG_MOD(extack, "Vlan range end doesn't exist, can't process options");
62 		return -ENOENT;
63 	}
64 
65 	pvid = br_get_pvid(vg);
66 	for (vid = range_start->vid; vid <= range_end->vid; vid++) {
67 		bool changed = false;
68 
69 		v = br_vlan_find(vg, vid);
70 		if (!v || !br_vlan_should_use(v)) {
71 			NL_SET_ERR_MSG_MOD(extack, "Vlan in range doesn't exist, can't process options");
72 			err = -ENOENT;
73 			break;
74 		}
75 
76 		err = br_vlan_process_one_opts(br, p, vg, v, tb, &changed,
77 					       extack);
78 		if (err)
79 			break;
80 
81 		if (changed) {
82 			/* vlan options changed, check for range */
83 			if (!curr_start) {
84 				curr_start = v;
85 				curr_end = v;
86 				continue;
87 			}
88 
89 			if (v->vid == pvid ||
90 			    !br_vlan_can_enter_range(v, curr_end)) {
91 				br_vlan_notify(br, p, curr_start->vid,
92 					       curr_end->vid, RTM_NEWVLAN);
93 				curr_start = v;
94 			}
95 			curr_end = v;
96 		} else {
97 			/* nothing changed and nothing to notify yet */
98 			if (!curr_start)
99 				continue;
100 
101 			br_vlan_notify(br, p, curr_start->vid, curr_end->vid,
102 				       RTM_NEWVLAN);
103 			curr_start = NULL;
104 			curr_end = NULL;
105 		}
106 	}
107 	if (curr_start)
108 		br_vlan_notify(br, p, curr_start->vid, curr_end->vid,
109 			       RTM_NEWVLAN);
110 
111 	return err;
112 }
113