forked from Minki/linux
mac80211: parse VHT channel switch IEs
VHT introduces multiple IEs that need to be parsed for a wide bandwidth channel switch. Two are (currently) needed in mac80211: * wide bandwidth channel switch element * channel switch wrapper element The former is contained in the latter for beacons and probe responses, but not for the spectrum management action frames so the IE parser needs a new argument to differentiate them. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
parent
1b3a2e494b
commit
b2e506bfc4
@ -694,6 +694,14 @@ struct ieee80211_sec_chan_offs_ie {
|
||||
u8 sec_chan_offs;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ieee80211_wide_bw_chansw_ie - wide bandwidth channel switch IE
|
||||
*/
|
||||
struct ieee80211_wide_bw_chansw_ie {
|
||||
u8 new_channel_width;
|
||||
u8 new_center_freq_seg0, new_center_freq_seg1;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ieee80211_tim
|
||||
*
|
||||
@ -1698,6 +1706,8 @@ enum ieee80211_eid {
|
||||
WLAN_EID_VHT_CAPABILITY = 191,
|
||||
WLAN_EID_VHT_OPERATION = 192,
|
||||
WLAN_EID_OPMODE_NOTIF = 199,
|
||||
WLAN_EID_WIDE_BW_CHANNEL_SWITCH = 194,
|
||||
WLAN_EID_CHANNEL_SWITCH_WRAPPER = 196,
|
||||
|
||||
/* 802.11ad */
|
||||
WLAN_EID_NON_TX_BSSID_CAP = 83,
|
||||
|
@ -914,7 +914,7 @@ void ieee80211_rx_mgmt_probe_beacon(struct ieee80211_sub_if_data *sdata,
|
||||
return;
|
||||
|
||||
ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
|
||||
&elems);
|
||||
false, &elems);
|
||||
|
||||
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
|
||||
}
|
||||
|
@ -1179,6 +1179,7 @@ struct ieee802_11_elems {
|
||||
const struct ieee80211_rann_ie *rann;
|
||||
const struct ieee80211_channel_sw_ie *ch_switch_ie;
|
||||
const struct ieee80211_ext_chansw_ie *ext_chansw_ie;
|
||||
const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie;
|
||||
const u8 *country_elem;
|
||||
const u8 *pwr_constr_elem;
|
||||
const struct ieee80211_timeout_interval_ie *timeout_int;
|
||||
@ -1490,13 +1491,13 @@ static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata,
|
||||
ieee80211_tx_skb_tid(sdata, skb, 7);
|
||||
}
|
||||
|
||||
u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
|
||||
u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, bool action,
|
||||
struct ieee802_11_elems *elems,
|
||||
u64 filter, u32 crc);
|
||||
static inline void ieee802_11_parse_elems(u8 *start, size_t len,
|
||||
static inline void ieee802_11_parse_elems(u8 *start, size_t len, bool action,
|
||||
struct ieee802_11_elems *elems)
|
||||
{
|
||||
ieee802_11_parse_elems_crc(start, len, elems, 0, 0);
|
||||
ieee802_11_parse_elems_crc(start, len, action, elems, 0, 0);
|
||||
}
|
||||
|
||||
u32 ieee80211_mandatory_rates(struct ieee80211_local *local,
|
||||
|
@ -838,7 +838,7 @@ ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,
|
||||
if (baselen > len)
|
||||
return;
|
||||
|
||||
ieee802_11_parse_elems(pos, len - baselen, &elems);
|
||||
ieee802_11_parse_elems(pos, len - baselen, false, &elems);
|
||||
|
||||
/* 802.11-2012 10.1.4.3.2 */
|
||||
if ((!ether_addr_equal(mgmt->da, sdata->vif.addr) &&
|
||||
@ -899,7 +899,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
|
||||
return;
|
||||
|
||||
ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
|
||||
&elems);
|
||||
false, &elems);
|
||||
|
||||
/* ignore non-mesh or secure / unsecure mismatch */
|
||||
if ((!elems.mesh_id || !elems.mesh_config) ||
|
||||
|
@ -880,7 +880,7 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
baselen = (u8 *) mgmt->u.action.u.mesh_action.variable - (u8 *) mgmt;
|
||||
ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable,
|
||||
len - baselen, &elems);
|
||||
len - baselen, false, &elems);
|
||||
|
||||
if (elems.preq) {
|
||||
if (elems.preq_len != 37)
|
||||
|
@ -687,7 +687,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,
|
||||
baseaddr += 4;
|
||||
baselen += 4;
|
||||
}
|
||||
ieee802_11_parse_elems(baseaddr, len - baselen, &elems);
|
||||
ieee802_11_parse_elems(baseaddr, len - baselen, true, &elems);
|
||||
|
||||
if (!elems.peering) {
|
||||
mpl_dbg(sdata,
|
||||
|
@ -2203,7 +2203,7 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
|
||||
u32 tx_flags = 0;
|
||||
|
||||
pos = mgmt->u.auth.variable;
|
||||
ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
|
||||
ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems);
|
||||
if (!elems.challenge)
|
||||
return;
|
||||
auth_data->expected_transaction = 4;
|
||||
@ -2468,7 +2468,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
|
||||
}
|
||||
|
||||
pos = mgmt->u.assoc_resp.variable;
|
||||
ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
|
||||
ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems);
|
||||
|
||||
if (!elems.supp_rates) {
|
||||
sdata_info(sdata, "no SuppRates element in AssocResp\n");
|
||||
@ -2637,7 +2637,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
|
||||
capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14))));
|
||||
|
||||
pos = mgmt->u.assoc_resp.variable;
|
||||
ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
|
||||
ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems);
|
||||
|
||||
if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY &&
|
||||
elems.timeout_int &&
|
||||
@ -2760,7 +2760,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
|
||||
return;
|
||||
|
||||
ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
|
||||
&elems);
|
||||
false, &elems);
|
||||
|
||||
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
|
||||
|
||||
@ -2843,7 +2843,7 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
||||
if (ifmgd->assoc_data && ifmgd->assoc_data->need_beacon &&
|
||||
ether_addr_equal(mgmt->bssid, ifmgd->assoc_data->bss->bssid)) {
|
||||
ieee802_11_parse_elems(mgmt->u.beacon.variable,
|
||||
len - baselen, &elems);
|
||||
len - baselen, false, &elems);
|
||||
|
||||
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
|
||||
ifmgd->assoc_data->have_beacon = true;
|
||||
@ -2953,7 +2953,7 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4);
|
||||
ncrc = ieee802_11_parse_elems_crc(mgmt->u.beacon.variable,
|
||||
len - baselen, &elems,
|
||||
len - baselen, false, &elems,
|
||||
care_about_ies, ncrc);
|
||||
|
||||
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) {
|
||||
@ -3141,7 +3141,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
ieee802_11_parse_elems(
|
||||
mgmt->u.action.u.chan_switch.variable,
|
||||
ies_len, &elems);
|
||||
ies_len, true, &elems);
|
||||
|
||||
if (elems.parse_error)
|
||||
break;
|
||||
@ -3159,7 +3159,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
ieee802_11_parse_elems(
|
||||
mgmt->u.action.u.ext_chan_switch.variable,
|
||||
ies_len, &elems);
|
||||
ies_len, true, &elems);
|
||||
|
||||
if (elems.parse_error)
|
||||
break;
|
||||
|
@ -181,7 +181,7 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
|
||||
if (baselen > skb->len)
|
||||
return;
|
||||
|
||||
ieee802_11_parse_elems(elements, skb->len - baselen, &elems);
|
||||
ieee802_11_parse_elems(elements, skb->len - baselen, false, &elems);
|
||||
|
||||
channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq);
|
||||
|
||||
|
@ -661,7 +661,7 @@ void ieee80211_queue_delayed_work(struct ieee80211_hw *hw,
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_queue_delayed_work);
|
||||
|
||||
u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
|
||||
u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, bool action,
|
||||
struct ieee802_11_elems *elems,
|
||||
u64 filter, u32 crc)
|
||||
{
|
||||
@ -669,6 +669,7 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
|
||||
u8 *pos = start;
|
||||
bool calc_crc = filter != 0;
|
||||
DECLARE_BITMAP(seen_elems, 256);
|
||||
const u8 *ie;
|
||||
|
||||
bitmap_zero(seen_elems, 256);
|
||||
memset(elems, 0, sizeof(*elems));
|
||||
@ -717,6 +718,11 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
|
||||
case WLAN_EID_PWR_CONSTRAINT:
|
||||
case WLAN_EID_TIMEOUT_INTERVAL:
|
||||
case WLAN_EID_SECONDARY_CHANNEL_OFFSET:
|
||||
case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
|
||||
/*
|
||||
* not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible
|
||||
* that if the content gets bigger it might be needed more than once
|
||||
*/
|
||||
if (test_bit(id, seen_elems)) {
|
||||
elems->parse_error = true;
|
||||
left -= elen;
|
||||
@ -878,6 +884,34 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
|
||||
}
|
||||
elems->sec_chan_offs = (void *)pos;
|
||||
break;
|
||||
case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
|
||||
if (!action ||
|
||||
elen != sizeof(*elems->wide_bw_chansw_ie)) {
|
||||
elem_parse_failed = true;
|
||||
break;
|
||||
}
|
||||
elems->wide_bw_chansw_ie = (void *)pos;
|
||||
break;
|
||||
case WLAN_EID_CHANNEL_SWITCH_WRAPPER:
|
||||
if (action) {
|
||||
elem_parse_failed = true;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* This is a bit tricky, but as we only care about
|
||||
* the wide bandwidth channel switch element, so
|
||||
* just parse it out manually.
|
||||
*/
|
||||
ie = cfg80211_find_ie(WLAN_EID_WIDE_BW_CHANNEL_SWITCH,
|
||||
pos, elen);
|
||||
if (ie) {
|
||||
if (ie[1] == sizeof(*elems->wide_bw_chansw_ie))
|
||||
elems->wide_bw_chansw_ie =
|
||||
(void *)(ie + 2);
|
||||
else
|
||||
elem_parse_failed = true;
|
||||
}
|
||||
break;
|
||||
case WLAN_EID_COUNTRY:
|
||||
elems->country_elem = pos;
|
||||
elems->country_elem_len = elen;
|
||||
|
Loading…
Reference in New Issue
Block a user