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 #include <net/ip_tunnels.h> 8 9 #include "br_private.h" 10 #include "br_private_tunnel.h" 11 12 static bool __vlan_tun_put(struct sk_buff *skb, const struct net_bridge_vlan *v) 13 { 14 __be32 tid = tunnel_id_to_key32(v->tinfo.tunnel_id); 15 struct nlattr *nest; 16 17 if (!v->tinfo.tunnel_dst) 18 return true; 19 20 nest = nla_nest_start(skb, BRIDGE_VLANDB_ENTRY_TUNNEL_INFO); 21 if (!nest) 22 return false; 23 if (nla_put_u32(skb, BRIDGE_VLANDB_TINFO_ID, be32_to_cpu(tid))) { 24 nla_nest_cancel(skb, nest); 25 return false; 26 } 27 nla_nest_end(skb, nest); 28 29 return true; 30 } 31 32 static bool __vlan_tun_can_enter_range(const struct net_bridge_vlan *v_curr, 33 const struct net_bridge_vlan *range_end) 34 { 35 return (!v_curr->tinfo.tunnel_dst && !range_end->tinfo.tunnel_dst) || 36 vlan_tunid_inrange(v_curr, range_end); 37 } 38 39 /* check if the options' state of v_curr allow it to enter the range */ 40 bool br_vlan_opts_eq_range(const struct net_bridge_vlan *v_curr, 41 const struct net_bridge_vlan *range_end) 42 { 43 return v_curr->state == range_end->state && 44 __vlan_tun_can_enter_range(v_curr, range_end); 45 } 46 47 bool br_vlan_opts_fill(struct sk_buff *skb, const struct net_bridge_vlan *v) 48 { 49 return !nla_put_u8(skb, BRIDGE_VLANDB_ENTRY_STATE, 50 br_vlan_get_state(v)) && 51 __vlan_tun_put(skb, v); 52 } 53 54 size_t br_vlan_opts_nl_size(void) 55 { 56 return nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_ENTRY_STATE */ 57 + nla_total_size(0) /* BRIDGE_VLANDB_ENTRY_TUNNEL_INFO */ 58 + nla_total_size(sizeof(u32)); /* BRIDGE_VLANDB_TINFO_ID */ 59 } 60 61 static int br_vlan_modify_state(struct net_bridge_vlan_group *vg, 62 struct net_bridge_vlan *v, 63 u8 state, 64 bool *changed, 65 struct netlink_ext_ack *extack) 66 { 67 struct net_bridge *br; 68 69 ASSERT_RTNL(); 70 71 if (state > BR_STATE_BLOCKING) { 72 NL_SET_ERR_MSG_MOD(extack, "Invalid vlan state"); 73 return -EINVAL; 74 } 75 76 if (br_vlan_is_brentry(v)) 77 br = v->br; 78 else 79 br = v->port->br; 80 81 if (br->stp_enabled == BR_KERNEL_STP) { 82 NL_SET_ERR_MSG_MOD(extack, "Can't modify vlan state when using kernel STP"); 83 return -EBUSY; 84 } 85 86 if (v->state == state) 87 return 0; 88 89 if (v->vid == br_get_pvid(vg)) 90 br_vlan_set_pvid_state(vg, state); 91 92 br_vlan_set_state(v, state); 93 *changed = true; 94 95 return 0; 96 } 97 98 static const struct nla_policy br_vlandb_tinfo_pol[BRIDGE_VLANDB_TINFO_MAX + 1] = { 99 [BRIDGE_VLANDB_TINFO_ID] = { .type = NLA_U32 }, 100 }; 101 102 static int br_vlan_modify_tunnel(const struct net_bridge_port *p, 103 struct net_bridge_vlan *v, 104 struct nlattr **tb, 105 bool *changed, 106 struct netlink_ext_ack *extack) 107 { 108 struct nlattr *tun_tb[BRIDGE_VLANDB_TINFO_MAX + 1], *attr; 109 struct bridge_vlan_info *vinfo; 110 int cmdmap, err; 111 u32 tun_id; 112 113 if (!p) { 114 NL_SET_ERR_MSG_MOD(extack, "Can't modify tunnel mapping of non-port vlans"); 115 return -EINVAL; 116 } 117 if (!(p->flags & BR_VLAN_TUNNEL)) { 118 NL_SET_ERR_MSG_MOD(extack, "Port doesn't have tunnel flag set"); 119 return -EINVAL; 120 } 121 122 attr = tb[BRIDGE_VLANDB_ENTRY_TUNNEL_INFO]; 123 err = nla_parse_nested(tun_tb, BRIDGE_VLANDB_TINFO_MAX, attr, 124 br_vlandb_tinfo_pol, extack); 125 if (err) 126 return err; 127 128 if (!tun_tb[BRIDGE_VLANDB_TINFO_ID]) { 129 NL_SET_ERR_MSG_MOD(extack, "Missing tunnel id attribute"); 130 return -ENOENT; 131 } 132 /* vlan info attribute is guaranteed by br_vlan_rtm_process_one */ 133 vinfo = nla_data(tb[BRIDGE_VLANDB_ENTRY_INFO]); 134 cmdmap = vinfo->flags & BRIDGE_VLAN_INFO_REMOVE_TUN ? RTM_DELLINK : 135 RTM_SETLINK; 136 /* when working on vlan ranges this represents the starting tunnel id */ 137 tun_id = nla_get_u32(tun_tb[BRIDGE_VLANDB_TINFO_ID]); 138 /* tunnel ids are mapped to each vlan in increasing order, 139 * the starting vlan is in BRIDGE_VLANDB_ENTRY_INFO and v is the 140 * current vlan, so we compute: tun_id + v - vinfo->vid 141 */ 142 tun_id += v->vid - vinfo->vid; 143 144 return br_vlan_tunnel_info(p, cmdmap, v->vid, tun_id, changed); 145 } 146 147 static int br_vlan_process_one_opts(const struct net_bridge *br, 148 const struct net_bridge_port *p, 149 struct net_bridge_vlan_group *vg, 150 struct net_bridge_vlan *v, 151 struct nlattr **tb, 152 bool *changed, 153 struct netlink_ext_ack *extack) 154 { 155 int err; 156 157 *changed = false; 158 if (tb[BRIDGE_VLANDB_ENTRY_STATE]) { 159 u8 state = nla_get_u8(tb[BRIDGE_VLANDB_ENTRY_STATE]); 160 161 err = br_vlan_modify_state(vg, v, state, changed, extack); 162 if (err) 163 return err; 164 } 165 if (tb[BRIDGE_VLANDB_ENTRY_TUNNEL_INFO]) { 166 err = br_vlan_modify_tunnel(p, v, tb, changed, extack); 167 if (err) 168 return err; 169 } 170 171 return 0; 172 } 173 174 int br_vlan_process_options(const struct net_bridge *br, 175 const struct net_bridge_port *p, 176 struct net_bridge_vlan *range_start, 177 struct net_bridge_vlan *range_end, 178 struct nlattr **tb, 179 struct netlink_ext_ack *extack) 180 { 181 struct net_bridge_vlan *v, *curr_start = NULL, *curr_end = NULL; 182 struct net_bridge_vlan_group *vg; 183 int vid, err = 0; 184 u16 pvid; 185 186 if (p) 187 vg = nbp_vlan_group(p); 188 else 189 vg = br_vlan_group(br); 190 191 if (!range_start || !br_vlan_should_use(range_start)) { 192 NL_SET_ERR_MSG_MOD(extack, "Vlan range start doesn't exist, can't process options"); 193 return -ENOENT; 194 } 195 if (!range_end || !br_vlan_should_use(range_end)) { 196 NL_SET_ERR_MSG_MOD(extack, "Vlan range end doesn't exist, can't process options"); 197 return -ENOENT; 198 } 199 200 pvid = br_get_pvid(vg); 201 for (vid = range_start->vid; vid <= range_end->vid; vid++) { 202 bool changed = false; 203 204 v = br_vlan_find(vg, vid); 205 if (!v || !br_vlan_should_use(v)) { 206 NL_SET_ERR_MSG_MOD(extack, "Vlan in range doesn't exist, can't process options"); 207 err = -ENOENT; 208 break; 209 } 210 211 err = br_vlan_process_one_opts(br, p, vg, v, tb, &changed, 212 extack); 213 if (err) 214 break; 215 216 if (changed) { 217 /* vlan options changed, check for range */ 218 if (!curr_start) { 219 curr_start = v; 220 curr_end = v; 221 continue; 222 } 223 224 if (v->vid == pvid || 225 !br_vlan_can_enter_range(v, curr_end)) { 226 br_vlan_notify(br, p, curr_start->vid, 227 curr_end->vid, RTM_NEWVLAN); 228 curr_start = v; 229 } 230 curr_end = v; 231 } else { 232 /* nothing changed and nothing to notify yet */ 233 if (!curr_start) 234 continue; 235 236 br_vlan_notify(br, p, curr_start->vid, curr_end->vid, 237 RTM_NEWVLAN); 238 curr_start = NULL; 239 curr_end = NULL; 240 } 241 } 242 if (curr_start) 243 br_vlan_notify(br, p, curr_start->vid, curr_end->vid, 244 RTM_NEWVLAN); 245 246 return err; 247 } 248