| From: Felix Fietkau <nbd@nbd.name> |
| Date: Fri, 30 Jun 2023 13:11:51 +0200 |
| Subject: [PATCH] mac80211: split mesh fast tx cache into |
| local/proxied/forwarded |
| |
| Depending on the origin of the packets (and their SA), 802.11 + mesh headers |
| could be filled in differently. In order to properly deal with that, add a |
| new field to the lookup key, indicating the type (local, proxied or |
| forwarded). This can fix spurious packet drop issues that depend on the order |
| in which nodes/hosts communicate with each other. |
| |
| Signed-off-by: Felix Fietkau <nbd@nbd.name> |
| --- |
| |
| --- a/net/mac80211/mesh.c |
| +++ b/net/mac80211/mesh.c |
| @@ -765,6 +765,9 @@ bool ieee80211_mesh_xmit_fast(struct iee |
| struct sk_buff *skb, u32 ctrl_flags) |
| { |
| struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; |
| + struct ieee80211_mesh_fast_tx_key key = { |
| + .type = MESH_FAST_TX_TYPE_LOCAL |
| + }; |
| struct ieee80211_mesh_fast_tx *entry; |
| struct ieee80211s_hdr *meshhdr; |
| u8 sa[ETH_ALEN] __aligned(2); |
| @@ -800,7 +803,10 @@ bool ieee80211_mesh_xmit_fast(struct iee |
| return false; |
| } |
| |
| - entry = mesh_fast_tx_get(sdata, skb->data); |
| + ether_addr_copy(key.addr, skb->data); |
| + if (!ether_addr_equal(skb->data + ETH_ALEN, sdata->vif.addr)) |
| + key.type = MESH_FAST_TX_TYPE_PROXIED; |
| + entry = mesh_fast_tx_get(sdata, &key); |
| if (!entry) |
| return false; |
| |
| --- a/net/mac80211/mesh.h |
| +++ b/net/mac80211/mesh.h |
| @@ -134,9 +134,33 @@ struct mesh_path { |
| #define MESH_FAST_TX_CACHE_TIMEOUT 8000 /* msecs */ |
| |
| /** |
| + * enum ieee80211_mesh_fast_tx_type - cached mesh fast tx entry type |
| + * |
| + * @MESH_FAST_TX_TYPE_LOCAL: tx from the local vif address as SA |
| + * @MESH_FAST_TX_TYPE_PROXIED: local tx with a different SA (e.g. bridged) |
| + * @MESH_FAST_TX_TYPE_FORWARDED: forwarded from a different mesh point |
| + */ |
| +enum ieee80211_mesh_fast_tx_type { |
| + MESH_FAST_TX_TYPE_LOCAL, |
| + MESH_FAST_TX_TYPE_PROXIED, |
| + MESH_FAST_TX_TYPE_FORWARDED, |
| +}; |
| + |
| +/** |
| + * struct ieee80211_mesh_fast_tx_key - cached mesh fast tx entry key |
| + * |
| + * @addr: The Ethernet DA for this entry |
| + * @type: cache entry type |
| + */ |
| +struct ieee80211_mesh_fast_tx_key { |
| + u8 addr[ETH_ALEN] __aligned(2); |
| + enum ieee80211_mesh_fast_tx_type type; |
| +}; |
| + |
| +/** |
| * struct ieee80211_mesh_fast_tx - cached mesh fast tx entry |
| * @rhash: rhashtable pointer |
| - * @addr_key: The Ethernet DA which is the key for this entry |
| + * @key: the lookup key for this cache entry |
| * @fast_tx: base fast_tx data |
| * @hdr: cached mesh and rfc1042 headers |
| * @hdrlen: length of mesh + rfc1042 |
| @@ -147,7 +171,7 @@ struct mesh_path { |
| */ |
| struct ieee80211_mesh_fast_tx { |
| struct rhash_head rhash; |
| - u8 addr_key[ETH_ALEN] __aligned(2); |
| + struct ieee80211_mesh_fast_tx_key key; |
| |
| struct ieee80211_fast_tx fast_tx; |
| u8 hdr[sizeof(struct ieee80211s_hdr) + sizeof(rfc1042_header)]; |
| @@ -333,7 +357,8 @@ void mesh_path_tx_root_frame(struct ieee |
| |
| bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt); |
| struct ieee80211_mesh_fast_tx * |
| -mesh_fast_tx_get(struct ieee80211_sub_if_data *sdata, const u8 *addr); |
| +mesh_fast_tx_get(struct ieee80211_sub_if_data *sdata, |
| + struct ieee80211_mesh_fast_tx_key *key); |
| bool ieee80211_mesh_xmit_fast(struct ieee80211_sub_if_data *sdata, |
| struct sk_buff *skb, u32 ctrl_flags); |
| void mesh_fast_tx_cache(struct ieee80211_sub_if_data *sdata, |
| --- a/net/mac80211/mesh_pathtbl.c |
| +++ b/net/mac80211/mesh_pathtbl.c |
| @@ -36,8 +36,8 @@ static const struct rhashtable_params me |
| static const struct rhashtable_params fast_tx_rht_params = { |
| .nelem_hint = 10, |
| .automatic_shrinking = true, |
| - .key_len = ETH_ALEN, |
| - .key_offset = offsetof(struct ieee80211_mesh_fast_tx, addr_key), |
| + .key_len = sizeof(struct ieee80211_mesh_fast_tx_key), |
| + .key_offset = offsetof(struct ieee80211_mesh_fast_tx, key), |
| .head_offset = offsetof(struct ieee80211_mesh_fast_tx, rhash), |
| .hashfn = mesh_table_hash, |
| }; |
| @@ -426,20 +426,21 @@ static void mesh_fast_tx_entry_free(stru |
| } |
| |
| struct ieee80211_mesh_fast_tx * |
| -mesh_fast_tx_get(struct ieee80211_sub_if_data *sdata, const u8 *addr) |
| +mesh_fast_tx_get(struct ieee80211_sub_if_data *sdata, |
| + struct ieee80211_mesh_fast_tx_key *key) |
| { |
| struct ieee80211_mesh_fast_tx *entry; |
| struct mesh_tx_cache *cache; |
| |
| cache = &sdata->u.mesh.tx_cache; |
| - entry = rhashtable_lookup(&cache->rht, addr, fast_tx_rht_params); |
| + entry = rhashtable_lookup(&cache->rht, key, fast_tx_rht_params); |
| if (!entry) |
| return NULL; |
| |
| if (!(entry->mpath->flags & MESH_PATH_ACTIVE) || |
| mpath_expired(entry->mpath)) { |
| spin_lock_bh(&cache->walk_lock); |
| - entry = rhashtable_lookup(&cache->rht, addr, fast_tx_rht_params); |
| + entry = rhashtable_lookup(&cache->rht, key, fast_tx_rht_params); |
| if (entry) |
| mesh_fast_tx_entry_free(cache, entry); |
| spin_unlock_bh(&cache->walk_lock); |
| @@ -484,18 +485,24 @@ void mesh_fast_tx_cache(struct ieee80211 |
| if (!sta) |
| return; |
| |
| + build.key.type = MESH_FAST_TX_TYPE_LOCAL; |
| if ((meshhdr->flags & MESH_FLAGS_AE) == MESH_FLAGS_AE_A5_A6) { |
| /* This is required to keep the mppath alive */ |
| mppath = mpp_path_lookup(sdata, meshhdr->eaddr1); |
| if (!mppath) |
| return; |
| build.mppath = mppath; |
| + if (!ether_addr_equal(meshhdr->eaddr2, sdata->vif.addr)) |
| + build.key.type = MESH_FAST_TX_TYPE_PROXIED; |
| } else if (ieee80211_has_a4(hdr->frame_control)) { |
| mppath = mpath; |
| } else { |
| return; |
| } |
| |
| + if (!ether_addr_equal(hdr->addr4, sdata->vif.addr)) |
| + build.key.type = MESH_FAST_TX_TYPE_FORWARDED; |
| + |
| /* rate limit, in case fast xmit can't be enabled */ |
| if (mppath->fast_tx_check == jiffies) |
| return; |
| @@ -542,7 +549,7 @@ void mesh_fast_tx_cache(struct ieee80211 |
| } |
| } |
| |
| - memcpy(build.addr_key, mppath->dst, ETH_ALEN); |
| + memcpy(build.key.addr, mppath->dst, ETH_ALEN); |
| build.timestamp = jiffies; |
| build.fast_tx.band = info->band; |
| build.fast_tx.da_offs = offsetof(struct ieee80211_hdr, addr3); |
| @@ -644,13 +651,19 @@ void mesh_fast_tx_flush_addr(struct ieee |
| const u8 *addr) |
| { |
| struct mesh_tx_cache *cache = &sdata->u.mesh.tx_cache; |
| + struct ieee80211_mesh_fast_tx_key key = {}; |
| struct ieee80211_mesh_fast_tx *entry; |
| + int i; |
| |
| + ether_addr_copy(key.addr, addr); |
| cache = &sdata->u.mesh.tx_cache; |
| spin_lock_bh(&cache->walk_lock); |
| - entry = rhashtable_lookup_fast(&cache->rht, addr, fast_tx_rht_params); |
| - if (entry) |
| - mesh_fast_tx_entry_free(cache, entry); |
| + for (i = MESH_FAST_TX_TYPE_LOCAL; i < MESH_FAST_TX_TYPE_FORWARDED; i++) { |
| + key.type = i; |
| + entry = rhashtable_lookup_fast(&cache->rht, &key, fast_tx_rht_params); |
| + if (entry) |
| + mesh_fast_tx_entry_free(cache, entry); |
| + } |
| spin_unlock_bh(&cache->walk_lock); |
| } |
| |
| --- a/net/mac80211/rx.c |
| +++ b/net/mac80211/rx.c |
| @@ -2726,7 +2726,10 @@ ieee80211_rx_mesh_fast_forward(struct ie |
| struct sk_buff *skb, int hdrlen) |
| { |
| struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; |
| - struct ieee80211_mesh_fast_tx *entry = NULL; |
| + struct ieee80211_mesh_fast_tx_key key = { |
| + .type = MESH_FAST_TX_TYPE_FORWARDED |
| + }; |
| + struct ieee80211_mesh_fast_tx *entry; |
| struct ieee80211s_hdr *mesh_hdr; |
| struct tid_ampdu_tx *tid_tx; |
| struct sta_info *sta; |
| @@ -2735,9 +2738,13 @@ ieee80211_rx_mesh_fast_forward(struct ie |
| |
| mesh_hdr = (struct ieee80211s_hdr *)(skb->data + sizeof(eth)); |
| if ((mesh_hdr->flags & MESH_FLAGS_AE) == MESH_FLAGS_AE_A5_A6) |
| - entry = mesh_fast_tx_get(sdata, mesh_hdr->eaddr1); |
| + ether_addr_copy(key.addr, mesh_hdr->eaddr1); |
| else if (!(mesh_hdr->flags & MESH_FLAGS_AE)) |
| - entry = mesh_fast_tx_get(sdata, skb->data); |
| + ether_addr_copy(key.addr, skb->data); |
| + else |
| + return false; |
| + |
| + entry = mesh_fast_tx_get(sdata, &key); |
| if (!entry) |
| return false; |
| |