Commit b2e506bf authored by Johannes Berg's avatar Johannes Berg

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: default avatarJohannes Berg <johannes.berg@intel.com>
parent 1b3a2e49
...@@ -694,6 +694,14 @@ struct ieee80211_sec_chan_offs_ie { ...@@ -694,6 +694,14 @@ struct ieee80211_sec_chan_offs_ie {
u8 sec_chan_offs; u8 sec_chan_offs;
} __packed; } __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 * struct ieee80211_tim
* *
...@@ -1698,6 +1706,8 @@ enum ieee80211_eid { ...@@ -1698,6 +1706,8 @@ enum ieee80211_eid {
WLAN_EID_VHT_CAPABILITY = 191, WLAN_EID_VHT_CAPABILITY = 191,
WLAN_EID_VHT_OPERATION = 192, WLAN_EID_VHT_OPERATION = 192,
WLAN_EID_OPMODE_NOTIF = 199, WLAN_EID_OPMODE_NOTIF = 199,
WLAN_EID_WIDE_BW_CHANNEL_SWITCH = 194,
WLAN_EID_CHANNEL_SWITCH_WRAPPER = 196,
/* 802.11ad */ /* 802.11ad */
WLAN_EID_NON_TX_BSSID_CAP = 83, WLAN_EID_NON_TX_BSSID_CAP = 83,
......
...@@ -914,7 +914,7 @@ void ieee80211_rx_mgmt_probe_beacon(struct ieee80211_sub_if_data *sdata, ...@@ -914,7 +914,7 @@ void ieee80211_rx_mgmt_probe_beacon(struct ieee80211_sub_if_data *sdata,
return; return;
ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
&elems); false, &elems);
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems); ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
} }
......
...@@ -1179,6 +1179,7 @@ struct ieee802_11_elems { ...@@ -1179,6 +1179,7 @@ struct ieee802_11_elems {
const struct ieee80211_rann_ie *rann; const struct ieee80211_rann_ie *rann;
const struct ieee80211_channel_sw_ie *ch_switch_ie; const struct ieee80211_channel_sw_ie *ch_switch_ie;
const struct ieee80211_ext_chansw_ie *ext_chansw_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 *country_elem;
const u8 *pwr_constr_elem; const u8 *pwr_constr_elem;
const struct ieee80211_timeout_interval_ie *timeout_int; const struct ieee80211_timeout_interval_ie *timeout_int;
...@@ -1490,13 +1491,13 @@ static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, ...@@ -1490,13 +1491,13 @@ static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata,
ieee80211_tx_skb_tid(sdata, skb, 7); 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, struct ieee802_11_elems *elems,
u64 filter, u32 crc); 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) 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, u32 ieee80211_mandatory_rates(struct ieee80211_local *local,
......
...@@ -838,7 +838,7 @@ ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata, ...@@ -838,7 +838,7 @@ ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,
if (baselen > len) if (baselen > len)
return; 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 */ /* 802.11-2012 10.1.4.3.2 */
if ((!ether_addr_equal(mgmt->da, sdata->vif.addr) && 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, ...@@ -899,7 +899,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
return; return;
ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
&elems); false, &elems);
/* ignore non-mesh or secure / unsecure mismatch */ /* ignore non-mesh or secure / unsecure mismatch */
if ((!elems.mesh_id || !elems.mesh_config) || if ((!elems.mesh_id || !elems.mesh_config) ||
......
...@@ -880,7 +880,7 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, ...@@ -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; baselen = (u8 *) mgmt->u.action.u.mesh_action.variable - (u8 *) mgmt;
ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable, ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable,
len - baselen, &elems); len - baselen, false, &elems);
if (elems.preq) { if (elems.preq) {
if (elems.preq_len != 37) if (elems.preq_len != 37)
......
...@@ -687,7 +687,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, ...@@ -687,7 +687,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,
baseaddr += 4; baseaddr += 4;
baselen += 4; baselen += 4;
} }
ieee802_11_parse_elems(baseaddr, len - baselen, &elems); ieee802_11_parse_elems(baseaddr, len - baselen, true, &elems);
if (!elems.peering) { if (!elems.peering) {
mpl_dbg(sdata, mpl_dbg(sdata,
......
...@@ -2203,7 +2203,7 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata, ...@@ -2203,7 +2203,7 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
u32 tx_flags = 0; u32 tx_flags = 0;
pos = mgmt->u.auth.variable; 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) if (!elems.challenge)
return; return;
auth_data->expected_transaction = 4; auth_data->expected_transaction = 4;
...@@ -2468,7 +2468,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, ...@@ -2468,7 +2468,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
} }
pos = mgmt->u.assoc_resp.variable; 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) { if (!elems.supp_rates) {
sdata_info(sdata, "no SuppRates element in AssocResp\n"); sdata_info(sdata, "no SuppRates element in AssocResp\n");
...@@ -2637,7 +2637,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, ...@@ -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)))); capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14))));
pos = mgmt->u.assoc_resp.variable; 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 && if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY &&
elems.timeout_int && elems.timeout_int &&
...@@ -2760,7 +2760,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, ...@@ -2760,7 +2760,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
return; return;
ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
&elems); false, &elems);
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &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, ...@@ -2843,7 +2843,7 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
if (ifmgd->assoc_data && ifmgd->assoc_data->need_beacon && if (ifmgd->assoc_data && ifmgd->assoc_data->need_beacon &&
ether_addr_equal(mgmt->bssid, ifmgd->assoc_data->bss->bssid)) { ether_addr_equal(mgmt->bssid, ifmgd->assoc_data->bss->bssid)) {
ieee802_11_parse_elems(mgmt->u.beacon.variable, 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); ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
ifmgd->assoc_data->have_beacon = true; ifmgd->assoc_data->have_beacon = true;
...@@ -2953,7 +2953,7 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ...@@ -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 = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4);
ncrc = ieee802_11_parse_elems_crc(mgmt->u.beacon.variable, ncrc = ieee802_11_parse_elems_crc(mgmt->u.beacon.variable,
len - baselen, &elems, len - baselen, false, &elems,
care_about_ies, ncrc); care_about_ies, ncrc);
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) { 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, ...@@ -3141,7 +3141,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
ieee802_11_parse_elems( ieee802_11_parse_elems(
mgmt->u.action.u.chan_switch.variable, mgmt->u.action.u.chan_switch.variable,
ies_len, &elems); ies_len, true, &elems);
if (elems.parse_error) if (elems.parse_error)
break; break;
...@@ -3159,7 +3159,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, ...@@ -3159,7 +3159,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
ieee802_11_parse_elems( ieee802_11_parse_elems(
mgmt->u.action.u.ext_chan_switch.variable, mgmt->u.action.u.ext_chan_switch.variable,
ies_len, &elems); ies_len, true, &elems);
if (elems.parse_error) if (elems.parse_error)
break; break;
......
...@@ -181,7 +181,7 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb) ...@@ -181,7 +181,7 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
if (baselen > skb->len) if (baselen > skb->len)
return; 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); channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq);
......
...@@ -661,7 +661,7 @@ void ieee80211_queue_delayed_work(struct ieee80211_hw *hw, ...@@ -661,7 +661,7 @@ void ieee80211_queue_delayed_work(struct ieee80211_hw *hw,
} }
EXPORT_SYMBOL(ieee80211_queue_delayed_work); 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, struct ieee802_11_elems *elems,
u64 filter, u32 crc) u64 filter, u32 crc)
{ {
...@@ -669,6 +669,7 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, ...@@ -669,6 +669,7 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
u8 *pos = start; u8 *pos = start;
bool calc_crc = filter != 0; bool calc_crc = filter != 0;
DECLARE_BITMAP(seen_elems, 256); DECLARE_BITMAP(seen_elems, 256);
const u8 *ie;
bitmap_zero(seen_elems, 256); bitmap_zero(seen_elems, 256);
memset(elems, 0, sizeof(*elems)); memset(elems, 0, sizeof(*elems));
...@@ -717,6 +718,11 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, ...@@ -717,6 +718,11 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
case WLAN_EID_PWR_CONSTRAINT: case WLAN_EID_PWR_CONSTRAINT:
case WLAN_EID_TIMEOUT_INTERVAL: case WLAN_EID_TIMEOUT_INTERVAL:
case WLAN_EID_SECONDARY_CHANNEL_OFFSET: 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)) { if (test_bit(id, seen_elems)) {
elems->parse_error = true; elems->parse_error = true;
left -= elen; left -= elen;
...@@ -878,6 +884,34 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, ...@@ -878,6 +884,34 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
} }
elems->sec_chan_offs = (void *)pos; elems->sec_chan_offs = (void *)pos;
break; 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: case WLAN_EID_COUNTRY:
elems->country_elem = pos; elems->country_elem = pos;
elems->country_elem_len = elen; elems->country_elem_len = elen;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment