--- zzzz-none-000/linux-3.10.107/net/bridge/br_fdb.c 2017-06-27 09:49:32.000000000 +0000 +++ vr9-7490-729/linux-3.10.107/net/bridge/br_fdb.c 2021-11-10 11:53:56.000000000 +0000 @@ -11,6 +11,13 @@ * 2 of the License, or (at your option) any later version. */ +/** + * Some part of this file is modified by Ikanos Communications. + * + * Copyright (C) 2013-2014 Ikanos Communications. + */ + +#include #include #include #include @@ -26,7 +33,16 @@ #include #include "br_private.h" +#if IS_ENABLED(CONFIG_FUSIV_KERNEL_AP_2_AP) +void (*ap2apBridgeFlowDelete_ptr)(struct net_bridge_fdb_entry *src)= NULL; +int (*Isap2apBridgeTraffic_ptr)(struct net_bridge_fdb_entry *fdb) = NULL; +short (*apAddBridgePortNewHWaddr_ptr)( struct net_bridge_port* p) = NULL; +#endif + static struct kmem_cache *br_fdb_cache __read_mostly; +static struct net_bridge_fdb_entry *fdb_find(struct hlist_head *head, + const unsigned char *addr, + __u16 vid); static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, const unsigned char *addr, u16 vid); static void fdb_notify(struct net_bridge *br, @@ -82,18 +98,88 @@ kmem_cache_free(br_fdb_cache, ent); } +static void fdb_delete_recipient(struct net_bridge *br, struct net_bridge_fdb_entry *f) +{ +#ifdef CONFIG_AVM_BRIDGE_MULTICAST_TO_UNICAST + struct net_bridge_group_recipient *pos; + struct hlist_node *temp; + /* When deleting the fdb update each net_bridge_group_recipient which + * has it as recipient. Do so in an RCU-safe manner because the recipients + * might be walked concurrently in br_forward_as_unicast(). the pg_list updaters + * operate so under the br->multicast_lock so sync with that*/ + spin_lock_bh(&br->multicast_lock); + hlist_for_each_entry_safe(pos, temp, &f->pg_list, fdb_list) + br_multicast_delete_recipient(pos); + spin_unlock_bh(&br->multicast_lock); +#endif +} + static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f) { +#if IS_ENABLED(CONFIG_FUSIV_KERNEL_AP_2_AP) + if(ap2apBridgeFlowDelete_ptr != NULL) + { + (*ap2apBridgeFlowDelete_ptr)(f); + } else + printk("\nbr_fdb_put: ap2ap_lkm not initialized properly...\n"); +#endif hlist_del_rcu(&f->hlist); fdb_notify(br, f, RTM_DELNEIGH); + fdb_delete_recipient(br, f); call_rcu(&f->rcu, fdb_rcu_free); } +/* Delete a local entry if no other port had the same address. */ +static void fdb_delete_local(struct net_bridge *br, + const struct net_bridge_port *p, + struct net_bridge_fdb_entry *f) +{ + const unsigned char *addr = f->addr.addr; + u16 vid = f->vlan_id; + struct net_bridge_port *op; + + /* Maybe another port has same hw addr? */ + list_for_each_entry(op, &br->port_list, list) { + if (op != p && ether_addr_equal(op->dev->dev_addr, addr) && + (!vid || nbp_vlan_find(op, vid))) { + f->dst = op; + f->added_by_user = 0; + return; + } + } + + /* Maybe bridge device has same hw addr? */ + if (p && ether_addr_equal(br->dev->dev_addr, addr) && + (!vid || br_vlan_find(br, vid))) { + f->dst = NULL; + f->added_by_user = 0; + return; + } + + fdb_delete(br, f); +} + +void br_fdb_find_delete_local(struct net_bridge *br, + const struct net_bridge_port *p, + const unsigned char *addr, u16 vid) +{ + struct hlist_head *head = &br->hash[br_mac_hash(addr, vid)]; + struct net_bridge_fdb_entry *f; + + spin_lock_bh(&br->hash_lock); + f = fdb_find(head, addr, vid); + if (f && f->is_local && !f->added_by_user && f->dst == p) + fdb_delete_local(br, p, f); + spin_unlock_bh(&br->hash_lock); +} + void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr) { struct net_bridge *br = p->br; - bool no_vlan = (nbp_get_vlan_info(p) == NULL) ? true : false; + struct net_port_vlans *pv = nbp_get_vlan_info(p); + bool no_vlan = !pv; int i; + u16 vid; spin_lock_bh(&br->hash_lock); @@ -104,38 +190,39 @@ struct net_bridge_fdb_entry *f; f = hlist_entry(h, struct net_bridge_fdb_entry, hlist); - if (f->dst == p && f->is_local) { - /* maybe another port has same hw addr? */ - struct net_bridge_port *op; - u16 vid = f->vlan_id; - list_for_each_entry(op, &br->port_list, list) { - if (op != p && - ether_addr_equal(op->dev->dev_addr, - f->addr.addr) && - nbp_vlan_find(op, vid)) { - f->dst = op; - goto insert; - } - } - + if (f->dst == p && f->is_local && !f->added_by_user) { /* delete old one */ - fdb_delete(br, f); -insert: - /* insert new address, may fail if invalid - * address or dup. - */ - fdb_insert(br, p, newaddr, vid); - + fdb_delete_local(br, p, f); +#if IS_ENABLED(CONFIG_FUSIV_KERNEL_AP_2_AP) + if(apAddBridgePortNewHWaddr_ptr != NULL) + { + (*apAddBridgePortNewHWaddr_ptr)(p); + } +#endif /* if this port has no vlan information * configured, we can safely be done at * this point. */ if (no_vlan) - goto done; + goto insert; } } } +insert: + /* insert new address, may fail if invalid address or dup. */ + fdb_insert(br, p, newaddr, 0); + + if (no_vlan) + 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. + */ + for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) + fdb_insert(br, p, newaddr, vid); + done: spin_unlock_bh(&br->hash_lock); } @@ -146,10 +233,12 @@ struct net_port_vlans *pv; u16 vid = 0; + spin_lock_bh(&br->hash_lock); + /* 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) - fdb_delete(br, f); + fdb_delete_local(br, NULL, f); fdb_insert(br, NULL, newaddr, 0); @@ -159,14 +248,16 @@ */ pv = br_get_vlan_info(br); if (!pv) - return; + goto out; for_each_set_bit_from(vid, pv->vlan_bitmap, VLAN_N_VID) { f = __br_fdb_get(br, br->dev->dev_addr, vid); if (f && f->is_local && !f->dst) - fdb_delete(br, f); + fdb_delete_local(br, NULL, f); fdb_insert(br, NULL, newaddr, vid); } +out: + spin_unlock_bh(&br->hash_lock); } void br_fdb_cleanup(unsigned long _data) @@ -183,6 +274,15 @@ hlist_for_each_entry_safe(f, n, &br->hash[i], hlist) { unsigned long this_timer; +#if IS_ENABLED(CONFIG_FUSIV_KERNEL_AP_2_AP) + if(Isap2apBridgeTraffic_ptr != NULL) + { + if (!(*Isap2apBridgeTraffic_ptr)((void *)f)) + continue; + } else + printk("\n ap2ap module must required\n"); +#endif + if (f->is_static) continue; this_timer = f->updated + delay; @@ -235,25 +335,11 @@ if (f->is_static && !do_all) continue; - /* - * if multiple ports all have the same device address - * then when one port is deleted, assign - * the local entry to other port - */ - if (f->is_local) { - struct net_bridge_port *op; - list_for_each_entry(op, &br->port_list, list) { - if (op != p && - ether_addr_equal(op->dev->dev_addr, - f->addr.addr)) { - f->dst = op; - goto skip_delete; - } - } - } - fdb_delete(br, f); - skip_delete: ; + if (f->is_local) + fdb_delete_local(br, p, f); + else + fdb_delete(br, f); } } spin_unlock_bh(&br->hash_lock); @@ -303,6 +389,23 @@ } #endif /* CONFIG_ATM_LANE */ +// AVM/TKL: MERGE from Ikanos +#if 0 +/* Set entry up for deletion with RCU */ +void br_fdb_put(struct net_bridge_fdb_entry *ent) +{ + if (atomic_dec_and_test(&ent->use_count)) { +#if IS_ENABLED(CONFIG_FUSIV_KERNEL_AP_2_AP) + if(ap2apBridgeFlowDelete_ptr != NULL) + { + (*ap2apBridgeFlowDelete_ptr)(ent); + } else + printk("\nbr_fdb_put: ap2ap_lkm not initialized properly...\n"); +#endif + call_rcu(&ent->rcu, fdb_rcu_free); + } +} +#endif /* * Fill buffer with forwarding table records in * the API format. @@ -392,12 +495,19 @@ fdb = kmem_cache_alloc(br_fdb_cache, GFP_ATOMIC); if (fdb) { +#if IS_ENABLED(CONFIG_FUSIV_KERNEL_AP_2_AP) + memset(fdb, 0, sizeof(*fdb)); +#endif memcpy(fdb->addr.addr, addr, ETH_ALEN); fdb->dst = source; fdb->vlan_id = vid; fdb->is_local = 0; fdb->is_static = 0; + fdb->added_by_user = 0; fdb->updated = fdb->used = jiffies; +#ifdef CONFIG_AVM_BRIDGE_MULTICAST_TO_UNICAST + INIT_HLIST_HEAD(&fdb->pg_list); +#endif hlist_add_head_rcu(&fdb->hlist, head); } return fdb; @@ -447,10 +557,11 @@ } void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, - const unsigned char *addr, u16 vid) + const unsigned char *addr, u16 vid, bool added_by_user) { struct hlist_head *head = &br->hash[br_mac_hash(addr, vid)]; struct net_bridge_fdb_entry *fdb; + bool fdb_modified = false; /* some users want to always flood. */ if (hold_time(br) == 0) @@ -471,15 +582,25 @@ source->dev->name); } else { /* fastpath: update of existing entry */ - fdb->dst = source; + if (unlikely(source != fdb->dst)) { + fdb->dst = source; + fdb_modified = true; + } fdb->updated = jiffies; + if (unlikely(added_by_user)) + fdb->added_by_user = 1; + if (unlikely(fdb_modified)) + fdb_notify(br, fdb, RTM_NEWNEIGH); } } else { spin_lock(&br->hash_lock); if (likely(!fdb_find(head, addr, vid))) { fdb = fdb_create(head, source, addr, vid); - if (fdb) + if (fdb) { + if (unlikely(added_by_user)) + fdb->added_by_user = 1; fdb_notify(br, fdb, RTM_NEWNEIGH); + } } /* else we lose race and someone else inserts * it first, don't bother updating @@ -556,6 +677,13 @@ struct sk_buff *skb; int err = -ENOBUFS; +#ifdef CONFIG_AVM_PA +#ifdef AVM_PA_FLUSH_SESSIONS_FOR_MAC + /* fdb changed port or was deleted, must cancel any existing bypass. */ + avm_pa_flush_sessions_for_mac(fdb->addr.addr); +#endif +#endif + skb = nlmsg_new(fdb_nlmsg_size(), GFP_ATOMIC); if (skb == NULL) goto errout; @@ -570,8 +698,7 @@ rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC); return; errout: - if (err < 0) - rtnl_set_sk_err(net, RTNLGRP_NEIGH, err); + rtnl_set_sk_err(net, RTNLGRP_NEIGH, err); } /* Dump information about entries, in response to GETNEIGH */ @@ -648,6 +775,7 @@ modified = true; } + fdb->added_by_user = 1; fdb->used = jiffies; if (modified) { @@ -665,7 +793,7 @@ if (ndm->ndm_flags & NTF_USE) { rcu_read_lock(); - br_fdb_update(p->br, p, addr, vid); + br_fdb_update(p->br, p, addr, vid, true); rcu_read_unlock(); } else { spin_lock_bh(&p->br->hash_lock); @@ -700,13 +828,18 @@ vid = nla_get_u16(tb[NDA_VLAN]); - if (vid >= VLAN_N_VID) { + if (!vid || vid >= VLAN_VID_MASK) { pr_info("bridge: RTM_NEWNEIGH with invalid vlan id %d\n", vid); return -EINVAL; } } + if (is_zero_ether_addr(addr)) { + pr_info("bridge: RTM_NEWNEIGH with invalid ether address\n"); + return -EINVAL; + } + p = br_port_get_rtnl(dev); if (p == NULL) { pr_info("bridge: RTM_NEWNEIGH %s not a bridge port\n", @@ -745,8 +878,7 @@ return err; } -int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, - u16 vlan) +static int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, u16 vlan) { struct hlist_head *head = &br->hash[br_mac_hash(addr, vlan)]; struct net_bridge_fdb_entry *fdb; @@ -789,7 +921,7 @@ vid = nla_get_u16(tb[NDA_VLAN]); - if (vid >= VLAN_N_VID) { + if (!vid || vid >= VLAN_VID_MASK) { pr_info("bridge: RTM_NEWNEIGH with invalid vlan id %d\n", vid); return -EINVAL; @@ -829,3 +961,24 @@ out: return err; } +#if IS_ENABLED(CONFIG_FUSIV_KERNEL_AP_2_AP) +struct net_bridge_fdb_entry *br_fdb_get(struct net_bridge *br, + unsigned char *addr) +{ + struct net_bridge_fdb_entry *fdb; + + rcu_read_lock(); + fdb = __br_fdb_get(br, addr,0); //Modified for 3.10.28 + /*if (fdb && !atomic_inc_not_zero(&fdb->use_count)) + fdb = NULL;*/ + rcu_read_unlock(); + return fdb; +} +EXPORT_SYMBOL(br_fdb_get); +EXPORT_SYMBOL(br_mac_hash); +EXPORT_SYMBOL(br_fdb_update); +EXPORT_SYMBOL(ap2apBridgeFlowDelete_ptr); +EXPORT_SYMBOL(Isap2apBridgeTraffic_ptr); +EXPORT_SYMBOL(fdb_delete); +EXPORT_SYMBOL(apAddBridgePortNewHWaddr_ptr); +#endif