diff options
Diffstat (limited to 'net/mac80211/mesh.c')
-rw-r--r-- | net/mac80211/mesh.c | 171 |
1 files changed, 165 insertions, 6 deletions
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 5a99b8f6e465..f72333201903 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -10,6 +10,7 @@ #include <asm/unaligned.h> #include "ieee80211_i.h" #include "mesh.h" +#include "wme.h" #include "driver-ops.h" static int mesh_allocated; @@ -104,7 +105,7 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata, ieee80211_chandef_vht_oper(&sdata->local->hw, vht_cap_info, ie->vht_operation, ie->ht_operation, &sta_chan_def); - ieee80211_chandef_he_6ghz_oper(sdata, ie->he_operation, NULL, + ieee80211_chandef_he_6ghz_oper(sdata, ie->he_operation, ie->eht_operation, &sta_chan_def); if (!cfg80211_chandef_compatible(&sdata->vif.bss_conf.chandef, @@ -638,6 +639,65 @@ int mesh_add_he_6ghz_cap_ie(struct ieee80211_sub_if_data *sdata, return 0; } +int mesh_add_eht_cap_ie(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, u8 ie_len) +{ + const struct ieee80211_sta_he_cap *he_cap; + const struct ieee80211_sta_eht_cap *eht_cap; + struct ieee80211_supported_band *sband; + u8 *pos; + + sband = ieee80211_get_sband(sdata); + if (!sband) + return -EINVAL; + + he_cap = ieee80211_get_he_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT); + eht_cap = ieee80211_get_eht_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT); + if (!he_cap || !eht_cap || + sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT || + sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 || + sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10) + return 0; + + if (skb_tailroom(skb) < ie_len) + return -ENOMEM; + + pos = skb_put(skb, ie_len); + ieee80211_ie_build_eht_cap(pos, he_cap, eht_cap, pos + ie_len, false); + + return 0; +} + +int mesh_add_eht_oper_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) +{ + const struct ieee80211_sta_eht_cap *eht_cap; + struct ieee80211_supported_band *sband; + u32 len; + u8 *pos; + + sband = ieee80211_get_sband(sdata); + if (!sband) + return -EINVAL; + + eht_cap = ieee80211_get_eht_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT); + if (!eht_cap || + sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT || + sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 || + sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10) + return 0; + + len = 2 + 1 + offsetof(struct ieee80211_eht_operation, optional) + + offsetof(struct ieee80211_eht_operation_info, optional); + + if (skb_tailroom(skb) < len) + return -ENOMEM; + + pos = skb_put(skb, len); + ieee80211_ie_build_eht_oper(pos, &sdata->vif.bss_conf.chandef, eht_cap); + + return 0; +} + static void ieee80211_mesh_path_timer(struct timer_list *t) { struct ieee80211_sub_if_data *sdata = @@ -696,6 +756,98 @@ ieee80211_mesh_update_bss_params(struct ieee80211_sub_if_data *sdata, if (he_oper) sdata->vif.bss_conf.he_oper.params = __le32_to_cpu(he_oper->he_oper_params); + + sdata->vif.bss_conf.eht_support = + !!ieee80211_get_eht_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT); +} + +bool ieee80211_mesh_xmit_fast(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, u32 ctrl_flags) +{ + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + struct ieee80211_mesh_fast_tx *entry; + struct ieee80211s_hdr *meshhdr; + u8 sa[ETH_ALEN] __aligned(2); + struct tid_ampdu_tx *tid_tx; + struct sta_info *sta; + bool copy_sa = false; + u16 ethertype; + u8 tid; + + if (ctrl_flags & IEEE80211_TX_CTRL_SKIP_MPATH_LOOKUP) + return false; + + if (ifmsh->mshcfg.dot11MeshNolearn) + return false; + + /* Add support for these cases later */ + if (ifmsh->ps_peers_light_sleep || ifmsh->ps_peers_deep_sleep) + return false; + + if (is_multicast_ether_addr(skb->data)) + return false; + + ethertype = (skb->data[12] << 8) | skb->data[13]; + if (ethertype < ETH_P_802_3_MIN) + return false; + + if (skb->sk && skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS) + return false; + + if (skb->ip_summed == CHECKSUM_PARTIAL) { + skb_set_transport_header(skb, skb_checksum_start_offset(skb)); + if (skb_checksum_help(skb)) + return false; + } + + entry = mesh_fast_tx_get(sdata, skb->data); + if (!entry) + return false; + + if (skb_headroom(skb) < entry->hdrlen + entry->fast_tx.hdr_len) + return false; + + sta = rcu_dereference(entry->mpath->next_hop); + if (!sta) + return false; + + tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; + tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]); + if (tid_tx) { + if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) + return false; + if (tid_tx->timeout) + tid_tx->last_tx = jiffies; + } + + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) + return true; + + skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb)); + + meshhdr = (struct ieee80211s_hdr *)entry->hdr; + if ((meshhdr->flags & MESH_FLAGS_AE) == MESH_FLAGS_AE_A5_A6) { + /* preserve SA from eth header for 6-addr frames */ + ether_addr_copy(sa, skb->data + ETH_ALEN); + copy_sa = true; + } + + memcpy(skb_push(skb, entry->hdrlen - 2 * ETH_ALEN), entry->hdr, + entry->hdrlen); + + meshhdr = (struct ieee80211s_hdr *)skb->data; + put_unaligned_le32(atomic_inc_return(&sdata->u.mesh.mesh_seqnum), + &meshhdr->seqnum); + meshhdr->ttl = sdata->u.mesh.mshcfg.dot11MeshTTL; + if (copy_sa) + ether_addr_copy(meshhdr->eaddr2, sa); + + skb_push(skb, 2 * ETH_ALEN); + __ieee80211_xmit_fast(sdata, sta, &entry->fast_tx, skb, tid_tx, + entry->mpath->dst, sdata->vif.addr); + + return true; } /** @@ -752,10 +904,8 @@ unsigned int ieee80211_new_mesh_header(struct ieee80211_sub_if_data *sdata, meshhdr->ttl = sdata->u.mesh.mshcfg.dot11MeshTTL; - /* FIXME: racy -- TX on multiple queues can be concurrent */ - put_unaligned(cpu_to_le32(sdata->u.mesh.mesh_seqnum), &meshhdr->seqnum); - sdata->u.mesh.mesh_seqnum++; - + put_unaligned_le32(atomic_inc_return(&sdata->u.mesh.mesh_seqnum), + &meshhdr->seqnum); if (addr4or5 && !addr6) { meshhdr->flags |= MESH_FLAGS_AE_A4; memcpy(meshhdr->eaddr1, addr4or5, ETH_ALEN); @@ -782,6 +932,8 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata) changed = mesh_accept_plinks_update(sdata); ieee80211_mbss_info_change_notify(sdata, changed); + mesh_fast_tx_gc(sdata); + mod_timer(&ifmsh->housekeeping_timer, round_jiffies(jiffies + IEEE80211_MESH_HOUSEKEEPING_INTERVAL)); @@ -813,7 +965,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) struct ieee80211_chanctx_conf *chanctx_conf; struct mesh_csa_settings *csa; enum nl80211_band band; - u8 ie_len_he_cap; + u8 ie_len_he_cap, ie_len_eht_cap; u8 *pos; struct ieee80211_sub_if_data *sdata; int hdr_len = offsetofend(struct ieee80211_mgmt, u.beacon); @@ -826,6 +978,8 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) ie_len_he_cap = ieee80211_ie_len_he_cap(sdata, NL80211_IFTYPE_MESH_POINT); + ie_len_eht_cap = ieee80211_ie_len_eht_cap(sdata, + NL80211_IFTYPE_MESH_POINT); head_len = hdr_len + 2 + /* NULL SSID */ /* Channel Switch Announcement */ @@ -849,6 +1003,9 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) 2 + 1 + sizeof(struct ieee80211_he_operation) + sizeof(struct ieee80211_he_6ghz_oper) + 2 + 1 + sizeof(struct ieee80211_he_6ghz_capa) + + ie_len_eht_cap + + 2 + 1 + offsetof(struct ieee80211_eht_operation, optional) + + offsetof(struct ieee80211_eht_operation_info, optional) + ifmsh->ie_len; bcn = kzalloc(sizeof(*bcn) + head_len + tail_len, GFP_KERNEL); @@ -969,6 +1126,8 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) mesh_add_he_cap_ie(sdata, skb, ie_len_he_cap) || mesh_add_he_oper_ie(sdata, skb) || mesh_add_he_6ghz_cap_ie(sdata, skb) || + mesh_add_eht_cap_ie(sdata, skb, ie_len_eht_cap) || + mesh_add_eht_oper_ie(sdata, skb) || mesh_add_vendor_ies(sdata, skb)) goto out_free; |