aboutsummaryrefslogtreecommitdiffstats
path: root/net/bridge/br_fdb.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/bridge/br_fdb.c')
-rw-r--r--net/bridge/br_fdb.c128
1 files changed, 31 insertions, 97 deletions
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index 80dbce4974ce..5945c54bc2de 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -535,44 +535,38 @@ errout:
535} 535}
536 536
537/* Dump information about entries, in response to GETNEIGH */ 537/* Dump information about entries, in response to GETNEIGH */
538int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb) 538int br_fdb_dump(struct sk_buff *skb,
539 struct netlink_callback *cb,
540 struct net_device *dev,
541 int idx)
539{ 542{
540 struct net *net = sock_net(skb->sk); 543 struct net_bridge *br = netdev_priv(dev);
541 struct net_device *dev; 544 int i;
542 int idx = 0;
543
544 rcu_read_lock();
545 for_each_netdev_rcu(net, dev) {
546 struct net_bridge *br = netdev_priv(dev);
547 int i;
548
549 if (!(dev->priv_flags & IFF_EBRIDGE))
550 continue;
551 545
552 for (i = 0; i < BR_HASH_SIZE; i++) { 546 if (!(dev->priv_flags & IFF_EBRIDGE))
553 struct hlist_node *h; 547 goto out;
554 struct net_bridge_fdb_entry *f;
555 548
556 hlist_for_each_entry_rcu(f, h, &br->hash[i], hlist) { 549 for (i = 0; i < BR_HASH_SIZE; i++) {
557 if (idx < cb->args[0]) 550 struct hlist_node *h;
558 goto skip; 551 struct net_bridge_fdb_entry *f;
559 552
560 if (fdb_fill_info(skb, br, f, 553 hlist_for_each_entry_rcu(f, h, &br->hash[i], hlist) {
561 NETLINK_CB(cb->skb).pid, 554 if (idx < cb->args[0])
562 cb->nlh->nlmsg_seq, 555 goto skip;
563 RTM_NEWNEIGH, 556
564 NLM_F_MULTI) < 0) 557 if (fdb_fill_info(skb, br, f,
565 break; 558 NETLINK_CB(cb->skb).pid,
559 cb->nlh->nlmsg_seq,
560 RTM_NEWNEIGH,
561 NLM_F_MULTI) < 0)
562 break;
566skip: 563skip:
567 ++idx; 564 ++idx;
568 }
569 } 565 }
570 } 566 }
571 rcu_read_unlock();
572
573 cb->args[0] = idx;
574 567
575 return skb->len; 568out:
569 return idx;
576} 570}
577 571
578/* Update (create or replace) forwarding database entry */ 572/* Update (create or replace) forwarding database entry */
@@ -614,43 +608,11 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr,
614} 608}
615 609
616/* Add new permanent fdb entry with RTM_NEWNEIGH */ 610/* Add new permanent fdb entry with RTM_NEWNEIGH */
617int br_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) 611int br_fdb_add(struct ndmsg *ndm, struct net_device *dev,
612 unsigned char *addr, u16 nlh_flags)
618{ 613{
619 struct net *net = sock_net(skb->sk);
620 struct ndmsg *ndm;
621 struct nlattr *tb[NDA_MAX+1];
622 struct net_device *dev;
623 struct net_bridge_port *p; 614 struct net_bridge_port *p;
624 const __u8 *addr; 615 int err = 0;
625 int err;
626
627 ASSERT_RTNL();
628 err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL);
629 if (err < 0)
630 return err;
631
632 ndm = nlmsg_data(nlh);
633 if (ndm->ndm_ifindex == 0) {
634 pr_info("bridge: RTM_NEWNEIGH with invalid ifindex\n");
635 return -EINVAL;
636 }
637
638 dev = __dev_get_by_index(net, ndm->ndm_ifindex);
639 if (dev == NULL) {
640 pr_info("bridge: RTM_NEWNEIGH with unknown ifindex\n");
641 return -ENODEV;
642 }
643
644 if (!tb[NDA_LLADDR] || nla_len(tb[NDA_LLADDR]) != ETH_ALEN) {
645 pr_info("bridge: RTM_NEWNEIGH with invalid address\n");
646 return -EINVAL;
647 }
648
649 addr = nla_data(tb[NDA_LLADDR]);
650 if (!is_valid_ether_addr(addr)) {
651 pr_info("bridge: RTM_NEWNEIGH with invalid ether address\n");
652 return -EINVAL;
653 }
654 616
655 if (!(ndm->ndm_state & (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE))) { 617 if (!(ndm->ndm_state & (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE))) {
656 pr_info("bridge: RTM_NEWNEIGH with invalid state %#x\n", ndm->ndm_state); 618 pr_info("bridge: RTM_NEWNEIGH with invalid state %#x\n", ndm->ndm_state);
@@ -670,14 +632,14 @@ int br_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
670 rcu_read_unlock(); 632 rcu_read_unlock();
671 } else { 633 } else {
672 spin_lock_bh(&p->br->hash_lock); 634 spin_lock_bh(&p->br->hash_lock);
673 err = fdb_add_entry(p, addr, ndm->ndm_state, nlh->nlmsg_flags); 635 err = fdb_add_entry(p, addr, ndm->ndm_state, nlh_flags);
674 spin_unlock_bh(&p->br->hash_lock); 636 spin_unlock_bh(&p->br->hash_lock);
675 } 637 }
676 638
677 return err; 639 return err;
678} 640}
679 641
680static int fdb_delete_by_addr(struct net_bridge_port *p, const u8 *addr) 642static int fdb_delete_by_addr(struct net_bridge_port *p, u8 *addr)
681{ 643{
682 struct net_bridge *br = p->br; 644 struct net_bridge *br = p->br;
683 struct hlist_head *head = &br->hash[br_mac_hash(addr)]; 645 struct hlist_head *head = &br->hash[br_mac_hash(addr)];
@@ -692,40 +654,12 @@ static int fdb_delete_by_addr(struct net_bridge_port *p, const u8 *addr)
692} 654}
693 655
694/* Remove neighbor entry with RTM_DELNEIGH */ 656/* Remove neighbor entry with RTM_DELNEIGH */
695int br_fdb_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) 657int br_fdb_delete(struct ndmsg *ndm, struct net_device *dev,
658 unsigned char *addr)
696{ 659{
697 struct net *net = sock_net(skb->sk);
698 struct ndmsg *ndm;
699 struct net_bridge_port *p; 660 struct net_bridge_port *p;
700 struct nlattr *llattr;
701 const __u8 *addr;
702 struct net_device *dev;
703 int err; 661 int err;
704 662
705 ASSERT_RTNL();
706 if (nlmsg_len(nlh) < sizeof(*ndm))
707 return -EINVAL;
708
709 ndm = nlmsg_data(nlh);
710 if (ndm->ndm_ifindex == 0) {
711 pr_info("bridge: RTM_DELNEIGH with invalid ifindex\n");
712 return -EINVAL;
713 }
714
715 dev = __dev_get_by_index(net, ndm->ndm_ifindex);
716 if (dev == NULL) {
717 pr_info("bridge: RTM_DELNEIGH with unknown ifindex\n");
718 return -ENODEV;
719 }
720
721 llattr = nlmsg_find_attr(nlh, sizeof(*ndm), NDA_LLADDR);
722 if (llattr == NULL || nla_len(llattr) != ETH_ALEN) {
723 pr_info("bridge: RTM_DELNEIGH with invalid address\n");
724 return -EINVAL;
725 }
726
727 addr = nla_data(llattr);
728
729 p = br_port_get_rtnl(dev); 663 p = br_port_get_rtnl(dev);
730 if (p == NULL) { 664 if (p == NULL) {
731 pr_info("bridge: RTM_DELNEIGH %s not a bridge port\n", 665 pr_info("bridge: RTM_DELNEIGH %s not a bridge port\n",