diff options
Diffstat (limited to 'net/bridge/br_fdb.c')
-rw-r--r-- | net/bridge/br_fdb.c | 128 |
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 */ |
538 | int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb) | 538 | int 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; | ||
566 | skip: | 563 | skip: |
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; | 568 | out: |
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 */ |
617 | int br_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | 611 | int 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 | ||
680 | static int fdb_delete_by_addr(struct net_bridge_port *p, const u8 *addr) | 642 | static 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 */ |
695 | int br_fdb_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | 657 | int 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", |