| .. | .. |
|---|
| 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) { |
|---|