@@ -296,6 +296,7 @@ struct mesh_stats {
* @is_secure: true if the mesh is secure
* @can_share: true if this bss can be shared (user-configurable per-if)
* @net: network namespace devices in this mbss belong to
+ * @rmc: Recent Multicast Cache for this mbss
* @list: listptr for siblings in mesh_bss_list
* @if_list: interfaces sharing this bss
*/
@@ -310,6 +311,7 @@ struct mesh_local_bss {
bool can_share;
struct net *net;
+ struct mesh_rmc *rmc;
struct list_head list;
struct list_head if_list;
};
@@ -598,7 +600,6 @@ struct ieee80211_if_mesh {
unsigned long next_perr;
/* Timestamp of last PREQ sent */
unsigned long last_preq;
- struct mesh_rmc *rmc;
spinlock_t mesh_preq_queue_lock;
struct mesh_preq_queue preq_queue;
int preq_queue_len;
@@ -994,9 +994,6 @@ static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata)
__skb_queue_purge(&sdata->fragments[i].skb_list);
sdata->fragment_next = 0;
- if (ieee80211_vif_is_mesh(&sdata->vif))
- mesh_rmc_free(sdata);
-
flushed = sta_info_flush(sdata);
WARN_ON(flushed);
}
@@ -100,6 +100,11 @@ mesh_bss_create(struct ieee80211_sub_if_data *sdata, struct mesh_setup *setup)
if (!mbss)
return NULL;
+ if (mesh_rmc_init(mbss)) {
+ kfree(mbss);
+ return NULL;
+ }
+
INIT_LIST_HEAD(&mbss->if_list);
mbss->mesh_id_len = setup->mesh_id_len;
@@ -129,6 +134,7 @@ static void mesh_bss_remove(struct ieee80211_sub_if_data *sdata)
/* free when no more devs have this mbss */
if (list_empty(&mbss->if_list)) {
list_del(&mbss->list);
+ mesh_rmc_free(mbss);
kfree(mbss);
}
mutex_unlock(&mesh_bss_mtx);
@@ -360,26 +366,31 @@ void mesh_sta_cleanup(struct sta_info *sta)
ieee80211_mbss_info_change_notify(sdata, changed);
}
-int mesh_rmc_init(struct ieee80211_sub_if_data *sdata)
+int mesh_rmc_init(struct mesh_local_bss *mbss)
{
+ struct mesh_rmc *rmc;
int i;
- sdata->u.mesh.rmc = kmalloc(sizeof(struct mesh_rmc), GFP_KERNEL);
- if (!sdata->u.mesh.rmc)
+ rmc = kmalloc(sizeof(struct mesh_rmc), GFP_KERNEL);
+ if (!rmc)
return -ENOMEM;
- sdata->u.mesh.rmc->idx_mask = RMC_BUCKETS - 1;
- for (i = 0; i < RMC_BUCKETS; i++)
- INIT_LIST_HEAD(&sdata->u.mesh.rmc->bucket[i]);
+ rmc->idx_mask = RMC_BUCKETS - 1;
+ for (i = 0; i < RMC_BUCKETS; i++) {
+ INIT_LIST_HEAD(&rmc->bucket[i]);
+ rwlock_init(&rmc->bucket_lock[i]);
+ }
+
+ mbss->rmc = rmc;
return 0;
}
-void mesh_rmc_free(struct ieee80211_sub_if_data *sdata)
+void mesh_rmc_free(struct mesh_local_bss *mbss)
{
- struct mesh_rmc *rmc = sdata->u.mesh.rmc;
+ struct mesh_rmc *rmc = mbss->rmc;
struct rmc_entry *p, *n;
int i;
- if (!sdata->u.mesh.rmc)
+ if (!rmc)
return;
for (i = 0; i < RMC_BUCKETS; i++) {
@@ -390,13 +401,12 @@ void mesh_rmc_free(struct ieee80211_sub_if_data *sdata)
}
kfree(rmc);
- sdata->u.mesh.rmc = NULL;
+ mbss->rmc = NULL;
}
/**
* mesh_rmc_check - Check frame in recent multicast cache and add if absent.
*
- * @sdata: interface
* @sa: source address
* @mesh_hdr: mesh_header
*
@@ -406,18 +416,20 @@ void mesh_rmc_free(struct ieee80211_sub_if_data *sdata)
* received this frame lately. If the frame is not in the cache, it is added to
* it.
*/
-int mesh_rmc_check(struct ieee80211_sub_if_data *sdata,
+int mesh_rmc_check(struct mesh_local_bss *mbss,
const u8 *sa, struct ieee80211s_hdr *mesh_hdr)
{
- struct mesh_rmc *rmc = sdata->u.mesh.rmc;
+ struct mesh_rmc *rmc = mbss->rmc;
u32 seqnum = 0;
- int entries = 0;
+ int entries = 0, ret = 0;
u8 idx;
struct rmc_entry *p, *n;
/* Don't care about endianness since only match matters */
memcpy(&seqnum, &mesh_hdr->seqnum, sizeof(mesh_hdr->seqnum));
idx = le32_to_cpu(mesh_hdr->seqnum) & rmc->idx_mask;
+
+ read_lock(&rmc->bucket_lock[idx]);
list_for_each_entry_safe(p, n, &rmc->bucket[idx], list) {
++entries;
if (time_after(jiffies, p->exp_time) ||
@@ -425,19 +437,28 @@ int mesh_rmc_check(struct ieee80211_sub_if_data *sdata,
list_del(&p->list);
kmem_cache_free(rm_cache, p);
--entries;
- } else if ((seqnum == p->seqnum) && ether_addr_equal(sa, p->sa))
- return -1;
+ } else if ((seqnum == p->seqnum) &&
+ ether_addr_equal(sa, p->sa)) {
+ ret = -1;
+ break;
+ }
}
+ read_unlock(&rmc->bucket_lock[idx]);
+
+ if (ret)
+ return ret;
p = kmem_cache_alloc(rm_cache, GFP_ATOMIC);
if (!p)
return 0;
+ write_lock(&rmc->bucket_lock[idx]);
p->seqnum = seqnum;
p->exp_time = jiffies + RMC_TIMEOUT;
memcpy(p->sa, sa, ETH_ALEN);
list_add(&p->list, &rmc->bucket[idx]);
- return 0;
+ write_unlock(&rmc->bucket_lock[idx]);
+ return ret;
}
int mesh_add_meshconf_ie(struct ieee80211_sub_if_data *sdata,
@@ -1242,7 +1263,6 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
ifmsh->sn = 0;
ifmsh->num_gates = 0;
atomic_set(&ifmsh->mpaths, 0);
- mesh_rmc_init(sdata);
ifmsh->last_preq = jiffies;
ifmsh->next_perr = jiffies;
/* Allocate all mesh structures when creating the first mesh interface. */
@@ -185,6 +185,7 @@ struct rmc_entry {
struct mesh_rmc {
struct list_head bucket[RMC_BUCKETS];
+ rwlock_t bucket_lock[RMC_BUCKETS];
u32 idx_mask;
};
@@ -209,7 +210,7 @@ int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc,
int ieee80211_new_mesh_header(struct ieee80211_sub_if_data *sdata,
struct ieee80211s_hdr *meshhdr,
const char *addr4or5, const char *addr6);
-int mesh_rmc_check(struct ieee80211_sub_if_data *sdata,
+int mesh_rmc_check(struct mesh_local_bss *mbss,
const u8 *addr, struct ieee80211s_hdr *mesh_hdr);
bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
struct ieee802_11_elems *ie);
@@ -228,8 +229,8 @@ int mesh_add_ht_cap_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb);
int mesh_add_ht_oper_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb);
-void mesh_rmc_free(struct ieee80211_sub_if_data *sdata);
-int mesh_rmc_init(struct ieee80211_sub_if_data *sdata);
+void mesh_rmc_free(struct mesh_local_bss *mbss);
+int mesh_rmc_init(struct mesh_local_bss *mbss);
void ieee80211s_init(void);
void ieee80211s_update_metric(struct ieee80211_local *local,
struct sta_info *sta, struct sk_buff *skb);
@@ -2033,7 +2033,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
/* frame is in RMC, don't forward */
if (ieee80211_is_data(hdr->frame_control) &&
is_multicast_ether_addr(hdr->addr1) &&
- mesh_rmc_check(rx->sdata, hdr->addr3, mesh_hdr))
+ mesh_rmc_check(mbss(sdata), hdr->addr3, mesh_hdr))
return RX_DROP_MONITOR;
if (!ieee80211_is_data(hdr->frame_control) ||
The RMC was previously per-sdata, but it is sufficient for one of the interfaces in a local mbss to receive a multicast frame once. Once multiple interfaces share the same mesh data forwarding duties, we may inadvertently create a multicast routing loop and have a corner case where a multicast frame is retransmitted a few times too many. Signed-off-by: Thomas Pedersen <thomas@cozybit.com> --- net/mac80211/ieee80211_i.h | 3 ++- net/mac80211/iface.c | 3 --- net/mac80211/mesh.c | 56 ++++++++++++++++++++++++++++++-------------- net/mac80211/mesh.h | 7 +++--- net/mac80211/rx.c | 2 +- 5 files changed, 45 insertions(+), 26 deletions(-)