Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
1da177e4 LT |
2 | /* |
3 | * Generic parts | |
4 | * Linux ethernet bridge | |
5 | * | |
6 | * Authors: | |
7 | * Lennert Buytenhek <buytenh@gnu.org> | |
1da177e4 LT |
8 | */ |
9 | ||
1da177e4 LT |
10 | #include <linux/module.h> |
11 | #include <linux/kernel.h> | |
12 | #include <linux/netdevice.h> | |
13 | #include <linux/etherdevice.h> | |
14 | #include <linux/init.h> | |
cf0f02d0 SH |
15 | #include <linux/llc.h> |
16 | #include <net/llc.h> | |
7c85fbf0 | 17 | #include <net/stp.h> |
3aeb6617 | 18 | #include <net/switchdev.h> |
1da177e4 LT |
19 | |
20 | #include "br_private.h" | |
21 | ||
b1282726 CW |
22 | /* |
23 | * Handle changes in state of network devices enslaved to a bridge. | |
24 | * | |
25 | * Note: don't care about up/down if bridge itself is down, because | |
26 | * port state is checked when bridge is brought up. | |
27 | */ | |
28 | static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr) | |
29 | { | |
b89df65c PM |
30 | struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(ptr); |
31 | struct netdev_notifier_pre_changeaddr_info *prechaddr_info; | |
b1282726 CW |
32 | struct net_device *dev = netdev_notifier_info_to_dev(ptr); |
33 | struct net_bridge_port *p; | |
34 | struct net_bridge *br; | |
faa1cd82 | 35 | bool notified = false; |
b1282726 CW |
36 | bool changed_addr; |
37 | int err; | |
38 | ||
254ec036 | 39 | if (netif_is_bridge_master(dev)) { |
091adf9b NA |
40 | err = br_vlan_bridge_event(dev, event, ptr); |
41 | if (err) | |
42 | return notifier_from_errno(err); | |
43 | ||
9c0ec2e7 MM |
44 | if (event == NETDEV_REGISTER) { |
45 | /* register of bridge completed, add sysfs entries */ | |
989a1db0 WH |
46 | err = br_sysfs_addbr(dev); |
47 | if (err) | |
48 | return notifier_from_errno(err); | |
49 | ||
9c0ec2e7 MM |
50 | return NOTIFY_DONE; |
51 | } | |
b1282726 CW |
52 | } |
53 | ||
54 | /* not a port of a bridge */ | |
55 | p = br_port_get_rtnl(dev); | |
56 | if (!p) | |
57 | return NOTIFY_DONE; | |
58 | ||
59 | br = p->br; | |
60 | ||
61 | switch (event) { | |
62 | case NETDEV_CHANGEMTU: | |
804b854d | 63 | br_mtu_auto_adjust(br); |
b1282726 CW |
64 | break; |
65 | ||
b89df65c PM |
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); | |
75 | break; | |
76 | ||
b1282726 CW |
77 | case NETDEV_CHANGEADDR: |
78 | spin_lock_bh(&br->lock); | |
79 | br_fdb_changeaddr(p, dev->dev_addr); | |
80 | changed_addr = br_stp_recalculate_bridge_id(br); | |
81 | spin_unlock_bh(&br->lock); | |
82 | ||
83 | if (changed_addr) | |
84 | call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev); | |
85 | ||
86 | break; | |
87 | ||
88 | case NETDEV_CHANGE: | |
faa1cd82 | 89 | br_port_carrier_check(p, ¬ified); |
b1282726 CW |
90 | break; |
91 | ||
92 | case NETDEV_FEAT_CHANGE: | |
93 | netdev_update_features(br->dev); | |
94 | break; | |
95 | ||
96 | case NETDEV_DOWN: | |
97 | spin_lock_bh(&br->lock); | |
faa1cd82 | 98 | if (br->dev->flags & IFF_UP) { |
b1282726 | 99 | br_stp_disable_port(p); |
faa1cd82 NA |
100 | notified = true; |
101 | } | |
b1282726 CW |
102 | spin_unlock_bh(&br->lock); |
103 | break; | |
104 | ||
105 | case NETDEV_UP: | |
106 | if (netif_running(br->dev) && netif_oper_up(dev)) { | |
107 | spin_lock_bh(&br->lock); | |
108 | br_stp_enable_port(p); | |
faa1cd82 | 109 | notified = true; |
b1282726 CW |
110 | spin_unlock_bh(&br->lock); |
111 | } | |
112 | break; | |
113 | ||
114 | case NETDEV_UNREGISTER: | |
115 | br_del_if(br, dev); | |
116 | break; | |
117 | ||
118 | case NETDEV_CHANGENAME: | |
119 | err = br_sysfs_renameif(p); | |
120 | if (err) | |
121 | return notifier_from_errno(err); | |
122 | break; | |
123 | ||
124 | case NETDEV_PRE_TYPE_CHANGE: | |
efb5b338 | 125 | /* Forbid underlying device to change its type. */ |
b1282726 CW |
126 | return NOTIFY_BAD; |
127 | ||
128 | case NETDEV_RESEND_IGMP: | |
129 | /* Propagate to master device */ | |
130 | call_netdevice_notifiers(event, br->dev); | |
131 | break; | |
132 | } | |
133 | ||
697cd36c IS |
134 | if (event != NETDEV_UNREGISTER) |
135 | br_vlan_port_event(p, event); | |
9c0ec2e7 | 136 | |
b1282726 | 137 | /* Events that may cause spanning tree to refresh */ |
faa1cd82 NA |
138 | if (!notified && (event == NETDEV_CHANGEADDR || event == NETDEV_UP || |
139 | event == NETDEV_CHANGE || event == NETDEV_DOWN)) | |
92899063 | 140 | br_ifinfo_notify(RTM_NEWLINK, NULL, p); |
b1282726 CW |
141 | |
142 | return NOTIFY_DONE; | |
143 | } | |
144 | ||
145 | static struct notifier_block br_device_notifier = { | |
146 | .notifier_call = br_device_event | |
147 | }; | |
148 | ||
0baa10ff | 149 | /* called with RTNL or RCU */ |
ebb9a03a JP |
150 | static int br_switchdev_event(struct notifier_block *unused, |
151 | unsigned long event, void *ptr) | |
3aeb6617 | 152 | { |
ebb9a03a | 153 | struct net_device *dev = switchdev_notifier_info_to_dev(ptr); |
3aeb6617 JP |
154 | struct net_bridge_port *p; |
155 | struct net_bridge *br; | |
ebb9a03a | 156 | struct switchdev_notifier_fdb_info *fdb_info; |
3aeb6617 JP |
157 | int err = NOTIFY_DONE; |
158 | ||
0baa10ff | 159 | p = br_port_get_rtnl_rcu(dev); |
3aeb6617 JP |
160 | if (!p) |
161 | goto out; | |
162 | ||
163 | br = p->br; | |
164 | ||
165 | switch (event) { | |
6b26b51b | 166 | case SWITCHDEV_FDB_ADD_TO_BRIDGE: |
3aeb6617 JP |
167 | fdb_info = ptr; |
168 | err = br_fdb_external_learn_add(br, p, fdb_info->addr, | |
27fabd02 HS |
169 | fdb_info->vid, |
170 | fdb_info->locked, false); | |
9fe8bcec | 171 | if (err) { |
3aeb6617 | 172 | err = notifier_from_errno(err); |
9fe8bcec AS |
173 | break; |
174 | } | |
175 | br_fdb_offloaded_set(br, p, fdb_info->addr, | |
9baedc3c | 176 | fdb_info->vid, fdb_info->offloaded); |
3aeb6617 | 177 | break; |
6b26b51b | 178 | case SWITCHDEV_FDB_DEL_TO_BRIDGE: |
3aeb6617 JP |
179 | fdb_info = ptr; |
180 | err = br_fdb_external_learn_del(br, p, fdb_info->addr, | |
161d82de | 181 | fdb_info->vid, false); |
3aeb6617 JP |
182 | if (err) |
183 | err = notifier_from_errno(err); | |
184 | break; | |
9fe8bcec AS |
185 | case SWITCHDEV_FDB_OFFLOADED: |
186 | fdb_info = ptr; | |
187 | br_fdb_offloaded_set(br, p, fdb_info->addr, | |
e9ba0fbc | 188 | fdb_info->vid, fdb_info->offloaded); |
9fe8bcec | 189 | break; |
d05e8e68 AW |
190 | case SWITCHDEV_FDB_FLUSH_TO_BRIDGE: |
191 | fdb_info = ptr; | |
192 | /* Don't delete static entries */ | |
193 | br_fdb_delete_by_port(br, p, fdb_info->vid, 0); | |
194 | break; | |
3aeb6617 JP |
195 | } |
196 | ||
197 | out: | |
3aeb6617 JP |
198 | return err; |
199 | } | |
200 | ||
ebb9a03a JP |
201 | static struct notifier_block br_switchdev_notifier = { |
202 | .notifier_call = br_switchdev_event, | |
3aeb6617 JP |
203 | }; |
204 | ||
957e2235 VO |
205 | /* called under rtnl_mutex */ |
206 | static int br_switchdev_blocking_event(struct notifier_block *nb, | |
207 | unsigned long event, void *ptr) | |
208 | { | |
209 | struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(ptr); | |
210 | struct net_device *dev = switchdev_notifier_info_to_dev(ptr); | |
211 | struct switchdev_notifier_brport_info *brport_info; | |
212 | const struct switchdev_brport *b; | |
213 | struct net_bridge_port *p; | |
214 | int err = NOTIFY_DONE; | |
215 | ||
216 | p = br_port_get_rtnl(dev); | |
217 | if (!p) | |
218 | goto out; | |
219 | ||
220 | switch (event) { | |
221 | case SWITCHDEV_BRPORT_OFFLOADED: | |
222 | brport_info = ptr; | |
223 | b = &brport_info->brport; | |
224 | ||
225 | err = br_switchdev_port_offload(p, b->dev, b->ctx, | |
226 | b->atomic_nb, b->blocking_nb, | |
227 | b->tx_fwd_offload, extack); | |
228 | err = notifier_from_errno(err); | |
229 | break; | |
230 | case SWITCHDEV_BRPORT_UNOFFLOADED: | |
231 | brport_info = ptr; | |
232 | b = &brport_info->brport; | |
233 | ||
234 | br_switchdev_port_unoffload(p, b->ctx, b->atomic_nb, | |
235 | b->blocking_nb); | |
236 | break; | |
237 | } | |
238 | ||
239 | out: | |
240 | return err; | |
241 | } | |
242 | ||
243 | static struct notifier_block br_switchdev_blocking_notifier = { | |
244 | .notifier_call = br_switchdev_blocking_event, | |
245 | }; | |
246 | ||
a428afe8 NA |
247 | /* br_boolopt_toggle - change user-controlled boolean option |
248 | * | |
249 | * @br: bridge device | |
250 | * @opt: id of the option to change | |
251 | * @on: new option value | |
252 | * @extack: extack for error messages | |
253 | * | |
254 | * Changes the value of the respective boolean option to @on taking care of | |
255 | * any internal option value mapping and configuration. | |
256 | */ | |
257 | int br_boolopt_toggle(struct net_bridge *br, enum br_boolopt_id opt, bool on, | |
258 | struct netlink_ext_ack *extack) | |
259 | { | |
f4b7002a NA |
260 | int err = 0; |
261 | ||
a428afe8 | 262 | switch (opt) { |
70e4272b NA |
263 | case BR_BOOLOPT_NO_LL_LEARN: |
264 | br_opt_toggle(br, BROPT_NO_LL_LEARN, on); | |
265 | break; | |
f4b7002a NA |
266 | case BR_BOOLOPT_MCAST_VLAN_SNOOPING: |
267 | err = br_multicast_toggle_vlan_snooping(br, on, extack); | |
268 | break; | |
ec7328b5 TW |
269 | case BR_BOOLOPT_MST_ENABLE: |
270 | err = br_mst_set_enabled(br, on, extack); | |
271 | break; | |
a428afe8 NA |
272 | default: |
273 | /* shouldn't be called with unsupported options */ | |
274 | WARN_ON(1); | |
275 | break; | |
276 | } | |
277 | ||
f4b7002a | 278 | return err; |
a428afe8 NA |
279 | } |
280 | ||
281 | int br_boolopt_get(const struct net_bridge *br, enum br_boolopt_id opt) | |
282 | { | |
283 | switch (opt) { | |
70e4272b NA |
284 | case BR_BOOLOPT_NO_LL_LEARN: |
285 | return br_opt_get(br, BROPT_NO_LL_LEARN); | |
f4b7002a NA |
286 | case BR_BOOLOPT_MCAST_VLAN_SNOOPING: |
287 | return br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED); | |
ec7328b5 TW |
288 | case BR_BOOLOPT_MST_ENABLE: |
289 | return br_opt_get(br, BROPT_MST_ENABLED); | |
a428afe8 NA |
290 | default: |
291 | /* shouldn't be called with unsupported options */ | |
292 | WARN_ON(1); | |
293 | break; | |
294 | } | |
295 | ||
296 | return 0; | |
297 | } | |
298 | ||
299 | int br_boolopt_multi_toggle(struct net_bridge *br, | |
300 | struct br_boolopt_multi *bm, | |
301 | struct netlink_ext_ack *extack) | |
302 | { | |
303 | unsigned long bitmap = bm->optmask; | |
304 | int err = 0; | |
305 | int opt_id; | |
306 | ||
307 | for_each_set_bit(opt_id, &bitmap, BR_BOOLOPT_MAX) { | |
308 | bool on = !!(bm->optval & BIT(opt_id)); | |
309 | ||
310 | err = br_boolopt_toggle(br, opt_id, on, extack); | |
311 | if (err) { | |
312 | br_debug(br, "boolopt multi-toggle error: option: %d current: %d new: %d error: %d\n", | |
313 | opt_id, br_boolopt_get(br, opt_id), on, err); | |
314 | break; | |
315 | } | |
316 | } | |
317 | ||
318 | return err; | |
319 | } | |
320 | ||
321 | void br_boolopt_multi_get(const struct net_bridge *br, | |
322 | struct br_boolopt_multi *bm) | |
323 | { | |
324 | u32 optval = 0; | |
325 | int opt_id; | |
326 | ||
327 | for (opt_id = 0; opt_id < BR_BOOLOPT_MAX; opt_id++) | |
328 | optval |= (br_boolopt_get(br, opt_id) << opt_id); | |
329 | ||
330 | bm->optval = optval; | |
1ed1ccb9 | 331 | bm->optmask = GENMASK((BR_BOOLOPT_MAX - 1), 0); |
a428afe8 NA |
332 | } |
333 | ||
334 | /* private bridge options, controlled by the kernel */ | |
ae75767e NA |
335 | void br_opt_toggle(struct net_bridge *br, enum net_bridge_opts opt, bool on) |
336 | { | |
337 | bool cur = !!br_opt_get(br, opt); | |
338 | ||
339 | br_debug(br, "toggle option: %d state: %d -> %d\n", | |
340 | opt, cur, on); | |
341 | ||
342 | if (cur == on) | |
343 | return; | |
344 | ||
345 | if (on) | |
346 | set_bit(opt, &br->options); | |
347 | else | |
348 | clear_bit(opt, &br->options); | |
349 | } | |
350 | ||
36a29fb6 | 351 | static void __net_exit br_net_exit_batch(struct list_head *net_list) |
b86f81cc WC |
352 | { |
353 | struct net_device *dev; | |
36a29fb6 | 354 | struct net *net; |
b86f81cc WC |
355 | LIST_HEAD(list); |
356 | ||
357 | rtnl_lock(); | |
36a29fb6 ED |
358 | |
359 | list_for_each_entry(net, net_list, exit_list) | |
360 | for_each_netdev(net, dev) | |
361 | if (netif_is_bridge_master(dev)) | |
362 | br_dev_delete(dev, &list); | |
b86f81cc WC |
363 | |
364 | unregister_netdevice_many(&list); | |
b86f81cc | 365 | |
36a29fb6 | 366 | rtnl_unlock(); |
b86f81cc | 367 | } |
cf0f02d0 | 368 | |
712d6954 | 369 | static struct pernet_operations br_net_ops = { |
36a29fb6 | 370 | .exit_batch = br_net_exit_batch, |
712d6954 AD |
371 | }; |
372 | ||
b86f81cc WC |
373 | static const struct stp_proto br_stp_proto = { |
374 | .rcv = br_stp_rcv, | |
375 | }; | |
376 | ||
1da177e4 LT |
377 | static int __init br_init(void) |
378 | { | |
c0909713 SH |
379 | int err; |
380 | ||
c593642c | 381 | BUILD_BUG_ON(sizeof(struct br_input_skb_cb) > sizeof_field(struct sk_buff, cb)); |
71e168b1 | 382 | |
7c85fbf0 PM |
383 | err = stp_proto_register(&br_stp_proto); |
384 | if (err < 0) { | |
28a16c97 | 385 | pr_err("bridge: can't register sap for STP\n"); |
7c85fbf0 | 386 | return err; |
cf0f02d0 SH |
387 | } |
388 | ||
87a596e0 AM |
389 | err = br_fdb_init(); |
390 | if (err) | |
17efdd45 | 391 | goto err_out; |
1da177e4 | 392 | |
712d6954 | 393 | err = register_pernet_subsys(&br_net_ops); |
c0909713 SH |
394 | if (err) |
395 | goto err_out1; | |
396 | ||
34666d46 | 397 | err = br_nf_core_init(); |
c0909713 SH |
398 | if (err) |
399 | goto err_out2; | |
400 | ||
712d6954 | 401 | err = register_netdevice_notifier(&br_device_notifier); |
32fe21c0 TG |
402 | if (err) |
403 | goto err_out3; | |
404 | ||
ebb9a03a | 405 | err = register_switchdev_notifier(&br_switchdev_notifier); |
712d6954 AD |
406 | if (err) |
407 | goto err_out4; | |
408 | ||
957e2235 | 409 | err = register_switchdev_blocking_notifier(&br_switchdev_blocking_notifier); |
3aeb6617 JP |
410 | if (err) |
411 | goto err_out5; | |
412 | ||
957e2235 VO |
413 | err = br_netlink_init(); |
414 | if (err) | |
415 | goto err_out6; | |
416 | ||
ad2f99ae | 417 | brioctl_set(br_ioctl_stub); |
1da177e4 | 418 | |
e6373c4c | 419 | #if IS_ENABLED(CONFIG_ATM_LANE) |
da678292 MM |
420 | br_fdb_test_addr_hook = br_fdb_test_addr; |
421 | #endif | |
1da177e4 | 422 | |
d4ef9f72 SA |
423 | #if IS_MODULE(CONFIG_BRIDGE_NETFILTER) |
424 | pr_info("bridge: filtering via arp/ip/ip6tables is no longer available " | |
425 | "by default. Update your scripts to load br_netfilter if you " | |
34666d46 | 426 | "need this.\n"); |
d4ef9f72 | 427 | #endif |
34666d46 | 428 | |
1da177e4 | 429 | return 0; |
34666d46 | 430 | |
957e2235 VO |
431 | err_out6: |
432 | unregister_switchdev_blocking_notifier(&br_switchdev_blocking_notifier); | |
3aeb6617 | 433 | err_out5: |
ebb9a03a | 434 | unregister_switchdev_notifier(&br_switchdev_notifier); |
712d6954 | 435 | err_out4: |
32fe21c0 | 436 | unregister_netdevice_notifier(&br_device_notifier); |
712d6954 | 437 | err_out3: |
34666d46 | 438 | br_nf_core_fini(); |
712d6954 AD |
439 | err_out2: |
440 | unregister_pernet_subsys(&br_net_ops); | |
c0909713 | 441 | err_out1: |
17efdd45 PE |
442 | br_fdb_fini(); |
443 | err_out: | |
7c85fbf0 | 444 | stp_proto_unregister(&br_stp_proto); |
c0909713 | 445 | return err; |
1da177e4 LT |
446 | } |
447 | ||
448 | static void __exit br_deinit(void) | |
449 | { | |
7c85fbf0 | 450 | stp_proto_unregister(&br_stp_proto); |
11dc1f36 | 451 | br_netlink_fini(); |
957e2235 | 452 | unregister_switchdev_blocking_notifier(&br_switchdev_blocking_notifier); |
ebb9a03a | 453 | unregister_switchdev_notifier(&br_switchdev_notifier); |
1da177e4 LT |
454 | unregister_netdevice_notifier(&br_device_notifier); |
455 | brioctl_set(NULL); | |
712d6954 | 456 | unregister_pernet_subsys(&br_net_ops); |
1da177e4 | 457 | |
473c22d7 | 458 | rcu_barrier(); /* Wait for completion of call_rcu()'s */ |
1da177e4 | 459 | |
34666d46 | 460 | br_nf_core_fini(); |
e6373c4c | 461 | #if IS_ENABLED(CONFIG_ATM_LANE) |
da678292 MM |
462 | br_fdb_test_addr_hook = NULL; |
463 | #endif | |
1da177e4 LT |
464 | br_fdb_fini(); |
465 | } | |
466 | ||
1da177e4 LT |
467 | module_init(br_init) |
468 | module_exit(br_deinit) | |
469 | MODULE_LICENSE("GPL"); | |
8cbb512e | 470 | MODULE_VERSION(BR_VERSION); |
bb900b27 | 471 | MODULE_ALIAS_RTNL_LINK("bridge"); |