diff options
Diffstat (limited to 'net/bridge/br_fdb.c')
-rw-r--r-- | net/bridge/br_fdb.c | 38 |
1 files changed, 27 insertions, 11 deletions
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index c8e7861b88b0..973813e34428 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c | |||
@@ -556,7 +556,7 @@ skip: | |||
556 | return skb->len; | 556 | return skb->len; |
557 | } | 557 | } |
558 | 558 | ||
559 | /* Create new static fdb entry */ | 559 | /* Update (create or replace) forwarding database entry */ |
560 | static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr, | 560 | static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr, |
561 | __u16 state, __u16 flags) | 561 | __u16 state, __u16 flags) |
562 | { | 562 | { |
@@ -575,16 +575,21 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr, | |||
575 | } else { | 575 | } else { |
576 | if (flags & NLM_F_EXCL) | 576 | if (flags & NLM_F_EXCL) |
577 | return -EEXIST; | 577 | return -EEXIST; |
578 | } | ||
579 | |||
580 | if (fdb_to_nud(fdb) != state) { | ||
581 | if (state & NUD_PERMANENT) | ||
582 | fdb->is_local = fdb->is_static = 1; | ||
583 | else if (state & NUD_NOARP) { | ||
584 | fdb->is_local = 0; | ||
585 | fdb->is_static = 1; | ||
586 | } else | ||
587 | fdb->is_local = fdb->is_static = 0; | ||
578 | 588 | ||
579 | if (flags & NLM_F_REPLACE) | 589 | fdb->updated = fdb->used = jiffies; |
580 | fdb->updated = fdb->used = jiffies; | 590 | fdb_notify(fdb, RTM_NEWNEIGH); |
581 | fdb->is_local = fdb->is_static = 0; | ||
582 | } | 591 | } |
583 | 592 | ||
584 | if (state & NUD_PERMANENT) | ||
585 | fdb->is_local = fdb->is_static = 1; | ||
586 | else if (state & NUD_NOARP) | ||
587 | fdb->is_static = 1; | ||
588 | return 0; | 593 | return 0; |
589 | } | 594 | } |
590 | 595 | ||
@@ -627,6 +632,11 @@ int br_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | |||
627 | return -EINVAL; | 632 | return -EINVAL; |
628 | } | 633 | } |
629 | 634 | ||
635 | if (!(ndm->ndm_state & (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE))) { | ||
636 | pr_info("bridge: RTM_NEWNEIGH with invalid state %#x\n", ndm->ndm_state); | ||
637 | return -EINVAL; | ||
638 | } | ||
639 | |||
630 | p = br_port_get_rtnl(dev); | 640 | p = br_port_get_rtnl(dev); |
631 | if (p == NULL) { | 641 | if (p == NULL) { |
632 | pr_info("bridge: RTM_NEWNEIGH %s not a bridge port\n", | 642 | pr_info("bridge: RTM_NEWNEIGH %s not a bridge port\n", |
@@ -634,9 +644,15 @@ int br_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | |||
634 | return -EINVAL; | 644 | return -EINVAL; |
635 | } | 645 | } |
636 | 646 | ||
637 | spin_lock_bh(&p->br->hash_lock); | 647 | if (ndm->ndm_flags & NTF_USE) { |
638 | err = fdb_add_entry(p, addr, ndm->ndm_state, nlh->nlmsg_flags); | 648 | rcu_read_lock(); |
639 | spin_unlock_bh(&p->br->hash_lock); | 649 | br_fdb_update(p->br, p, addr); |
650 | rcu_read_unlock(); | ||
651 | } else { | ||
652 | spin_lock_bh(&p->br->hash_lock); | ||
653 | err = fdb_add_entry(p, addr, ndm->ndm_state, nlh->nlmsg_flags); | ||
654 | spin_unlock_bh(&p->br->hash_lock); | ||
655 | } | ||
640 | 656 | ||
641 | return err; | 657 | return err; |
642 | } | 658 | } |