|
@@ -40,7 +40,7 @@ static struct kmem_cache *br_fdb_cache __read_mostly;
|
|
static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
|
|
static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
|
|
const unsigned char *addr, u16 vid);
|
|
const unsigned char *addr, u16 vid);
|
|
static void fdb_notify(struct net_bridge *br,
|
|
static void fdb_notify(struct net_bridge *br,
|
|
- const struct net_bridge_fdb_entry *, int);
|
|
|
|
|
|
+ const struct net_bridge_fdb_entry *, int, bool);
|
|
|
|
|
|
int __init br_fdb_init(void)
|
|
int __init br_fdb_init(void)
|
|
{
|
|
{
|
|
@@ -195,7 +195,8 @@ static void fdb_del_hw_addr(struct net_bridge *br, const unsigned char *addr)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f)
|
|
|
|
|
|
+static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f,
|
|
|
|
+ bool swdev_notify)
|
|
{
|
|
{
|
|
trace_fdb_delete(br, f);
|
|
trace_fdb_delete(br, f);
|
|
|
|
|
|
@@ -205,7 +206,7 @@ static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f)
|
|
hlist_del_init_rcu(&f->fdb_node);
|
|
hlist_del_init_rcu(&f->fdb_node);
|
|
rhashtable_remove_fast(&br->fdb_hash_tbl, &f->rhnode,
|
|
rhashtable_remove_fast(&br->fdb_hash_tbl, &f->rhnode,
|
|
br_fdb_rht_params);
|
|
br_fdb_rht_params);
|
|
- fdb_notify(br, f, RTM_DELNEIGH);
|
|
|
|
|
|
+ fdb_notify(br, f, RTM_DELNEIGH, swdev_notify);
|
|
call_rcu(&f->rcu, fdb_rcu_free);
|
|
call_rcu(&f->rcu, fdb_rcu_free);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -241,7 +242,7 @@ static void fdb_delete_local(struct net_bridge *br,
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
- fdb_delete(br, f);
|
|
|
|
|
|
+ fdb_delete(br, f, true);
|
|
}
|
|
}
|
|
|
|
|
|
void br_fdb_find_delete_local(struct net_bridge *br,
|
|
void br_fdb_find_delete_local(struct net_bridge *br,
|
|
@@ -356,7 +357,7 @@ void br_fdb_cleanup(struct work_struct *work)
|
|
} else {
|
|
} else {
|
|
spin_lock_bh(&br->hash_lock);
|
|
spin_lock_bh(&br->hash_lock);
|
|
if (!hlist_unhashed(&f->fdb_node))
|
|
if (!hlist_unhashed(&f->fdb_node))
|
|
- fdb_delete(br, f);
|
|
|
|
|
|
+ fdb_delete(br, f, true);
|
|
spin_unlock_bh(&br->hash_lock);
|
|
spin_unlock_bh(&br->hash_lock);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -376,7 +377,7 @@ void br_fdb_flush(struct net_bridge *br)
|
|
spin_lock_bh(&br->hash_lock);
|
|
spin_lock_bh(&br->hash_lock);
|
|
hlist_for_each_entry_safe(f, tmp, &br->fdb_list, fdb_node) {
|
|
hlist_for_each_entry_safe(f, tmp, &br->fdb_list, fdb_node) {
|
|
if (!f->is_static)
|
|
if (!f->is_static)
|
|
- fdb_delete(br, f);
|
|
|
|
|
|
+ fdb_delete(br, f, true);
|
|
}
|
|
}
|
|
spin_unlock_bh(&br->hash_lock);
|
|
spin_unlock_bh(&br->hash_lock);
|
|
}
|
|
}
|
|
@@ -405,7 +406,7 @@ void br_fdb_delete_by_port(struct net_bridge *br,
|
|
if (f->is_local)
|
|
if (f->is_local)
|
|
fdb_delete_local(br, p, f);
|
|
fdb_delete_local(br, p, f);
|
|
else
|
|
else
|
|
- fdb_delete(br, f);
|
|
|
|
|
|
+ fdb_delete(br, f, true);
|
|
}
|
|
}
|
|
spin_unlock_bh(&br->hash_lock);
|
|
spin_unlock_bh(&br->hash_lock);
|
|
}
|
|
}
|
|
@@ -531,7 +532,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
|
|
return 0;
|
|
return 0;
|
|
br_warn(br, "adding interface %s with same address as a received packet (addr:%pM, vlan:%u)\n",
|
|
br_warn(br, "adding interface %s with same address as a received packet (addr:%pM, vlan:%u)\n",
|
|
source ? source->dev->name : br->dev->name, addr, vid);
|
|
source ? source->dev->name : br->dev->name, addr, vid);
|
|
- fdb_delete(br, fdb);
|
|
|
|
|
|
+ fdb_delete(br, fdb, true);
|
|
}
|
|
}
|
|
|
|
|
|
fdb = fdb_create(br, source, addr, vid, 1, 1);
|
|
fdb = fdb_create(br, source, addr, vid, 1, 1);
|
|
@@ -539,7 +540,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
|
|
|
|
fdb_add_hw_addr(br, addr);
|
|
fdb_add_hw_addr(br, addr);
|
|
- fdb_notify(br, fdb, RTM_NEWNEIGH);
|
|
|
|
|
|
+ fdb_notify(br, fdb, RTM_NEWNEIGH, true);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -594,7 +595,7 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
|
|
fdb->added_by_user = 1;
|
|
fdb->added_by_user = 1;
|
|
if (unlikely(fdb_modified)) {
|
|
if (unlikely(fdb_modified)) {
|
|
trace_br_fdb_update(br, source, addr, vid, added_by_user);
|
|
trace_br_fdb_update(br, source, addr, vid, added_by_user);
|
|
- fdb_notify(br, fdb, RTM_NEWNEIGH);
|
|
|
|
|
|
+ fdb_notify(br, fdb, RTM_NEWNEIGH, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
} else {
|
|
@@ -605,7 +606,7 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
|
|
fdb->added_by_user = 1;
|
|
fdb->added_by_user = 1;
|
|
trace_br_fdb_update(br, source, addr, vid,
|
|
trace_br_fdb_update(br, source, addr, vid,
|
|
added_by_user);
|
|
added_by_user);
|
|
- fdb_notify(br, fdb, RTM_NEWNEIGH);
|
|
|
|
|
|
+ fdb_notify(br, fdb, RTM_NEWNEIGH, true);
|
|
}
|
|
}
|
|
/* else we lose race and someone else inserts
|
|
/* else we lose race and someone else inserts
|
|
* it first, don't bother updating
|
|
* it first, don't bother updating
|
|
@@ -687,13 +688,15 @@ static inline size_t fdb_nlmsg_size(void)
|
|
}
|
|
}
|
|
|
|
|
|
static void fdb_notify(struct net_bridge *br,
|
|
static void fdb_notify(struct net_bridge *br,
|
|
- const struct net_bridge_fdb_entry *fdb, int type)
|
|
|
|
|
|
+ const struct net_bridge_fdb_entry *fdb, int type,
|
|
|
|
+ bool swdev_notify)
|
|
{
|
|
{
|
|
struct net *net = dev_net(br->dev);
|
|
struct net *net = dev_net(br->dev);
|
|
struct sk_buff *skb;
|
|
struct sk_buff *skb;
|
|
int err = -ENOBUFS;
|
|
int err = -ENOBUFS;
|
|
|
|
|
|
- br_switchdev_fdb_notify(fdb, type);
|
|
|
|
|
|
+ if (swdev_notify)
|
|
|
|
+ br_switchdev_fdb_notify(fdb, type);
|
|
|
|
|
|
skb = nlmsg_new(fdb_nlmsg_size(), GFP_ATOMIC);
|
|
skb = nlmsg_new(fdb_nlmsg_size(), GFP_ATOMIC);
|
|
if (skb == NULL)
|
|
if (skb == NULL)
|
|
@@ -832,7 +835,7 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source,
|
|
fdb->used = jiffies;
|
|
fdb->used = jiffies;
|
|
if (modified) {
|
|
if (modified) {
|
|
fdb->updated = jiffies;
|
|
fdb->updated = jiffies;
|
|
- fdb_notify(br, fdb, RTM_NEWNEIGH);
|
|
|
|
|
|
+ fdb_notify(br, fdb, RTM_NEWNEIGH, true);
|
|
}
|
|
}
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
@@ -856,7 +859,7 @@ static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge *br,
|
|
rcu_read_unlock();
|
|
rcu_read_unlock();
|
|
local_bh_enable();
|
|
local_bh_enable();
|
|
} else if (ndm->ndm_flags & NTF_EXT_LEARNED) {
|
|
} else if (ndm->ndm_flags & NTF_EXT_LEARNED) {
|
|
- err = br_fdb_external_learn_add(br, p, addr, vid);
|
|
|
|
|
|
+ err = br_fdb_external_learn_add(br, p, addr, vid, true);
|
|
} else {
|
|
} else {
|
|
spin_lock_bh(&br->hash_lock);
|
|
spin_lock_bh(&br->hash_lock);
|
|
err = fdb_add_entry(br, p, addr, ndm->ndm_state,
|
|
err = fdb_add_entry(br, p, addr, ndm->ndm_state,
|
|
@@ -945,7 +948,7 @@ static int fdb_delete_by_addr_and_port(struct net_bridge *br,
|
|
if (!fdb || fdb->dst != p)
|
|
if (!fdb || fdb->dst != p)
|
|
return -ENOENT;
|
|
return -ENOENT;
|
|
|
|
|
|
- fdb_delete(br, fdb);
|
|
|
|
|
|
+ fdb_delete(br, fdb, true);
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -1065,7 +1068,8 @@ void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p)
|
|
}
|
|
}
|
|
|
|
|
|
int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
|
|
int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
|
|
- const unsigned char *addr, u16 vid)
|
|
|
|
|
|
+ const unsigned char *addr, u16 vid,
|
|
|
|
+ bool swdev_notify)
|
|
{
|
|
{
|
|
struct net_bridge_fdb_entry *fdb;
|
|
struct net_bridge_fdb_entry *fdb;
|
|
bool modified = false;
|
|
bool modified = false;
|
|
@@ -1083,7 +1087,7 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
|
|
goto err_unlock;
|
|
goto err_unlock;
|
|
}
|
|
}
|
|
fdb->added_by_external_learn = 1;
|
|
fdb->added_by_external_learn = 1;
|
|
- fdb_notify(br, fdb, RTM_NEWNEIGH);
|
|
|
|
|
|
+ fdb_notify(br, fdb, RTM_NEWNEIGH, swdev_notify);
|
|
} else {
|
|
} else {
|
|
fdb->updated = jiffies;
|
|
fdb->updated = jiffies;
|
|
|
|
|
|
@@ -1102,7 +1106,7 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
|
|
}
|
|
}
|
|
|
|
|
|
if (modified)
|
|
if (modified)
|
|
- fdb_notify(br, fdb, RTM_NEWNEIGH);
|
|
|
|
|
|
+ fdb_notify(br, fdb, RTM_NEWNEIGH, swdev_notify);
|
|
}
|
|
}
|
|
|
|
|
|
err_unlock:
|
|
err_unlock:
|
|
@@ -1112,7 +1116,8 @@ err_unlock:
|
|
}
|
|
}
|
|
|
|
|
|
int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p,
|
|
int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p,
|
|
- const unsigned char *addr, u16 vid)
|
|
|
|
|
|
+ const unsigned char *addr, u16 vid,
|
|
|
|
+ bool swdev_notify)
|
|
{
|
|
{
|
|
struct net_bridge_fdb_entry *fdb;
|
|
struct net_bridge_fdb_entry *fdb;
|
|
int err = 0;
|
|
int err = 0;
|
|
@@ -1121,7 +1126,7 @@ int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p,
|
|
|
|
|
|
fdb = br_fdb_find(br, addr, vid);
|
|
fdb = br_fdb_find(br, addr, vid);
|
|
if (fdb && fdb->added_by_external_learn)
|
|
if (fdb && fdb->added_by_external_learn)
|
|
- fdb_delete(br, fdb);
|
|
|
|
|
|
+ fdb_delete(br, fdb, swdev_notify);
|
|
else
|
|
else
|
|
err = -ENOENT;
|
|
err = -ENOENT;
|
|
|
|
|