diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/bridge/br_fdb.c | 91 | ||||
-rw-r--r-- | net/bridge/br_private.h | 3 |
2 files changed, 92 insertions, 2 deletions
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index b1be971eb06c..cc36e59db7d7 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c | |||
@@ -481,6 +481,7 @@ static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head, | |||
481 | fdb->is_local = 0; | 481 | fdb->is_local = 0; |
482 | fdb->is_static = 0; | 482 | fdb->is_static = 0; |
483 | fdb->added_by_user = 0; | 483 | fdb->added_by_user = 0; |
484 | fdb->added_by_external_learn = 0; | ||
484 | fdb->updated = fdb->used = jiffies; | 485 | fdb->updated = fdb->used = jiffies; |
485 | hlist_add_head_rcu(&fdb->hlist, head); | 486 | hlist_add_head_rcu(&fdb->hlist, head); |
486 | } | 487 | } |
@@ -613,7 +614,7 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br, | |||
613 | ndm->ndm_family = AF_BRIDGE; | 614 | ndm->ndm_family = AF_BRIDGE; |
614 | ndm->ndm_pad1 = 0; | 615 | ndm->ndm_pad1 = 0; |
615 | ndm->ndm_pad2 = 0; | 616 | ndm->ndm_pad2 = 0; |
616 | ndm->ndm_flags = 0; | 617 | ndm->ndm_flags = fdb->added_by_external_learn ? NTF_EXT_LEARNED : 0; |
617 | ndm->ndm_type = 0; | 618 | ndm->ndm_type = 0; |
618 | ndm->ndm_ifindex = fdb->dst ? fdb->dst->dev->ifindex : br->dev->ifindex; | 619 | ndm->ndm_ifindex = fdb->dst ? fdb->dst->dev->ifindex : br->dev->ifindex; |
619 | ndm->ndm_state = fdb_to_nud(fdb); | 620 | ndm->ndm_state = fdb_to_nud(fdb); |
@@ -983,3 +984,91 @@ void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p) | |||
983 | } | 984 | } |
984 | } | 985 | } |
985 | } | 986 | } |
987 | |||
988 | int br_fdb_external_learn_add(struct net_device *dev, | ||
989 | const unsigned char *addr, u16 vid) | ||
990 | { | ||
991 | struct net_bridge_port *p; | ||
992 | struct net_bridge *br; | ||
993 | struct hlist_head *head; | ||
994 | struct net_bridge_fdb_entry *fdb; | ||
995 | int err = 0; | ||
996 | |||
997 | rtnl_lock(); | ||
998 | |||
999 | p = br_port_get_rtnl(dev); | ||
1000 | if (!p) { | ||
1001 | pr_info("bridge: %s not a bridge port\n", dev->name); | ||
1002 | err = -EINVAL; | ||
1003 | goto err_rtnl_unlock; | ||
1004 | } | ||
1005 | |||
1006 | br = p->br; | ||
1007 | |||
1008 | spin_lock_bh(&br->hash_lock); | ||
1009 | |||
1010 | head = &br->hash[br_mac_hash(addr, vid)]; | ||
1011 | fdb = fdb_find(head, addr, vid); | ||
1012 | if (!fdb) { | ||
1013 | fdb = fdb_create(head, p, addr, vid); | ||
1014 | if (!fdb) { | ||
1015 | err = -ENOMEM; | ||
1016 | goto err_unlock; | ||
1017 | } | ||
1018 | fdb->added_by_external_learn = 1; | ||
1019 | fdb_notify(br, fdb, RTM_NEWNEIGH); | ||
1020 | } else if (fdb->added_by_external_learn) { | ||
1021 | /* Refresh entry */ | ||
1022 | fdb->updated = fdb->used = jiffies; | ||
1023 | } else if (!fdb->added_by_user) { | ||
1024 | /* Take over SW learned entry */ | ||
1025 | fdb->added_by_external_learn = 1; | ||
1026 | fdb->updated = jiffies; | ||
1027 | fdb_notify(br, fdb, RTM_NEWNEIGH); | ||
1028 | } | ||
1029 | |||
1030 | err_unlock: | ||
1031 | spin_unlock_bh(&br->hash_lock); | ||
1032 | err_rtnl_unlock: | ||
1033 | rtnl_unlock(); | ||
1034 | |||
1035 | return err; | ||
1036 | } | ||
1037 | EXPORT_SYMBOL(br_fdb_external_learn_add); | ||
1038 | |||
1039 | int br_fdb_external_learn_del(struct net_device *dev, | ||
1040 | const unsigned char *addr, u16 vid) | ||
1041 | { | ||
1042 | struct net_bridge_port *p; | ||
1043 | struct net_bridge *br; | ||
1044 | struct hlist_head *head; | ||
1045 | struct net_bridge_fdb_entry *fdb; | ||
1046 | int err = 0; | ||
1047 | |||
1048 | rtnl_lock(); | ||
1049 | |||
1050 | p = br_port_get_rtnl(dev); | ||
1051 | if (!p) { | ||
1052 | pr_info("bridge: %s not a bridge port\n", dev->name); | ||
1053 | err = -EINVAL; | ||
1054 | goto err_rtnl_unlock; | ||
1055 | } | ||
1056 | |||
1057 | br = p->br; | ||
1058 | |||
1059 | spin_lock_bh(&br->hash_lock); | ||
1060 | |||
1061 | head = &br->hash[br_mac_hash(addr, vid)]; | ||
1062 | fdb = fdb_find(head, addr, vid); | ||
1063 | if (fdb && fdb->added_by_external_learn) | ||
1064 | fdb_delete(br, fdb); | ||
1065 | else | ||
1066 | err = -ENOENT; | ||
1067 | |||
1068 | spin_unlock_bh(&br->hash_lock); | ||
1069 | err_rtnl_unlock: | ||
1070 | rtnl_unlock(); | ||
1071 | |||
1072 | return err; | ||
1073 | } | ||
1074 | EXPORT_SYMBOL(br_fdb_external_learn_del); | ||
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 1b529da8234d..cc36fb3efbdd 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h | |||
@@ -100,7 +100,8 @@ struct net_bridge_fdb_entry | |||
100 | mac_addr addr; | 100 | mac_addr addr; |
101 | unsigned char is_local:1, | 101 | unsigned char is_local:1, |
102 | is_static:1, | 102 | is_static:1, |
103 | added_by_user:1; | 103 | added_by_user:1, |
104 | added_by_external_learn:1; | ||
104 | __u16 vlan_id; | 105 | __u16 vlan_id; |
105 | }; | 106 | }; |
106 | 107 | ||