/* When a static FDB entry is added, the mac address from the entry is * added to the bridge private HW address list and all required ports * are then updated with the new information. * Called under RTNL.
*/ staticvoid fdb_add_hw_addr(struct net_bridge *br, constunsignedchar *addr)
{ int err; struct net_bridge_port *p;
ASSERT_RTNL();
list_for_each_entry(p, &br->port_list, list) { if (!br_promisc_port(p)) {
err = dev_uc_add(p->dev, addr); if (err) goto undo;
}
}
/* When a static FDB entry is deleted, the HW address from that entry is * also removed from the bridge private HW address list and updates all * the ports with needed information. * Called under RTNL.
*/ staticvoid fdb_del_hw_addr(struct net_bridge *br, constunsignedchar *addr)
{ struct net_bridge_port *p;
ASSERT_RTNL();
list_for_each_entry(p, &br->port_list, list) { if (!br_promisc_port(p))
dev_uc_del(p->dev, addr);
}
}
/* Delete a local entry if no other port had the same address. * * This function should only be called on entries with BR_FDB_LOCAL set, * so even with BR_FDB_ADDED_BY_USER cleared we never need to increase * the accounting for dynamically learned entries again.
*/ staticvoid fdb_delete_local(struct net_bridge *br, conststruct net_bridge_port *p, struct net_bridge_fdb_entry *f)
{ constunsignedchar *addr = f->key.addr.addr; struct net_bridge_vlan_group *vg; conststruct net_bridge_vlan *v; struct net_bridge_port *op;
u16 vid = f->key.vlan_id;
/* Maybe another port has same hw addr? */
list_for_each_entry(op, &br->port_list, list) {
vg = nbp_vlan_group(op); if (op != p && ether_addr_equal(op->dev->dev_addr, addr) &&
(!vid || br_vlan_find(vg, vid))) {
f->dst = op;
clear_bit(BR_FDB_ADDED_BY_USER, &f->flags); return;
}
}
vg = br_vlan_group(br);
v = br_vlan_find(vg, vid); /* Maybe bridge device has same hw addr? */ if (p && ether_addr_equal(br->dev->dev_addr, addr) &&
(!vid || (v && br_vlan_should_use(v)))) {
f->dst = NULL;
clear_bit(BR_FDB_ADDED_BY_USER, &f->flags); return;
}
fdb = br_fdb_find(br, addr, vid); if (fdb) { /* it is okay to have multiple ports with same * address, just use the first one.
*/ if (test_bit(BR_FDB_LOCAL, &fdb->flags)) return0;
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);
fdb_delete(br, fdb, true);
}
spin_lock_bh(&br->hash_lock);
vg = nbp_vlan_group(p);
hlist_for_each_entry(f, &br->fdb_list, fdb_node) { if (f->dst == p && test_bit(BR_FDB_LOCAL, &f->flags) &&
!test_bit(BR_FDB_ADDED_BY_USER, &f->flags)) { /* delete old one */
fdb_delete_local(br, p, f);
/* if this port has no vlan information * configured, we can safely be done at * this point.
*/ if (!vg || !vg->num_vlans) goto insert;
}
}
insert: /* insert new address, may fail if invalid address or dup. */
fdb_add_local(br, p, newaddr, 0);
if (!vg || !vg->num_vlans) goto done;
/* Now add entries for every VLAN configured on the port. * This function runs under RTNL so the bitmap will not change * from under us.
*/
list_for_each_entry(v, &vg->vlan_list, vlist)
fdb_add_local(br, p, newaddr, v->vid);
/* If old entry was unassociated with any port, then delete it. */
f = br_fdb_find(br, br->dev->dev_addr, 0); if (f && test_bit(BR_FDB_LOCAL, &f->flags) &&
!f->dst && !test_bit(BR_FDB_ADDED_BY_USER, &f->flags))
fdb_delete_local(br, NULL, f);
fdb_add_local(br, NULL, newaddr, 0);
vg = br_vlan_group(br); if (!vg || !vg->num_vlans) goto out; /* Now remove and add entries for every VLAN configured on the * bridge. This function runs under RTNL so the bitmap will not * change from under us.
*/
list_for_each_entry(v, &vg->vlan_list, vlist) { if (!br_vlan_should_use(v)) continue;
f = br_fdb_find(br, br->dev->dev_addr, v->vid); if (f && test_bit(BR_FDB_LOCAL, &f->flags) &&
!f->dst && !test_bit(BR_FDB_ADDED_BY_USER, &f->flags))
fdb_delete_local(br, NULL, f);
fdb_add_local(br, NULL, newaddr, v->vid);
}
out:
spin_unlock_bh(&br->hash_lock);
}
/* this part is tricky, in order to avoid blocking learning and * consequently forwarding, we rely on rcu to delete objects with * delayed freeing allowing us to continue traversing
*/
rcu_read_lock();
hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) { unsignedlong this_timer = f->updated + delay;
if (test_bit(BR_FDB_STATIC, &f->flags) ||
test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &f->flags)) { if (test_bit(BR_FDB_NOTIFY, &f->flags)) { if (time_after(this_timer, now))
work_delay = min(work_delay,
this_timer - now); elseif (!test_and_set_bit(BR_FDB_NOTIFY_INACTIVE,
&f->flags))
fdb_notify(br, f, RTM_NEWNEIGH, false);
} continue;
}
dev = __dev_get_by_index(dev_net(br->dev), ifindex); if (!dev) {
NL_SET_ERR_MSG_MOD(extack, "Unknown flush device ifindex"); return -ENODEV;
} if (!netif_is_bridge_master(dev) && !netif_is_bridge_port(dev)) {
NL_SET_ERR_MSG_MOD(extack, "Flush device is not a bridge or bridge port"); return -EINVAL;
} if (netif_is_bridge_master(dev) && dev != br->dev) {
NL_SET_ERR_MSG_MOD(extack, "Flush bridge device does not match target bridge device"); return -EINVAL;
} if (netif_is_bridge_port(dev)) { struct net_bridge_port *p = br_port_get_rtnl(dev);
if (p->br != br) {
NL_SET_ERR_MSG_MOD(extack, "Port belongs to a different bridge device"); return -EINVAL;
}
}
if (netif_is_bridge_master(dev)) {
br = netdev_priv(dev);
} else {
p = br_port_get_rtnl(dev); if (!p) {
NL_SET_ERR_MSG_MOD(extack, "Device is not a bridge port"); return -EINVAL;
}
br = p->br;
}
if (tb[NDA_VLAN])
desc.vlan_id = nla_get_u16(tb[NDA_VLAN]);
if (ndm_flags & ~FDB_FLUSH_ALLOWED_NDM_FLAGS) {
NL_SET_ERR_MSG(extack, "Unsupported fdb flush ndm flag bits set"); return -EINVAL;
} if (ndm->ndm_state & ~FDB_FLUSH_ALLOWED_NDM_STATES) {
NL_SET_ERR_MSG(extack, "Unsupported fdb flush ndm state bits set"); return -EINVAL;
}
/* Flush all entries referring to a specific port. * if do_all is set also flush static entries * if vid is set delete all entries that match the vlan_id
*/ void br_fdb_delete_by_port(struct net_bridge *br, conststruct net_bridge_port *p,
u16 vid, int do_all)
{ struct net_bridge_fdb_entry *f; struct hlist_node *tmp;
#if IS_ENABLED(CONFIG_ATM_LANE) /* Interface used by ATM LANE hook to test
* if an addr is on some other bridge port */ int br_fdb_test_addr(struct net_device *dev, unsignedchar *addr)
{ struct net_bridge_fdb_entry *fdb; struct net_bridge_port *port; int ret;
rcu_read_lock();
port = br_port_get_rcu(dev); if (!port)
ret = 0; else { conststruct net_bridge_port *dst = NULL;
fdb = br_fdb_find_rcu(port->br, addr, 0); if (fdb)
dst = READ_ONCE(fdb->dst);
ret = dst && dst->dev != dev &&
dst->state == BR_STATE_FORWARDING;
}
rcu_read_unlock();
return ret;
} #endif/* CONFIG_ATM_LANE */
/* * Fill buffer with forwarding table records in * the API format.
*/ int br_fdb_fillbuf(struct net_bridge *br, void *buf, unsignedlong maxnum, unsignedlong skip)
{ struct net_bridge_fdb_entry *f; struct __fdb_entry *fe = buf; int num = 0;
/* Add entry for local address of interface */ int br_fdb_add_local(struct net_bridge *br, struct net_bridge_port *source, constunsignedchar *addr, u16 vid)
{ int ret;
/* some users want to always flood. */ if (hold_time(br) == 0) return;
fdb = fdb_find_rcu(&br->fdb_hash_tbl, addr, vid); if (likely(fdb)) { /* attempt to update an entry for a local interface */ if (unlikely(test_bit(BR_FDB_LOCAL, &fdb->flags))) { if (net_ratelimit())
br_warn(br, "received packet on %s with own address as source address (addr:%pM, vlan:%u)\n",
source->dev->name, addr, vid);
} else { unsignedlong now = jiffies; bool fdb_modified = false;
/* fastpath: update of existing entry */ if (unlikely(source != READ_ONCE(fdb->dst) &&
!test_bit(BR_FDB_STICKY, &fdb->flags))) {
br_switchdev_fdb_notify(br, fdb, RTM_DELNEIGH);
WRITE_ONCE(fdb->dst, source);
fdb_modified = true; /* Take over HW learned entry */ if (unlikely(test_bit(BR_FDB_ADDED_BY_EXT_LEARN,
&fdb->flags)))
clear_bit(BR_FDB_ADDED_BY_EXT_LEARN,
&fdb->flags); /* Clear locked flag when roaming to an * unlocked port.
*/ if (unlikely(test_bit(BR_FDB_LOCKED, &fdb->flags)))
clear_bit(BR_FDB_LOCKED, &fdb->flags);
}
if (unlikely(test_bit(BR_FDB_ADDED_BY_USER, &flags))) {
set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags); if (test_and_clear_bit(BR_FDB_DYNAMIC_LEARNED,
&fdb->flags))
atomic_dec(&br->fdb_n_learned);
} if (unlikely(fdb_modified)) {
trace_br_fdb_update(br, source, addr, vid, flags);
fdb_notify(br, fdb, RTM_NEWNEIGH, true);
}
}
} else {
spin_lock(&br->hash_lock);
fdb = fdb_create(br, source, addr, vid, flags); if (fdb) {
trace_br_fdb_update(br, source, addr, vid, flags);
fdb_notify(br, fdb, RTM_NEWNEIGH, true);
} /* else we lose race and someone else inserts * it first, don't bother updating
*/
spin_unlock(&br->hash_lock);
}
}
/* Dump information about entries, in response to GETNEIGH */ int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, struct net_device *dev, struct net_device *filter_dev, int *idx)
{ struct ndo_fdb_dump_context *ctx = (void *)cb->ctx; struct net_bridge *br = netdev_priv(dev); struct net_bridge_fdb_entry *f; int err = 0;
if (!netif_is_bridge_master(dev)) return err;
if (!filter_dev) {
err = ndo_dflt_fdb_dump(skb, cb, dev, NULL, idx); if (err < 0) return err;
}
rcu_read_lock();
hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) { if (*idx < ctx->fdb_idx) goto skip; if (filter_dev && (!f->dst || f->dst->dev != filter_dev)) { if (filter_dev != dev) goto skip; /* !f->dst is a special case for bridge * It means the MAC belongs to the bridge * Therefore need a little more filtering * we only want to dump the !f->dst case
*/ if (f->dst) goto skip;
} if (!filter_dev && f->dst) goto skip;
/* returns true if the fdb is modified */ staticbool fdb_handle_notify(struct net_bridge_fdb_entry *fdb, u8 notify)
{ bool modified = false;
/* allow to mark an entry as inactive, usually done on creation */ if ((notify & FDB_NOTIFY_INACTIVE_BIT) &&
!test_and_set_bit(BR_FDB_NOTIFY_INACTIVE, &fdb->flags))
modified = true;
/* If the port cannot learn allow only local and static entries */ 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;
}
if (is_sticky && (state & NUD_PERMANENT)) return -EINVAL;
if (nfea_tb[NFEA_ACTIVITY_NOTIFY]) {
notify = nla_get_u8(nfea_tb[NFEA_ACTIVITY_NOTIFY]); if ((notify & ~BR_FDB_NOTIFY_SETTABLE_BITS) ||
(notify & BR_FDB_NOTIFY_SETTABLE_BITS) == FDB_NOTIFY_INACTIVE_BIT) return -EINVAL;
}
fdb = br_fdb_find(br, addr, vid); if (fdb == NULL) { if (!(flags & NLM_F_CREATE)) return -ENOENT;
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;
} if (!nbp_state_should_learn(p)) return0;
if (!(ndm->ndm_state & (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE))) {
pr_info("bridge: RTM_NEWNEIGH with invalid state %#x\n", ndm->ndm_state); return -EINVAL;
}
if (is_zero_ether_addr(addr)) {
pr_info("bridge: RTM_NEWNEIGH with invalid ether address\n"); return -EINVAL;
}
if (netif_is_bridge_master(dev)) {
br = netdev_priv(dev);
vg = br_vlan_group(br);
} else {
p = br_port_get_rtnl(dev); if (!p) {
pr_info("bridge: RTM_NEWNEIGH %s not a bridge port\n",
dev->name); return -EINVAL;
}
br = p->br;
vg = nbp_vlan_group(p);
}
if (tb[NDA_FLAGS_EXT])
ext_flags = nla_get_u32(tb[NDA_FLAGS_EXT]);
if (ext_flags & NTF_EXT_LOCKED) {
NL_SET_ERR_MSG_MOD(extack, "Cannot add FDB entry with \"locked\" flag set"); return -EINVAL;
}
if (vid) {
v = br_vlan_find(vg, vid); if (!v || !br_vlan_should_use(v)) {
pr_info("bridge: RTM_NEWNEIGH with unconfigured vlan %d on %s\n", vid, dev->name); return -EINVAL;
}
/* VID was specified, so use it. */
err = __br_fdb_add(ndm, br, p, addr, nlh_flags, vid, nfea_tb,
notified, extack);
} else {
err = __br_fdb_add(ndm, br, p, addr, nlh_flags, 0, nfea_tb,
notified, extack); if (err || !vg || !vg->num_vlans) goto out;
/* We have vlans configured on this port and user didn't * specify a VLAN. To be nice, add/update entry for every * vlan on this port.
*/
list_for_each_entry(v, &vg->vlan_list, vlist) { if (!br_vlan_should_use(v)) continue;
err = __br_fdb_add(ndm, br, p, addr, nlh_flags, v->vid,
nfea_tb, notified, extack); if (err) goto out;
}
}
int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p)
{ struct net_bridge_fdb_entry *f, *tmp; int err = 0;
ASSERT_RTNL();
/* the key here is that static entries change only under rtnl */
rcu_read_lock();
hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) { /* We only care for static entries */ if (!test_bit(BR_FDB_STATIC, &f->flags)) continue;
err = dev_uc_add(p->dev, f->key.addr.addr); if (err) goto rollback;
}
done:
rcu_read_unlock();
return err;
rollback:
hlist_for_each_entry_rcu(tmp, &br->fdb_list, fdb_node) { /* We only care for static entries */ if (!test_bit(BR_FDB_STATIC, &tmp->flags)) continue; if (tmp == f) break;
dev_uc_del(p->dev, tmp->key.addr.addr);
}
rcu_read_lock();
hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) { /* We only care for static entries */ if (!test_bit(BR_FDB_STATIC, &f->flags)) continue;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Angebot
Hier finden Sie eine Liste der Produkte des Unternehmens