Skip to content
This repository has been archived by the owner on Dec 14, 2022. It is now read-only.

Commit

Permalink
bridge: Fix problems around fdb entries pointing to the bridge device
Browse files Browse the repository at this point in the history
Adding fdb entries pointing to the bridge device uses fdb_insert(),
which lacks various checks and does not respect added_by_user flag.

As a result, some inconsistent behavior can happen:
* Adding temporary entries succeeds but results in permanent entries.
* Same goes for "dynamic" and "use".
* Changing mac address of the bridge device causes deletion of
  user-added entries.
* Replacing existing entries looks successful from userspace but actually
  not, regardless of NLM_F_EXCL flag.

Use the same logic as other entries and fix them.

Fixes: 3741873 ("bridge: allow adding of fdb entries pointing to the bridge device")
Signed-off-by: Toshiaki Makita <[email protected]>
Acked-by: Roopa Prabhu <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
Toshiaki Makita authored and davem330 committed Aug 10, 2016
1 parent 836384d commit 7bb90c3
Showing 1 changed file with 27 additions and 25 deletions.
52 changes: 27 additions & 25 deletions net/bridge/br_fdb.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr)

/* If old entry was unassociated with any port, then delete it. */
f = __br_fdb_get(br, br->dev->dev_addr, 0);
if (f && f->is_local && !f->dst)
if (f && f->is_local && !f->dst && !f->added_by_user)
fdb_delete_local(br, NULL, f);

fdb_insert(br, NULL, newaddr, 0);
Expand All @@ -282,7 +282,7 @@ void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr)
if (!br_vlan_should_use(v))
continue;
f = __br_fdb_get(br, br->dev->dev_addr, v->vid);
if (f && f->is_local && !f->dst)
if (f && f->is_local && !f->dst && !f->added_by_user)
fdb_delete_local(br, NULL, f);
fdb_insert(br, NULL, newaddr, v->vid);
}
Expand Down Expand Up @@ -764,20 +764,25 @@ int br_fdb_dump(struct sk_buff *skb,
}

/* Update (create or replace) forwarding database entry */
static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr,
__u16 state, __u16 flags, __u16 vid)
static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source,
const __u8 *addr, __u16 state, __u16 flags, __u16 vid)
{
struct net_bridge *br = source->br;
struct hlist_head *head = &br->hash[br_mac_hash(addr, vid)];
struct net_bridge_fdb_entry *fdb;
bool modified = false;

/* If the port cannot learn allow only local and static entries */
if (!(state & NUD_PERMANENT) && !(state & NUD_NOARP) &&
if (source && !(state & NUD_PERMANENT) && !(state & NUD_NOARP) &&
!(source->state == BR_STATE_LEARNING ||
source->state == BR_STATE_FORWARDING))
return -EPERM;

if (!source && !(state & NUD_PERMANENT)) {
pr_info("bridge: RTM_NEWNEIGH %s without NUD_PERMANENT\n",
br->dev->name);
return -EINVAL;
}

fdb = fdb_find(head, addr, vid);
if (fdb == NULL) {
if (!(flags & NLM_F_CREATE))
Expand Down Expand Up @@ -832,22 +837,28 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr,
return 0;
}

static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge_port *p,
const unsigned char *addr, u16 nlh_flags, u16 vid)
static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge *br,
struct net_bridge_port *p, const unsigned char *addr,
u16 nlh_flags, u16 vid)
{
int err = 0;

if (ndm->ndm_flags & NTF_USE) {
if (!p) {
pr_info("bridge: RTM_NEWNEIGH %s with NTF_USE is not supported\n",
br->dev->name);
return -EINVAL;
}
local_bh_disable();
rcu_read_lock();
br_fdb_update(p->br, p, addr, vid, true);
br_fdb_update(br, p, addr, vid, true);
rcu_read_unlock();
local_bh_enable();
} else {
spin_lock_bh(&p->br->hash_lock);
err = fdb_add_entry(p, addr, ndm->ndm_state,
spin_lock_bh(&br->hash_lock);
err = fdb_add_entry(br, p, addr, ndm->ndm_state,
nlh_flags, vid);
spin_unlock_bh(&p->br->hash_lock);
spin_unlock_bh(&br->hash_lock);
}

return err;
Expand Down Expand Up @@ -884,6 +895,7 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
dev->name);
return -EINVAL;
}
br = p->br;
vg = nbp_vlan_group(p);
}

Expand All @@ -895,15 +907,9 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
}

/* VID was specified, so use it. */
if (dev->priv_flags & IFF_EBRIDGE)
err = br_fdb_insert(br, NULL, addr, vid);
else
err = __br_fdb_add(ndm, p, addr, nlh_flags, vid);
err = __br_fdb_add(ndm, br, p, addr, nlh_flags, vid);
} else {
if (dev->priv_flags & IFF_EBRIDGE)
err = br_fdb_insert(br, NULL, addr, 0);
else
err = __br_fdb_add(ndm, p, addr, nlh_flags, 0);
err = __br_fdb_add(ndm, br, p, addr, nlh_flags, 0);
if (err || !vg || !vg->num_vlans)
goto out;

Expand All @@ -914,11 +920,7 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
list_for_each_entry(v, &vg->vlan_list, vlist) {
if (!br_vlan_should_use(v))
continue;
if (dev->priv_flags & IFF_EBRIDGE)
err = br_fdb_insert(br, NULL, addr, v->vid);
else
err = __br_fdb_add(ndm, p, addr, nlh_flags,
v->vid);
err = __br_fdb_add(ndm, br, p, addr, nlh_flags, v->vid);
if (err)
goto out;
}
Expand Down

0 comments on commit 7bb90c3

Please sign in to comment.