aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorScott Feldman <sfeldma@gmail.com>2014-11-28 08:34:21 -0500
committerDavid S. Miller <davem@davemloft.net>2014-12-02 23:01:22 -0500
commitcf6b8e1eedffd9ef9a22c0c9453d752b07daf89a (patch)
tree35f71111059d8377c8069680f7a569a170f2801c /net
parent38dcf357aed299186ecb090cc2f5290cc17d637d (diff)
bridge: add API to notify bridge driver of learned FBD on offloaded device
When the swdev device learns a new mac/vlan on a port, it sends some async notification to the driver and the driver installs an FDB in the device. To give a holistic system view, the learned mac/vlan should be reflected in the bridge's FBD table, so the user, using normal iproute2 cmds, can view what is currently learned by the device. This API on the bridge driver gives a way for the swdev driver to install an FBD entry in the bridge FBD table. (And remove one). This is equivalent to the device running these cmds: bridge fdb [add|del] <mac> dev <dev> vid <vlan id> master This patch needs some extra eyeballs for review, in paricular around the locking and contexts. Signed-off-by: Scott Feldman <sfeldma@gmail.com> Signed-off-by: Jiri Pirko <jiri@resnulli.us> Acked-by: Jamal Hadi Salim <jhs@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/bridge/br_fdb.c91
-rw-r--r--net/bridge/br_private.h3
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
988int 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
1030err_unlock:
1031 spin_unlock_bh(&br->hash_lock);
1032err_rtnl_unlock:
1033 rtnl_unlock();
1034
1035 return err;
1036}
1037EXPORT_SYMBOL(br_fdb_external_learn_add);
1038
1039int 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);
1069err_rtnl_unlock:
1070 rtnl_unlock();
1071
1072 return err;
1073}
1074EXPORT_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