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 v1->state == v2->state; 15 } 16 17 bool br_vlan_opts_fill(struct sk_buff *skb, const struct net_bridge_vlan *v) 18 { 19 return !nla_put_u8(skb, BRIDGE_VLANDB_ENTRY_STATE, 20 br_vlan_get_state(v)); 21 } 22 23 size_t br_vlan_opts_nl_size(void) 24 { 25 return nla_total_size(sizeof(u8)); /* BRIDGE_VLANDB_ENTRY_STATE */ 26 } 27 28 static int br_vlan_modify_state(struct net_bridge_vlan_group *vg, 29 struct net_bridge_vlan *v, 30 u8 state, 31 bool *changed, 32 struct netlink_ext_ack *extack) 33 { 34 struct net_bridge *br; 35 36 ASSERT_RTNL(); 37 38 if (state > BR_STATE_BLOCKING) { 39 NL_SET_ERR_MSG_MOD(extack, "Invalid vlan state"); 40 return -EINVAL; 41 } 42 43 if (br_vlan_is_brentry(v)) 44 br = v->br; 45 else 46 br = v->port->br; 47 48 if (br->stp_enabled == BR_KERNEL_STP) { 49 NL_SET_ERR_MSG_MOD(extack, "Can't modify vlan state when using kernel STP"); 50 return -EBUSY; 51 } 52 53 if (v->state == state) 54 return 0; 55 56 if (v->vid == br_get_pvid(vg)) 57 br_vlan_set_pvid_state(vg, state); 58 59 br_vlan_set_state(v, state); 60 *changed = true; 61 62 return 0; 63 } 64 65 static int br_vlan_process_one_opts(const struct net_bridge *br, 66 const struct net_bridge_port *p, 67 struct net_bridge_vlan_group *vg, 68 struct net_bridge_vlan *v, 69 struct nlattr **tb, 70 bool *changed, 71 struct netlink_ext_ack *extack) 72 { 73 int err; 74 75 *changed = false; 76 if (tb[BRIDGE_VLANDB_ENTRY_STATE]) { 77 u8 state = nla_get_u8(tb[BRIDGE_VLANDB_ENTRY_STATE]); 78 79 err = br_vlan_modify_state(vg, v, state, changed, extack); 80 if (err) 81 return err; 82 } 83 84 return 0; 85 } 86 87 int br_vlan_process_options(const struct net_bridge *br, 88 const struct net_bridge_port *p, 89 struct net_bridge_vlan *range_start, 90 struct net_bridge_vlan *range_end, 91 struct nlattr **tb, 92 struct netlink_ext_ack *extack) 93 { 94 struct net_bridge_vlan *v, *curr_start = NULL, *curr_end = NULL; 95 struct net_bridge_vlan_group *vg; 96 int vid, err = 0; 97 u16 pvid; 98 99 if (p) 100 vg = nbp_vlan_group(p); 101 else 102 vg = br_vlan_group(br); 103 104 if (!range_start || !br_vlan_should_use(range_start)) { 105 NL_SET_ERR_MSG_MOD(extack, "Vlan range start doesn't exist, can't process options"); 106 return -ENOENT; 107 } 108 if (!range_end || !br_vlan_should_use(range_end)) { 109 NL_SET_ERR_MSG_MOD(extack, "Vlan range end doesn't exist, can't process options"); 110 return -ENOENT; 111 } 112 113 pvid = br_get_pvid(vg); 114 for (vid = range_start->vid; vid <= range_end->vid; vid++) { 115 bool changed = false; 116 117 v = br_vlan_find(vg, vid); 118 if (!v || !br_vlan_should_use(v)) { 119 NL_SET_ERR_MSG_MOD(extack, "Vlan in range doesn't exist, can't process options"); 120 err = -ENOENT; 121 break; 122 } 123 124 err = br_vlan_process_one_opts(br, p, vg, v, tb, &changed, 125 extack); 126 if (err) 127 break; 128 129 if (changed) { 130 /* vlan options changed, check for range */ 131 if (!curr_start) { 132 curr_start = v; 133 curr_end = v; 134 continue; 135 } 136 137 if (v->vid == pvid || 138 !br_vlan_can_enter_range(v, curr_end)) { 139 br_vlan_notify(br, p, curr_start->vid, 140 curr_end->vid, RTM_NEWVLAN); 141 curr_start = v; 142 } 143 curr_end = v; 144 } else { 145 /* nothing changed and nothing to notify yet */ 146 if (!curr_start) 147 continue; 148 149 br_vlan_notify(br, p, curr_start->vid, curr_end->vid, 150 RTM_NEWVLAN); 151 curr_start = NULL; 152 curr_end = NULL; 153 } 154 } 155 if (curr_start) 156 br_vlan_notify(br, p, curr_start->vid, curr_end->vid, 157 RTM_NEWVLAN); 158 159 return err; 160 } 161