.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * Generic parts |
---|
3 | 4 | * Linux ethernet bridge |
---|
4 | 5 | * |
---|
5 | 6 | * Authors: |
---|
6 | 7 | * Lennert Buytenhek <buytenh@gnu.org> |
---|
7 | | - * |
---|
8 | | - * This program is free software; you can redistribute it and/or |
---|
9 | | - * modify it under the terms of the GNU General Public License |
---|
10 | | - * as published by the Free Software Foundation; either version |
---|
11 | | - * 2 of the License, or (at your option) any later version. |
---|
12 | 8 | */ |
---|
13 | 9 | |
---|
14 | 10 | #include <linux/module.h> |
---|
.. | .. |
---|
31 | 27 | */ |
---|
32 | 28 | static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr) |
---|
33 | 29 | { |
---|
| 30 | + struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(ptr); |
---|
| 31 | + struct netdev_notifier_pre_changeaddr_info *prechaddr_info; |
---|
34 | 32 | struct net_device *dev = netdev_notifier_info_to_dev(ptr); |
---|
35 | 33 | struct net_bridge_port *p; |
---|
36 | 34 | struct net_bridge *br; |
---|
.. | .. |
---|
38 | 36 | bool changed_addr; |
---|
39 | 37 | int err; |
---|
40 | 38 | |
---|
41 | | - /* register of bridge completed, add sysfs entries */ |
---|
42 | | - if ((dev->priv_flags & IFF_EBRIDGE) && event == NETDEV_REGISTER) { |
---|
43 | | - br_sysfs_addbr(dev); |
---|
44 | | - return NOTIFY_DONE; |
---|
| 39 | + if (dev->priv_flags & IFF_EBRIDGE) { |
---|
| 40 | + err = br_vlan_bridge_event(dev, event, ptr); |
---|
| 41 | + if (err) |
---|
| 42 | + return notifier_from_errno(err); |
---|
| 43 | + |
---|
| 44 | + if (event == NETDEV_REGISTER) { |
---|
| 45 | + /* register of bridge completed, add sysfs entries */ |
---|
| 46 | + err = br_sysfs_addbr(dev); |
---|
| 47 | + if (err) |
---|
| 48 | + return notifier_from_errno(err); |
---|
| 49 | + |
---|
| 50 | + return NOTIFY_DONE; |
---|
| 51 | + } |
---|
45 | 52 | } |
---|
46 | 53 | |
---|
47 | 54 | /* not a port of a bridge */ |
---|
.. | .. |
---|
54 | 61 | switch (event) { |
---|
55 | 62 | case NETDEV_CHANGEMTU: |
---|
56 | 63 | br_mtu_auto_adjust(br); |
---|
| 64 | + break; |
---|
| 65 | + |
---|
| 66 | + case NETDEV_PRE_CHANGEADDR: |
---|
| 67 | + if (br->dev->addr_assign_type == NET_ADDR_SET) |
---|
| 68 | + break; |
---|
| 69 | + prechaddr_info = ptr; |
---|
| 70 | + err = dev_pre_changeaddr_notify(br->dev, |
---|
| 71 | + prechaddr_info->dev_addr, |
---|
| 72 | + extack); |
---|
| 73 | + if (err) |
---|
| 74 | + return notifier_from_errno(err); |
---|
57 | 75 | break; |
---|
58 | 76 | |
---|
59 | 77 | case NETDEV_CHANGEADDR: |
---|
.. | .. |
---|
113 | 131 | break; |
---|
114 | 132 | } |
---|
115 | 133 | |
---|
| 134 | + if (event != NETDEV_UNREGISTER) |
---|
| 135 | + br_vlan_port_event(p, event); |
---|
| 136 | + |
---|
116 | 137 | /* Events that may cause spanning tree to refresh */ |
---|
117 | 138 | if (!notified && (event == NETDEV_CHANGEADDR || event == NETDEV_UP || |
---|
118 | 139 | event == NETDEV_CHANGE || event == NETDEV_DOWN)) |
---|
.. | .. |
---|
151 | 172 | break; |
---|
152 | 173 | } |
---|
153 | 174 | br_fdb_offloaded_set(br, p, fdb_info->addr, |
---|
154 | | - fdb_info->vid); |
---|
| 175 | + fdb_info->vid, true); |
---|
155 | 176 | break; |
---|
156 | 177 | case SWITCHDEV_FDB_DEL_TO_BRIDGE: |
---|
157 | 178 | fdb_info = ptr; |
---|
.. | .. |
---|
163 | 184 | case SWITCHDEV_FDB_OFFLOADED: |
---|
164 | 185 | fdb_info = ptr; |
---|
165 | 186 | br_fdb_offloaded_set(br, p, fdb_info->addr, |
---|
166 | | - fdb_info->vid); |
---|
| 187 | + fdb_info->vid, fdb_info->offloaded); |
---|
| 188 | + break; |
---|
| 189 | + case SWITCHDEV_FDB_FLUSH_TO_BRIDGE: |
---|
| 190 | + fdb_info = ptr; |
---|
| 191 | + /* Don't delete static entries */ |
---|
| 192 | + br_fdb_delete_by_port(br, p, fdb_info->vid, 0); |
---|
167 | 193 | break; |
---|
168 | 194 | } |
---|
169 | 195 | |
---|
.. | .. |
---|
174 | 200 | static struct notifier_block br_switchdev_notifier = { |
---|
175 | 201 | .notifier_call = br_switchdev_event, |
---|
176 | 202 | }; |
---|
| 203 | + |
---|
| 204 | +/* br_boolopt_toggle - change user-controlled boolean option |
---|
| 205 | + * |
---|
| 206 | + * @br: bridge device |
---|
| 207 | + * @opt: id of the option to change |
---|
| 208 | + * @on: new option value |
---|
| 209 | + * @extack: extack for error messages |
---|
| 210 | + * |
---|
| 211 | + * Changes the value of the respective boolean option to @on taking care of |
---|
| 212 | + * any internal option value mapping and configuration. |
---|
| 213 | + */ |
---|
| 214 | +int br_boolopt_toggle(struct net_bridge *br, enum br_boolopt_id opt, bool on, |
---|
| 215 | + struct netlink_ext_ack *extack) |
---|
| 216 | +{ |
---|
| 217 | + switch (opt) { |
---|
| 218 | + case BR_BOOLOPT_NO_LL_LEARN: |
---|
| 219 | + br_opt_toggle(br, BROPT_NO_LL_LEARN, on); |
---|
| 220 | + break; |
---|
| 221 | + default: |
---|
| 222 | + /* shouldn't be called with unsupported options */ |
---|
| 223 | + WARN_ON(1); |
---|
| 224 | + break; |
---|
| 225 | + } |
---|
| 226 | + |
---|
| 227 | + return 0; |
---|
| 228 | +} |
---|
| 229 | + |
---|
| 230 | +int br_boolopt_get(const struct net_bridge *br, enum br_boolopt_id opt) |
---|
| 231 | +{ |
---|
| 232 | + switch (opt) { |
---|
| 233 | + case BR_BOOLOPT_NO_LL_LEARN: |
---|
| 234 | + return br_opt_get(br, BROPT_NO_LL_LEARN); |
---|
| 235 | + default: |
---|
| 236 | + /* shouldn't be called with unsupported options */ |
---|
| 237 | + WARN_ON(1); |
---|
| 238 | + break; |
---|
| 239 | + } |
---|
| 240 | + |
---|
| 241 | + return 0; |
---|
| 242 | +} |
---|
| 243 | + |
---|
| 244 | +int br_boolopt_multi_toggle(struct net_bridge *br, |
---|
| 245 | + struct br_boolopt_multi *bm, |
---|
| 246 | + struct netlink_ext_ack *extack) |
---|
| 247 | +{ |
---|
| 248 | + unsigned long bitmap = bm->optmask; |
---|
| 249 | + int err = 0; |
---|
| 250 | + int opt_id; |
---|
| 251 | + |
---|
| 252 | + for_each_set_bit(opt_id, &bitmap, BR_BOOLOPT_MAX) { |
---|
| 253 | + bool on = !!(bm->optval & BIT(opt_id)); |
---|
| 254 | + |
---|
| 255 | + err = br_boolopt_toggle(br, opt_id, on, extack); |
---|
| 256 | + if (err) { |
---|
| 257 | + br_debug(br, "boolopt multi-toggle error: option: %d current: %d new: %d error: %d\n", |
---|
| 258 | + opt_id, br_boolopt_get(br, opt_id), on, err); |
---|
| 259 | + break; |
---|
| 260 | + } |
---|
| 261 | + } |
---|
| 262 | + |
---|
| 263 | + return err; |
---|
| 264 | +} |
---|
| 265 | + |
---|
| 266 | +void br_boolopt_multi_get(const struct net_bridge *br, |
---|
| 267 | + struct br_boolopt_multi *bm) |
---|
| 268 | +{ |
---|
| 269 | + u32 optval = 0; |
---|
| 270 | + int opt_id; |
---|
| 271 | + |
---|
| 272 | + for (opt_id = 0; opt_id < BR_BOOLOPT_MAX; opt_id++) |
---|
| 273 | + optval |= (br_boolopt_get(br, opt_id) << opt_id); |
---|
| 274 | + |
---|
| 275 | + bm->optval = optval; |
---|
| 276 | + bm->optmask = GENMASK((BR_BOOLOPT_MAX - 1), 0); |
---|
| 277 | +} |
---|
| 278 | + |
---|
| 279 | +/* private bridge options, controlled by the kernel */ |
---|
| 280 | +void br_opt_toggle(struct net_bridge *br, enum net_bridge_opts opt, bool on) |
---|
| 281 | +{ |
---|
| 282 | + bool cur = !!br_opt_get(br, opt); |
---|
| 283 | + |
---|
| 284 | + br_debug(br, "toggle option: %d state: %d -> %d\n", |
---|
| 285 | + opt, cur, on); |
---|
| 286 | + |
---|
| 287 | + if (cur == on) |
---|
| 288 | + return; |
---|
| 289 | + |
---|
| 290 | + if (on) |
---|
| 291 | + set_bit(opt, &br->options); |
---|
| 292 | + else |
---|
| 293 | + clear_bit(opt, &br->options); |
---|
| 294 | +} |
---|
177 | 295 | |
---|
178 | 296 | static void __net_exit br_net_exit(struct net *net) |
---|
179 | 297 | { |
---|
.. | .. |
---|
202 | 320 | { |
---|
203 | 321 | int err; |
---|
204 | 322 | |
---|
205 | | - BUILD_BUG_ON(sizeof(struct br_input_skb_cb) > FIELD_SIZEOF(struct sk_buff, cb)); |
---|
| 323 | + BUILD_BUG_ON(sizeof(struct br_input_skb_cb) > sizeof_field(struct sk_buff, cb)); |
---|
206 | 324 | |
---|
207 | 325 | err = stp_proto_register(&br_stp_proto); |
---|
208 | 326 | if (err < 0) { |
---|