.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * Forwarding database |
---|
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/kernel.h> |
---|
.. | .. |
---|
33 | 29 | .key_offset = offsetof(struct net_bridge_fdb_entry, key), |
---|
34 | 30 | .key_len = sizeof(struct net_bridge_fdb_key), |
---|
35 | 31 | .automatic_shrinking = true, |
---|
36 | | - .locks_mul = 1, |
---|
37 | 32 | }; |
---|
38 | 33 | |
---|
39 | 34 | static struct kmem_cache *br_fdb_cache __read_mostly; |
---|
.. | .. |
---|
80 | 75 | static inline int has_expired(const struct net_bridge *br, |
---|
81 | 76 | const struct net_bridge_fdb_entry *fdb) |
---|
82 | 77 | { |
---|
83 | | - return !fdb->is_static && !fdb->added_by_external_learn && |
---|
84 | | - time_before_eq(fdb->updated + hold_time(br), jiffies); |
---|
| 78 | + return !test_bit(BR_FDB_STATIC, &fdb->flags) && |
---|
| 79 | + !test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags) && |
---|
| 80 | + time_before_eq(fdb->updated + hold_time(br), jiffies); |
---|
85 | 81 | } |
---|
86 | 82 | |
---|
87 | 83 | static void fdb_rcu_free(struct rcu_head *head) |
---|
.. | .. |
---|
202 | 198 | { |
---|
203 | 199 | trace_fdb_delete(br, f); |
---|
204 | 200 | |
---|
205 | | - if (f->is_static) |
---|
| 201 | + if (test_bit(BR_FDB_STATIC, &f->flags)) |
---|
206 | 202 | fdb_del_hw_addr(br, f->key.addr.addr); |
---|
207 | 203 | |
---|
208 | 204 | hlist_del_init_rcu(&f->fdb_node); |
---|
.. | .. |
---|
229 | 225 | if (op != p && ether_addr_equal(op->dev->dev_addr, addr) && |
---|
230 | 226 | (!vid || br_vlan_find(vg, vid))) { |
---|
231 | 227 | f->dst = op; |
---|
232 | | - f->added_by_user = 0; |
---|
| 228 | + clear_bit(BR_FDB_ADDED_BY_USER, &f->flags); |
---|
233 | 229 | return; |
---|
234 | 230 | } |
---|
235 | 231 | } |
---|
.. | .. |
---|
240 | 236 | if (p && ether_addr_equal(br->dev->dev_addr, addr) && |
---|
241 | 237 | (!vid || (v && br_vlan_should_use(v)))) { |
---|
242 | 238 | f->dst = NULL; |
---|
243 | | - f->added_by_user = 0; |
---|
| 239 | + clear_bit(BR_FDB_ADDED_BY_USER, &f->flags); |
---|
244 | 240 | return; |
---|
245 | 241 | } |
---|
246 | 242 | |
---|
.. | .. |
---|
255 | 251 | |
---|
256 | 252 | spin_lock_bh(&br->hash_lock); |
---|
257 | 253 | f = br_fdb_find(br, addr, vid); |
---|
258 | | - if (f && f->is_local && !f->added_by_user && f->dst == p) |
---|
| 254 | + if (f && test_bit(BR_FDB_LOCAL, &f->flags) && |
---|
| 255 | + !test_bit(BR_FDB_ADDED_BY_USER, &f->flags) && f->dst == p) |
---|
259 | 256 | fdb_delete_local(br, p, f); |
---|
260 | 257 | spin_unlock_bh(&br->hash_lock); |
---|
261 | 258 | } |
---|
.. | .. |
---|
270 | 267 | spin_lock_bh(&br->hash_lock); |
---|
271 | 268 | vg = nbp_vlan_group(p); |
---|
272 | 269 | hlist_for_each_entry(f, &br->fdb_list, fdb_node) { |
---|
273 | | - if (f->dst == p && f->is_local && !f->added_by_user) { |
---|
| 270 | + if (f->dst == p && test_bit(BR_FDB_LOCAL, &f->flags) && |
---|
| 271 | + !test_bit(BR_FDB_ADDED_BY_USER, &f->flags)) { |
---|
274 | 272 | /* delete old one */ |
---|
275 | 273 | fdb_delete_local(br, p, f); |
---|
276 | 274 | |
---|
.. | .. |
---|
311 | 309 | |
---|
312 | 310 | /* If old entry was unassociated with any port, then delete it. */ |
---|
313 | 311 | f = br_fdb_find(br, br->dev->dev_addr, 0); |
---|
314 | | - if (f && f->is_local && !f->dst && !f->added_by_user) |
---|
| 312 | + if (f && test_bit(BR_FDB_LOCAL, &f->flags) && |
---|
| 313 | + !f->dst && !test_bit(BR_FDB_ADDED_BY_USER, &f->flags)) |
---|
315 | 314 | fdb_delete_local(br, NULL, f); |
---|
316 | 315 | |
---|
317 | 316 | fdb_insert(br, NULL, newaddr, 0); |
---|
.. | .. |
---|
326 | 325 | if (!br_vlan_should_use(v)) |
---|
327 | 326 | continue; |
---|
328 | 327 | f = br_fdb_find(br, br->dev->dev_addr, v->vid); |
---|
329 | | - if (f && f->is_local && !f->dst && !f->added_by_user) |
---|
| 328 | + if (f && test_bit(BR_FDB_LOCAL, &f->flags) && |
---|
| 329 | + !f->dst && !test_bit(BR_FDB_ADDED_BY_USER, &f->flags)) |
---|
330 | 330 | fdb_delete_local(br, NULL, f); |
---|
331 | 331 | fdb_insert(br, NULL, newaddr, v->vid); |
---|
332 | 332 | } |
---|
.. | .. |
---|
349 | 349 | */ |
---|
350 | 350 | rcu_read_lock(); |
---|
351 | 351 | hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) { |
---|
352 | | - unsigned long this_timer; |
---|
| 352 | + unsigned long this_timer = f->updated + delay; |
---|
353 | 353 | |
---|
354 | | - if (f->is_static || f->added_by_external_learn) |
---|
| 354 | + if (test_bit(BR_FDB_STATIC, &f->flags) || |
---|
| 355 | + test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &f->flags)) { |
---|
| 356 | + if (test_bit(BR_FDB_NOTIFY, &f->flags)) { |
---|
| 357 | + if (time_after(this_timer, now)) |
---|
| 358 | + work_delay = min(work_delay, |
---|
| 359 | + this_timer - now); |
---|
| 360 | + else if (!test_and_set_bit(BR_FDB_NOTIFY_INACTIVE, |
---|
| 361 | + &f->flags)) |
---|
| 362 | + fdb_notify(br, f, RTM_NEWNEIGH, false); |
---|
| 363 | + } |
---|
355 | 364 | continue; |
---|
356 | | - this_timer = f->updated + delay; |
---|
| 365 | + } |
---|
| 366 | + |
---|
357 | 367 | if (time_after(this_timer, now)) { |
---|
358 | 368 | work_delay = min(work_delay, this_timer - now); |
---|
359 | 369 | } else { |
---|
.. | .. |
---|
378 | 388 | |
---|
379 | 389 | spin_lock_bh(&br->hash_lock); |
---|
380 | 390 | hlist_for_each_entry_safe(f, tmp, &br->fdb_list, fdb_node) { |
---|
381 | | - if (!f->is_static) |
---|
| 391 | + if (!test_bit(BR_FDB_STATIC, &f->flags)) |
---|
382 | 392 | fdb_delete(br, f, true); |
---|
383 | 393 | } |
---|
384 | 394 | spin_unlock_bh(&br->hash_lock); |
---|
.. | .. |
---|
402 | 412 | continue; |
---|
403 | 413 | |
---|
404 | 414 | if (!do_all) |
---|
405 | | - if (f->is_static || (vid && f->key.vlan_id != vid)) |
---|
| 415 | + if (test_bit(BR_FDB_STATIC, &f->flags) || |
---|
| 416 | + (test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &f->flags) && |
---|
| 417 | + !test_bit(BR_FDB_OFFLOADED, &f->flags)) || |
---|
| 418 | + (vid && f->key.vlan_id != vid)) |
---|
406 | 419 | continue; |
---|
407 | 420 | |
---|
408 | | - if (f->is_local) |
---|
| 421 | + if (test_bit(BR_FDB_LOCAL, &f->flags)) |
---|
409 | 422 | fdb_delete_local(br, p, f); |
---|
410 | 423 | else |
---|
411 | 424 | fdb_delete(br, f, true); |
---|
.. | .. |
---|
474 | 487 | fe->port_no = f->dst->port_no; |
---|
475 | 488 | fe->port_hi = f->dst->port_no >> 8; |
---|
476 | 489 | |
---|
477 | | - fe->is_local = f->is_local; |
---|
478 | | - if (!f->is_static) |
---|
| 490 | + fe->is_local = test_bit(BR_FDB_LOCAL, &f->flags); |
---|
| 491 | + if (!test_bit(BR_FDB_STATIC, &f->flags)) |
---|
479 | 492 | fe->ageing_timer_value = jiffies_delta_to_clock_t(jiffies - f->updated); |
---|
480 | 493 | ++fe; |
---|
481 | 494 | ++num; |
---|
.. | .. |
---|
489 | 502 | struct net_bridge_port *source, |
---|
490 | 503 | const unsigned char *addr, |
---|
491 | 504 | __u16 vid, |
---|
492 | | - unsigned char is_local, |
---|
493 | | - unsigned char is_static) |
---|
| 505 | + unsigned long flags) |
---|
494 | 506 | { |
---|
495 | 507 | struct net_bridge_fdb_entry *fdb; |
---|
496 | 508 | |
---|
.. | .. |
---|
499 | 511 | memcpy(fdb->key.addr.addr, addr, ETH_ALEN); |
---|
500 | 512 | fdb->dst = source; |
---|
501 | 513 | fdb->key.vlan_id = vid; |
---|
502 | | - fdb->is_local = is_local; |
---|
503 | | - fdb->is_static = is_static; |
---|
504 | | - fdb->added_by_user = 0; |
---|
505 | | - fdb->added_by_external_learn = 0; |
---|
506 | | - fdb->offloaded = 0; |
---|
| 514 | + fdb->flags = flags; |
---|
507 | 515 | fdb->updated = fdb->used = jiffies; |
---|
508 | 516 | if (rhashtable_lookup_insert_fast(&br->fdb_hash_tbl, |
---|
509 | 517 | &fdb->rhnode, |
---|
.. | .. |
---|
530 | 538 | /* it is okay to have multiple ports with same |
---|
531 | 539 | * address, just use the first one. |
---|
532 | 540 | */ |
---|
533 | | - if (fdb->is_local) |
---|
| 541 | + if (test_bit(BR_FDB_LOCAL, &fdb->flags)) |
---|
534 | 542 | return 0; |
---|
535 | 543 | br_warn(br, "adding interface %s with same address as a received packet (addr:%pM, vlan:%u)\n", |
---|
536 | 544 | source ? source->dev->name : br->dev->name, addr, vid); |
---|
537 | 545 | fdb_delete(br, fdb, true); |
---|
538 | 546 | } |
---|
539 | 547 | |
---|
540 | | - fdb = fdb_create(br, source, addr, vid, 1, 1); |
---|
| 548 | + fdb = fdb_create(br, source, addr, vid, |
---|
| 549 | + BIT(BR_FDB_LOCAL) | BIT(BR_FDB_STATIC)); |
---|
541 | 550 | if (!fdb) |
---|
542 | 551 | return -ENOMEM; |
---|
543 | 552 | |
---|
.. | .. |
---|
558 | 567 | return ret; |
---|
559 | 568 | } |
---|
560 | 569 | |
---|
| 570 | +/* returns true if the fdb was modified */ |
---|
| 571 | +static bool __fdb_mark_active(struct net_bridge_fdb_entry *fdb) |
---|
| 572 | +{ |
---|
| 573 | + return !!(test_bit(BR_FDB_NOTIFY_INACTIVE, &fdb->flags) && |
---|
| 574 | + test_and_clear_bit(BR_FDB_NOTIFY_INACTIVE, &fdb->flags)); |
---|
| 575 | +} |
---|
| 576 | + |
---|
561 | 577 | void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, |
---|
562 | | - const unsigned char *addr, u16 vid, bool added_by_user) |
---|
| 578 | + const unsigned char *addr, u16 vid, unsigned long flags) |
---|
563 | 579 | { |
---|
564 | 580 | struct net_bridge_fdb_entry *fdb; |
---|
565 | | - bool fdb_modified = false; |
---|
566 | 581 | |
---|
567 | 582 | /* some users want to always flood. */ |
---|
568 | 583 | if (hold_time(br) == 0) |
---|
569 | 584 | return; |
---|
570 | 585 | |
---|
571 | | - /* ignore packets unless we are using this port */ |
---|
572 | | - if (!(source->state == BR_STATE_LEARNING || |
---|
573 | | - source->state == BR_STATE_FORWARDING)) |
---|
574 | | - return; |
---|
575 | | - |
---|
576 | 586 | fdb = fdb_find_rcu(&br->fdb_hash_tbl, addr, vid); |
---|
577 | 587 | if (likely(fdb)) { |
---|
578 | 588 | /* attempt to update an entry for a local interface */ |
---|
579 | | - if (unlikely(fdb->is_local)) { |
---|
| 589 | + if (unlikely(test_bit(BR_FDB_LOCAL, &fdb->flags))) { |
---|
580 | 590 | if (net_ratelimit()) |
---|
581 | 591 | br_warn(br, "received packet on %s with own address as source address (addr:%pM, vlan:%u)\n", |
---|
582 | 592 | source->dev->name, addr, vid); |
---|
583 | 593 | } else { |
---|
584 | 594 | unsigned long now = jiffies; |
---|
| 595 | + bool fdb_modified = false; |
---|
| 596 | + |
---|
| 597 | + if (now != fdb->updated) { |
---|
| 598 | + fdb->updated = now; |
---|
| 599 | + fdb_modified = __fdb_mark_active(fdb); |
---|
| 600 | + } |
---|
585 | 601 | |
---|
586 | 602 | /* fastpath: update of existing entry */ |
---|
587 | | - if (unlikely(source != fdb->dst)) { |
---|
| 603 | + if (unlikely(source != fdb->dst && |
---|
| 604 | + !test_bit(BR_FDB_STICKY, &fdb->flags))) { |
---|
588 | 605 | fdb->dst = source; |
---|
589 | 606 | fdb_modified = true; |
---|
590 | 607 | /* Take over HW learned entry */ |
---|
591 | | - if (unlikely(fdb->added_by_external_learn)) |
---|
592 | | - fdb->added_by_external_learn = 0; |
---|
| 608 | + if (unlikely(test_bit(BR_FDB_ADDED_BY_EXT_LEARN, |
---|
| 609 | + &fdb->flags))) |
---|
| 610 | + clear_bit(BR_FDB_ADDED_BY_EXT_LEARN, |
---|
| 611 | + &fdb->flags); |
---|
593 | 612 | } |
---|
594 | | - if (now != fdb->updated) |
---|
595 | | - fdb->updated = now; |
---|
596 | | - if (unlikely(added_by_user)) |
---|
597 | | - fdb->added_by_user = 1; |
---|
| 613 | + |
---|
| 614 | + if (unlikely(test_bit(BR_FDB_ADDED_BY_USER, &flags))) |
---|
| 615 | + set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags); |
---|
598 | 616 | if (unlikely(fdb_modified)) { |
---|
599 | | - trace_br_fdb_update(br, source, addr, vid, added_by_user); |
---|
| 617 | + trace_br_fdb_update(br, source, addr, vid, flags); |
---|
600 | 618 | fdb_notify(br, fdb, RTM_NEWNEIGH, true); |
---|
601 | 619 | } |
---|
602 | 620 | } |
---|
603 | 621 | } else { |
---|
604 | 622 | spin_lock(&br->hash_lock); |
---|
605 | | - fdb = fdb_create(br, source, addr, vid, 0, 0); |
---|
| 623 | + fdb = fdb_create(br, source, addr, vid, flags); |
---|
606 | 624 | if (fdb) { |
---|
607 | | - if (unlikely(added_by_user)) |
---|
608 | | - fdb->added_by_user = 1; |
---|
609 | | - trace_br_fdb_update(br, source, addr, vid, |
---|
610 | | - added_by_user); |
---|
| 625 | + trace_br_fdb_update(br, source, addr, vid, flags); |
---|
611 | 626 | fdb_notify(br, fdb, RTM_NEWNEIGH, true); |
---|
612 | 627 | } |
---|
613 | 628 | /* else we lose race and someone else inserts |
---|
.. | .. |
---|
620 | 635 | static int fdb_to_nud(const struct net_bridge *br, |
---|
621 | 636 | const struct net_bridge_fdb_entry *fdb) |
---|
622 | 637 | { |
---|
623 | | - if (fdb->is_local) |
---|
| 638 | + if (test_bit(BR_FDB_LOCAL, &fdb->flags)) |
---|
624 | 639 | return NUD_PERMANENT; |
---|
625 | | - else if (fdb->is_static) |
---|
| 640 | + else if (test_bit(BR_FDB_STATIC, &fdb->flags)) |
---|
626 | 641 | return NUD_NOARP; |
---|
627 | 642 | else if (has_expired(br, fdb)) |
---|
628 | 643 | return NUD_STALE; |
---|
.. | .. |
---|
652 | 667 | ndm->ndm_ifindex = fdb->dst ? fdb->dst->dev->ifindex : br->dev->ifindex; |
---|
653 | 668 | ndm->ndm_state = fdb_to_nud(br, fdb); |
---|
654 | 669 | |
---|
655 | | - if (fdb->offloaded) |
---|
| 670 | + if (test_bit(BR_FDB_OFFLOADED, &fdb->flags)) |
---|
656 | 671 | ndm->ndm_flags |= NTF_OFFLOADED; |
---|
657 | | - if (fdb->added_by_external_learn) |
---|
| 672 | + if (test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags)) |
---|
658 | 673 | ndm->ndm_flags |= NTF_EXT_LEARNED; |
---|
| 674 | + if (test_bit(BR_FDB_STICKY, &fdb->flags)) |
---|
| 675 | + ndm->ndm_flags |= NTF_STICKY; |
---|
659 | 676 | |
---|
660 | 677 | if (nla_put(skb, NDA_LLADDR, ETH_ALEN, &fdb->key.addr)) |
---|
661 | 678 | goto nla_put_failure; |
---|
.. | .. |
---|
672 | 689 | &fdb->key.vlan_id)) |
---|
673 | 690 | goto nla_put_failure; |
---|
674 | 691 | |
---|
| 692 | + if (test_bit(BR_FDB_NOTIFY, &fdb->flags)) { |
---|
| 693 | + struct nlattr *nest = nla_nest_start(skb, NDA_FDB_EXT_ATTRS); |
---|
| 694 | + u8 notify_bits = FDB_NOTIFY_BIT; |
---|
| 695 | + |
---|
| 696 | + if (!nest) |
---|
| 697 | + goto nla_put_failure; |
---|
| 698 | + if (test_bit(BR_FDB_NOTIFY_INACTIVE, &fdb->flags)) |
---|
| 699 | + notify_bits |= FDB_NOTIFY_INACTIVE_BIT; |
---|
| 700 | + |
---|
| 701 | + if (nla_put_u8(skb, NFEA_ACTIVITY_NOTIFY, notify_bits)) { |
---|
| 702 | + nla_nest_cancel(skb, nest); |
---|
| 703 | + goto nla_put_failure; |
---|
| 704 | + } |
---|
| 705 | + |
---|
| 706 | + nla_nest_end(skb, nest); |
---|
| 707 | + } |
---|
| 708 | + |
---|
675 | 709 | nlmsg_end(skb, nlh); |
---|
676 | 710 | return 0; |
---|
677 | 711 | |
---|
.. | .. |
---|
686 | 720 | + nla_total_size(ETH_ALEN) /* NDA_LLADDR */ |
---|
687 | 721 | + nla_total_size(sizeof(u32)) /* NDA_MASTER */ |
---|
688 | 722 | + nla_total_size(sizeof(u16)) /* NDA_VLAN */ |
---|
689 | | - + nla_total_size(sizeof(struct nda_cacheinfo)); |
---|
| 723 | + + nla_total_size(sizeof(struct nda_cacheinfo)) |
---|
| 724 | + + nla_total_size(0) /* NDA_FDB_EXT_ATTRS */ |
---|
| 725 | + + nla_total_size(sizeof(u8)); /* NFEA_ACTIVITY_NOTIFY */ |
---|
690 | 726 | } |
---|
691 | 727 | |
---|
692 | 728 | static void fdb_notify(struct net_bridge *br, |
---|
.. | .. |
---|
770 | 806 | return err; |
---|
771 | 807 | } |
---|
772 | 808 | |
---|
| 809 | +int br_fdb_get(struct sk_buff *skb, |
---|
| 810 | + struct nlattr *tb[], |
---|
| 811 | + struct net_device *dev, |
---|
| 812 | + const unsigned char *addr, |
---|
| 813 | + u16 vid, u32 portid, u32 seq, |
---|
| 814 | + struct netlink_ext_ack *extack) |
---|
| 815 | +{ |
---|
| 816 | + struct net_bridge *br = netdev_priv(dev); |
---|
| 817 | + struct net_bridge_fdb_entry *f; |
---|
| 818 | + int err = 0; |
---|
| 819 | + |
---|
| 820 | + rcu_read_lock(); |
---|
| 821 | + f = br_fdb_find_rcu(br, addr, vid); |
---|
| 822 | + if (!f) { |
---|
| 823 | + NL_SET_ERR_MSG(extack, "Fdb entry not found"); |
---|
| 824 | + err = -ENOENT; |
---|
| 825 | + goto errout; |
---|
| 826 | + } |
---|
| 827 | + |
---|
| 828 | + err = fdb_fill_info(skb, br, f, portid, seq, |
---|
| 829 | + RTM_NEWNEIGH, 0); |
---|
| 830 | +errout: |
---|
| 831 | + rcu_read_unlock(); |
---|
| 832 | + return err; |
---|
| 833 | +} |
---|
| 834 | + |
---|
| 835 | +/* returns true if the fdb is modified */ |
---|
| 836 | +static bool fdb_handle_notify(struct net_bridge_fdb_entry *fdb, u8 notify) |
---|
| 837 | +{ |
---|
| 838 | + bool modified = false; |
---|
| 839 | + |
---|
| 840 | + /* allow to mark an entry as inactive, usually done on creation */ |
---|
| 841 | + if ((notify & FDB_NOTIFY_INACTIVE_BIT) && |
---|
| 842 | + !test_and_set_bit(BR_FDB_NOTIFY_INACTIVE, &fdb->flags)) |
---|
| 843 | + modified = true; |
---|
| 844 | + |
---|
| 845 | + if ((notify & FDB_NOTIFY_BIT) && |
---|
| 846 | + !test_and_set_bit(BR_FDB_NOTIFY, &fdb->flags)) { |
---|
| 847 | + /* enabled activity tracking */ |
---|
| 848 | + modified = true; |
---|
| 849 | + } else if (!(notify & FDB_NOTIFY_BIT) && |
---|
| 850 | + test_and_clear_bit(BR_FDB_NOTIFY, &fdb->flags)) { |
---|
| 851 | + /* disabled activity tracking, clear notify state */ |
---|
| 852 | + clear_bit(BR_FDB_NOTIFY_INACTIVE, &fdb->flags); |
---|
| 853 | + modified = true; |
---|
| 854 | + } |
---|
| 855 | + |
---|
| 856 | + return modified; |
---|
| 857 | +} |
---|
| 858 | + |
---|
773 | 859 | /* Update (create or replace) forwarding database entry */ |
---|
774 | 860 | static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source, |
---|
775 | | - const __u8 *addr, __u16 state, __u16 flags, __u16 vid) |
---|
| 861 | + const u8 *addr, struct ndmsg *ndm, u16 flags, u16 vid, |
---|
| 862 | + struct nlattr *nfea_tb[]) |
---|
776 | 863 | { |
---|
| 864 | + bool is_sticky = !!(ndm->ndm_flags & NTF_STICKY); |
---|
| 865 | + bool refresh = !nfea_tb[NFEA_DONT_REFRESH]; |
---|
777 | 866 | struct net_bridge_fdb_entry *fdb; |
---|
| 867 | + u16 state = ndm->ndm_state; |
---|
778 | 868 | bool modified = false; |
---|
| 869 | + u8 notify = 0; |
---|
779 | 870 | |
---|
780 | 871 | /* If the port cannot learn allow only local and static entries */ |
---|
781 | 872 | if (source && !(state & NUD_PERMANENT) && !(state & NUD_NOARP) && |
---|
.. | .. |
---|
789 | 880 | return -EINVAL; |
---|
790 | 881 | } |
---|
791 | 882 | |
---|
| 883 | + if (is_sticky && (state & NUD_PERMANENT)) |
---|
| 884 | + return -EINVAL; |
---|
| 885 | + |
---|
| 886 | + if (nfea_tb[NFEA_ACTIVITY_NOTIFY]) { |
---|
| 887 | + notify = nla_get_u8(nfea_tb[NFEA_ACTIVITY_NOTIFY]); |
---|
| 888 | + if ((notify & ~BR_FDB_NOTIFY_SETTABLE_BITS) || |
---|
| 889 | + (notify & BR_FDB_NOTIFY_SETTABLE_BITS) == FDB_NOTIFY_INACTIVE_BIT) |
---|
| 890 | + return -EINVAL; |
---|
| 891 | + } |
---|
| 892 | + |
---|
792 | 893 | fdb = br_fdb_find(br, addr, vid); |
---|
793 | 894 | if (fdb == NULL) { |
---|
794 | 895 | if (!(flags & NLM_F_CREATE)) |
---|
795 | 896 | return -ENOENT; |
---|
796 | 897 | |
---|
797 | | - fdb = fdb_create(br, source, addr, vid, 0, 0); |
---|
| 898 | + fdb = fdb_create(br, source, addr, vid, 0); |
---|
798 | 899 | if (!fdb) |
---|
799 | 900 | return -ENOMEM; |
---|
800 | 901 | |
---|
.. | .. |
---|
811 | 912 | |
---|
812 | 913 | if (fdb_to_nud(br, fdb) != state) { |
---|
813 | 914 | if (state & NUD_PERMANENT) { |
---|
814 | | - fdb->is_local = 1; |
---|
815 | | - if (!fdb->is_static) { |
---|
816 | | - fdb->is_static = 1; |
---|
| 915 | + set_bit(BR_FDB_LOCAL, &fdb->flags); |
---|
| 916 | + if (!test_and_set_bit(BR_FDB_STATIC, &fdb->flags)) |
---|
817 | 917 | fdb_add_hw_addr(br, addr); |
---|
818 | | - } |
---|
819 | 918 | } else if (state & NUD_NOARP) { |
---|
820 | | - fdb->is_local = 0; |
---|
821 | | - if (!fdb->is_static) { |
---|
822 | | - fdb->is_static = 1; |
---|
| 919 | + clear_bit(BR_FDB_LOCAL, &fdb->flags); |
---|
| 920 | + if (!test_and_set_bit(BR_FDB_STATIC, &fdb->flags)) |
---|
823 | 921 | fdb_add_hw_addr(br, addr); |
---|
824 | | - } |
---|
825 | 922 | } else { |
---|
826 | | - fdb->is_local = 0; |
---|
827 | | - if (fdb->is_static) { |
---|
828 | | - fdb->is_static = 0; |
---|
| 923 | + clear_bit(BR_FDB_LOCAL, &fdb->flags); |
---|
| 924 | + if (test_and_clear_bit(BR_FDB_STATIC, &fdb->flags)) |
---|
829 | 925 | fdb_del_hw_addr(br, addr); |
---|
830 | | - } |
---|
831 | 926 | } |
---|
832 | 927 | |
---|
833 | 928 | modified = true; |
---|
834 | 929 | } |
---|
835 | | - fdb->added_by_user = 1; |
---|
| 930 | + |
---|
| 931 | + if (is_sticky != test_bit(BR_FDB_STICKY, &fdb->flags)) { |
---|
| 932 | + change_bit(BR_FDB_STICKY, &fdb->flags); |
---|
| 933 | + modified = true; |
---|
| 934 | + } |
---|
| 935 | + |
---|
| 936 | + if (fdb_handle_notify(fdb, notify)) |
---|
| 937 | + modified = true; |
---|
| 938 | + |
---|
| 939 | + set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags); |
---|
836 | 940 | |
---|
837 | 941 | fdb->used = jiffies; |
---|
838 | 942 | if (modified) { |
---|
839 | | - fdb->updated = jiffies; |
---|
| 943 | + if (refresh) |
---|
| 944 | + fdb->updated = jiffies; |
---|
840 | 945 | fdb_notify(br, fdb, RTM_NEWNEIGH, true); |
---|
841 | 946 | } |
---|
842 | 947 | |
---|
.. | .. |
---|
845 | 950 | |
---|
846 | 951 | static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge *br, |
---|
847 | 952 | struct net_bridge_port *p, const unsigned char *addr, |
---|
848 | | - u16 nlh_flags, u16 vid) |
---|
| 953 | + u16 nlh_flags, u16 vid, struct nlattr *nfea_tb[], |
---|
| 954 | + struct netlink_ext_ack *extack) |
---|
849 | 955 | { |
---|
850 | 956 | int err = 0; |
---|
851 | 957 | |
---|
.. | .. |
---|
855 | 961 | br->dev->name); |
---|
856 | 962 | return -EINVAL; |
---|
857 | 963 | } |
---|
| 964 | + if (!nbp_state_should_learn(p)) |
---|
| 965 | + return 0; |
---|
| 966 | + |
---|
858 | 967 | local_bh_disable(); |
---|
859 | 968 | rcu_read_lock(); |
---|
860 | | - br_fdb_update(br, p, addr, vid, true); |
---|
| 969 | + br_fdb_update(br, p, addr, vid, BIT(BR_FDB_ADDED_BY_USER)); |
---|
861 | 970 | rcu_read_unlock(); |
---|
862 | 971 | local_bh_enable(); |
---|
863 | 972 | } else if (ndm->ndm_flags & NTF_EXT_LEARNED) { |
---|
| 973 | + if (!p && !(ndm->ndm_state & NUD_PERMANENT)) { |
---|
| 974 | + NL_SET_ERR_MSG_MOD(extack, |
---|
| 975 | + "FDB entry towards bridge must be permanent"); |
---|
| 976 | + return -EINVAL; |
---|
| 977 | + } |
---|
864 | 978 | err = br_fdb_external_learn_add(br, p, addr, vid, true); |
---|
865 | 979 | } else { |
---|
866 | 980 | spin_lock_bh(&br->hash_lock); |
---|
867 | | - err = fdb_add_entry(br, p, addr, ndm->ndm_state, |
---|
868 | | - nlh_flags, vid); |
---|
| 981 | + err = fdb_add_entry(br, p, addr, ndm, nlh_flags, vid, nfea_tb); |
---|
869 | 982 | spin_unlock_bh(&br->hash_lock); |
---|
870 | 983 | } |
---|
871 | 984 | |
---|
872 | 985 | return err; |
---|
873 | 986 | } |
---|
874 | 987 | |
---|
| 988 | +static const struct nla_policy br_nda_fdb_pol[NFEA_MAX + 1] = { |
---|
| 989 | + [NFEA_ACTIVITY_NOTIFY] = { .type = NLA_U8 }, |
---|
| 990 | + [NFEA_DONT_REFRESH] = { .type = NLA_FLAG }, |
---|
| 991 | +}; |
---|
| 992 | + |
---|
875 | 993 | /* Add new permanent fdb entry with RTM_NEWNEIGH */ |
---|
876 | 994 | int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], |
---|
877 | 995 | struct net_device *dev, |
---|
878 | | - const unsigned char *addr, u16 vid, u16 nlh_flags) |
---|
| 996 | + const unsigned char *addr, u16 vid, u16 nlh_flags, |
---|
| 997 | + struct netlink_ext_ack *extack) |
---|
879 | 998 | { |
---|
| 999 | + struct nlattr *nfea_tb[NFEA_MAX + 1], *attr; |
---|
880 | 1000 | struct net_bridge_vlan_group *vg; |
---|
881 | 1001 | struct net_bridge_port *p = NULL; |
---|
882 | 1002 | struct net_bridge_vlan *v; |
---|
.. | .. |
---|
909 | 1029 | vg = nbp_vlan_group(p); |
---|
910 | 1030 | } |
---|
911 | 1031 | |
---|
| 1032 | + if (tb[NDA_FDB_EXT_ATTRS]) { |
---|
| 1033 | + attr = tb[NDA_FDB_EXT_ATTRS]; |
---|
| 1034 | + err = nla_parse_nested(nfea_tb, NFEA_MAX, attr, |
---|
| 1035 | + br_nda_fdb_pol, extack); |
---|
| 1036 | + if (err) |
---|
| 1037 | + return err; |
---|
| 1038 | + } else { |
---|
| 1039 | + memset(nfea_tb, 0, sizeof(struct nlattr *) * (NFEA_MAX + 1)); |
---|
| 1040 | + } |
---|
| 1041 | + |
---|
912 | 1042 | if (vid) { |
---|
913 | 1043 | v = br_vlan_find(vg, vid); |
---|
914 | 1044 | if (!v || !br_vlan_should_use(v)) { |
---|
.. | .. |
---|
917 | 1047 | } |
---|
918 | 1048 | |
---|
919 | 1049 | /* VID was specified, so use it. */ |
---|
920 | | - err = __br_fdb_add(ndm, br, p, addr, nlh_flags, vid); |
---|
| 1050 | + err = __br_fdb_add(ndm, br, p, addr, nlh_flags, vid, nfea_tb, |
---|
| 1051 | + extack); |
---|
921 | 1052 | } else { |
---|
922 | | - err = __br_fdb_add(ndm, br, p, addr, nlh_flags, 0); |
---|
| 1053 | + err = __br_fdb_add(ndm, br, p, addr, nlh_flags, 0, nfea_tb, |
---|
| 1054 | + extack); |
---|
923 | 1055 | if (err || !vg || !vg->num_vlans) |
---|
924 | 1056 | goto out; |
---|
925 | 1057 | |
---|
.. | .. |
---|
930 | 1062 | list_for_each_entry(v, &vg->vlan_list, vlist) { |
---|
931 | 1063 | if (!br_vlan_should_use(v)) |
---|
932 | 1064 | continue; |
---|
933 | | - err = __br_fdb_add(ndm, br, p, addr, nlh_flags, v->vid); |
---|
| 1065 | + err = __br_fdb_add(ndm, br, p, addr, nlh_flags, v->vid, |
---|
| 1066 | + nfea_tb, extack); |
---|
934 | 1067 | if (err) |
---|
935 | 1068 | goto out; |
---|
936 | 1069 | } |
---|
.. | .. |
---|
1028 | 1161 | rcu_read_lock(); |
---|
1029 | 1162 | hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) { |
---|
1030 | 1163 | /* We only care for static entries */ |
---|
1031 | | - if (!f->is_static) |
---|
| 1164 | + if (!test_bit(BR_FDB_STATIC, &f->flags)) |
---|
1032 | 1165 | continue; |
---|
1033 | 1166 | err = dev_uc_add(p->dev, f->key.addr.addr); |
---|
1034 | 1167 | if (err) |
---|
.. | .. |
---|
1042 | 1175 | rollback: |
---|
1043 | 1176 | hlist_for_each_entry_rcu(tmp, &br->fdb_list, fdb_node) { |
---|
1044 | 1177 | /* We only care for static entries */ |
---|
1045 | | - if (!tmp->is_static) |
---|
| 1178 | + if (!test_bit(BR_FDB_STATIC, &tmp->flags)) |
---|
1046 | 1179 | continue; |
---|
1047 | 1180 | if (tmp == f) |
---|
1048 | 1181 | break; |
---|
.. | .. |
---|
1061 | 1194 | rcu_read_lock(); |
---|
1062 | 1195 | hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) { |
---|
1063 | 1196 | /* We only care for static entries */ |
---|
1064 | | - if (!f->is_static) |
---|
| 1197 | + if (!test_bit(BR_FDB_STATIC, &f->flags)) |
---|
1065 | 1198 | continue; |
---|
1066 | 1199 | |
---|
1067 | 1200 | dev_uc_del(p->dev, f->key.addr.addr); |
---|
.. | .. |
---|
1083 | 1216 | |
---|
1084 | 1217 | fdb = br_fdb_find(br, addr, vid); |
---|
1085 | 1218 | if (!fdb) { |
---|
1086 | | - fdb = fdb_create(br, p, addr, vid, 0, 0); |
---|
| 1219 | + unsigned long flags = BIT(BR_FDB_ADDED_BY_EXT_LEARN); |
---|
| 1220 | + |
---|
| 1221 | + if (swdev_notify) |
---|
| 1222 | + flags |= BIT(BR_FDB_ADDED_BY_USER); |
---|
| 1223 | + |
---|
| 1224 | + if (!p) |
---|
| 1225 | + flags |= BIT(BR_FDB_LOCAL); |
---|
| 1226 | + |
---|
| 1227 | + fdb = fdb_create(br, p, addr, vid, flags); |
---|
1087 | 1228 | if (!fdb) { |
---|
1088 | 1229 | err = -ENOMEM; |
---|
1089 | 1230 | goto err_unlock; |
---|
1090 | 1231 | } |
---|
1091 | | - if (swdev_notify) |
---|
1092 | | - fdb->added_by_user = 1; |
---|
1093 | | - fdb->added_by_external_learn = 1; |
---|
1094 | 1232 | fdb_notify(br, fdb, RTM_NEWNEIGH, swdev_notify); |
---|
1095 | 1233 | } else { |
---|
1096 | 1234 | fdb->updated = jiffies; |
---|
.. | .. |
---|
1100 | 1238 | modified = true; |
---|
1101 | 1239 | } |
---|
1102 | 1240 | |
---|
1103 | | - if (fdb->added_by_external_learn) { |
---|
| 1241 | + if (test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags)) { |
---|
1104 | 1242 | /* Refresh entry */ |
---|
1105 | 1243 | fdb->used = jiffies; |
---|
1106 | | - } else if (!fdb->added_by_user) { |
---|
| 1244 | + } else if (!test_bit(BR_FDB_ADDED_BY_USER, &fdb->flags)) { |
---|
1107 | 1245 | /* Take over SW learned entry */ |
---|
1108 | | - fdb->added_by_external_learn = 1; |
---|
| 1246 | + set_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags); |
---|
1109 | 1247 | modified = true; |
---|
1110 | 1248 | } |
---|
1111 | 1249 | |
---|
1112 | 1250 | if (swdev_notify) |
---|
1113 | | - fdb->added_by_user = 1; |
---|
| 1251 | + set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags); |
---|
| 1252 | + |
---|
| 1253 | + if (!p) |
---|
| 1254 | + set_bit(BR_FDB_LOCAL, &fdb->flags); |
---|
1114 | 1255 | |
---|
1115 | 1256 | if (modified) |
---|
1116 | 1257 | fdb_notify(br, fdb, RTM_NEWNEIGH, swdev_notify); |
---|
.. | .. |
---|
1132 | 1273 | spin_lock_bh(&br->hash_lock); |
---|
1133 | 1274 | |
---|
1134 | 1275 | fdb = br_fdb_find(br, addr, vid); |
---|
1135 | | - if (fdb && fdb->added_by_external_learn) |
---|
| 1276 | + if (fdb && test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags)) |
---|
1136 | 1277 | fdb_delete(br, fdb, swdev_notify); |
---|
1137 | 1278 | else |
---|
1138 | 1279 | err = -ENOENT; |
---|
.. | .. |
---|
1143 | 1284 | } |
---|
1144 | 1285 | |
---|
1145 | 1286 | void br_fdb_offloaded_set(struct net_bridge *br, struct net_bridge_port *p, |
---|
1146 | | - const unsigned char *addr, u16 vid) |
---|
| 1287 | + const unsigned char *addr, u16 vid, bool offloaded) |
---|
1147 | 1288 | { |
---|
1148 | 1289 | struct net_bridge_fdb_entry *fdb; |
---|
1149 | 1290 | |
---|
1150 | 1291 | spin_lock_bh(&br->hash_lock); |
---|
1151 | 1292 | |
---|
1152 | 1293 | fdb = br_fdb_find(br, addr, vid); |
---|
1153 | | - if (fdb) |
---|
1154 | | - fdb->offloaded = 1; |
---|
| 1294 | + if (fdb && offloaded != test_bit(BR_FDB_OFFLOADED, &fdb->flags)) |
---|
| 1295 | + change_bit(BR_FDB_OFFLOADED, &fdb->flags); |
---|
1155 | 1296 | |
---|
1156 | 1297 | spin_unlock_bh(&br->hash_lock); |
---|
1157 | 1298 | } |
---|
| 1299 | + |
---|
| 1300 | +void br_fdb_clear_offload(const struct net_device *dev, u16 vid) |
---|
| 1301 | +{ |
---|
| 1302 | + struct net_bridge_fdb_entry *f; |
---|
| 1303 | + struct net_bridge_port *p; |
---|
| 1304 | + |
---|
| 1305 | + ASSERT_RTNL(); |
---|
| 1306 | + |
---|
| 1307 | + p = br_port_get_rtnl(dev); |
---|
| 1308 | + if (!p) |
---|
| 1309 | + return; |
---|
| 1310 | + |
---|
| 1311 | + spin_lock_bh(&p->br->hash_lock); |
---|
| 1312 | + hlist_for_each_entry(f, &p->br->fdb_list, fdb_node) { |
---|
| 1313 | + if (f->dst == p && f->key.vlan_id == vid) |
---|
| 1314 | + clear_bit(BR_FDB_OFFLOADED, &f->flags); |
---|
| 1315 | + } |
---|
| 1316 | + spin_unlock_bh(&p->br->hash_lock); |
---|
| 1317 | +} |
---|
| 1318 | +EXPORT_SYMBOL_GPL(br_fdb_clear_offload); |
---|