bridge: add API to notify bridge driver of learned FBD on offloaded device
authorScott Feldman <sfeldma@gmail.com>
Fri, 28 Nov 2014 13:34:21 +0000 (14:34 +0100)
committerDavid S. Miller <davem@davemloft.net>
Wed, 3 Dec 2014 04:01:22 +0000 (20:01 -0800)
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>
include/linux/if_bridge.h
include/uapi/linux/neighbour.h
net/bridge/br_fdb.c
net/bridge/br_private.h

index 808dcb8cc04fbeee82f00bab944ff45e8087078a..fa2eca6251298850e75cb06c34b85686cf8c4c91 100644 (file)
@@ -37,6 +37,24 @@ extern void brioctl_set(int (*ioctl_hook)(struct net *, unsigned int, void __use
 typedef int br_should_route_hook_t(struct sk_buff *skb);
 extern br_should_route_hook_t __rcu *br_should_route_hook;
 
+#if IS_ENABLED(CONFIG_BRIDGE)
+int br_fdb_external_learn_add(struct net_device *dev,
+                             const unsigned char *addr, u16 vid);
+int br_fdb_external_learn_del(struct net_device *dev,
+                             const unsigned char *addr, u16 vid);
+#else
+static inline int br_fdb_external_learn_add(struct net_device *dev,
+                                           const unsigned char *addr, u16 vid)
+{
+       return 0;
+}
+static inline int br_fdb_external_learn_del(struct net_device *dev,
+                                           const unsigned char *addr, u16 vid)
+{
+       return 0;
+}
+#endif
+
 #if IS_ENABLED(CONFIG_BRIDGE) && IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING)
 int br_multicast_list_adjacent(struct net_device *dev,
                               struct list_head *br_ip_list);
index 2f043af7fcd69dbe8b3a3941d618638c24bef26e..f3d77f9f1e0bb582b1f1ca02fd8bd99ff60729dd 100644 (file)
@@ -38,6 +38,7 @@ enum {
 #define NTF_SELF       0x02
 #define NTF_MASTER     0x04
 #define NTF_PROXY      0x08    /* == ATF_PUBL */
+#define NTF_EXT_LEARNED        0x10
 #define NTF_ROUTER     0x80
 
 /*
index b1be971eb06ca49e31f07d187fb8b70645cf8068..cc36e59db7d75203d89017e8d458b72370df33da 100644 (file)
@@ -481,6 +481,7 @@ static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head,
                fdb->is_local = 0;
                fdb->is_static = 0;
                fdb->added_by_user = 0;
+               fdb->added_by_external_learn = 0;
                fdb->updated = fdb->used = jiffies;
                hlist_add_head_rcu(&fdb->hlist, head);
        }
@@ -613,7 +614,7 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br,
        ndm->ndm_family  = AF_BRIDGE;
        ndm->ndm_pad1    = 0;
        ndm->ndm_pad2    = 0;
-       ndm->ndm_flags   = 0;
+       ndm->ndm_flags   = fdb->added_by_external_learn ? NTF_EXT_LEARNED : 0;
        ndm->ndm_type    = 0;
        ndm->ndm_ifindex = fdb->dst ? fdb->dst->dev->ifindex : br->dev->ifindex;
        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)
                }
        }
 }
+
+int br_fdb_external_learn_add(struct net_device *dev,
+                             const unsigned char *addr, u16 vid)
+{
+       struct net_bridge_port *p;
+       struct net_bridge *br;
+       struct hlist_head *head;
+       struct net_bridge_fdb_entry *fdb;
+       int err = 0;
+
+       rtnl_lock();
+
+       p = br_port_get_rtnl(dev);
+       if (!p) {
+               pr_info("bridge: %s not a bridge port\n", dev->name);
+               err = -EINVAL;
+               goto err_rtnl_unlock;
+       }
+
+       br = p->br;
+
+       spin_lock_bh(&br->hash_lock);
+
+       head = &br->hash[br_mac_hash(addr, vid)];
+       fdb = fdb_find(head, addr, vid);
+       if (!fdb) {
+               fdb = fdb_create(head, p, addr, vid);
+               if (!fdb) {
+                       err = -ENOMEM;
+                       goto err_unlock;
+               }
+               fdb->added_by_external_learn = 1;
+               fdb_notify(br, fdb, RTM_NEWNEIGH);
+       } else if (fdb->added_by_external_learn) {
+               /* Refresh entry */
+               fdb->updated = fdb->used = jiffies;
+       } else if (!fdb->added_by_user) {
+               /* Take over SW learned entry */
+               fdb->added_by_external_learn = 1;
+               fdb->updated = jiffies;
+               fdb_notify(br, fdb, RTM_NEWNEIGH);
+       }
+
+err_unlock:
+       spin_unlock_bh(&br->hash_lock);
+err_rtnl_unlock:
+       rtnl_unlock();
+
+       return err;
+}
+EXPORT_SYMBOL(br_fdb_external_learn_add);
+
+int br_fdb_external_learn_del(struct net_device *dev,
+                             const unsigned char *addr, u16 vid)
+{
+       struct net_bridge_port *p;
+       struct net_bridge *br;
+       struct hlist_head *head;
+       struct net_bridge_fdb_entry *fdb;
+       int err = 0;
+
+       rtnl_lock();
+
+       p = br_port_get_rtnl(dev);
+       if (!p) {
+               pr_info("bridge: %s not a bridge port\n", dev->name);
+               err = -EINVAL;
+               goto err_rtnl_unlock;
+       }
+
+       br = p->br;
+
+       spin_lock_bh(&br->hash_lock);
+
+       head = &br->hash[br_mac_hash(addr, vid)];
+       fdb = fdb_find(head, addr, vid);
+       if (fdb && fdb->added_by_external_learn)
+               fdb_delete(br, fdb);
+       else
+               err = -ENOENT;
+
+       spin_unlock_bh(&br->hash_lock);
+err_rtnl_unlock:
+       rtnl_unlock();
+
+       return err;
+}
+EXPORT_SYMBOL(br_fdb_external_learn_del);
index 1b529da8234d6352deaf6967ffe737200d773530..cc36fb3efbdd46578f9c07931920ed3048fc6dd6 100644 (file)
@@ -100,7 +100,8 @@ struct net_bridge_fdb_entry
        mac_addr                        addr;
        unsigned char                   is_local:1,
                                        is_static:1,
-                                       added_by_user:1;
+                                       added_by_user:1,
+                                       added_by_external_learn:1;
        __u16                           vlan_id;
 };