Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os 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>

+63 -18
+10
include/linux/ieee80211.h
··· 695 695 } __packed; 696 696 697 697 /** 698 + * struct ieee80211_wide_bw_chansw_ie - wide bandwidth channel switch IE 699 + */ 700 + struct ieee80211_wide_bw_chansw_ie { 701 + u8 new_channel_width; 702 + u8 new_center_freq_seg0, new_center_freq_seg1; 703 + } __packed; 704 + 705 + /** 698 706 * struct ieee80211_tim 699 707 * 700 708 * This structure refers to "Traffic Indication Map information element" ··· 1706 1698 WLAN_EID_VHT_CAPABILITY = 191, 1707 1699 WLAN_EID_VHT_OPERATION = 192, 1708 1700 WLAN_EID_OPMODE_NOTIF = 199, 1701 + WLAN_EID_WIDE_BW_CHANNEL_SWITCH = 194, 1702 + WLAN_EID_CHANNEL_SWITCH_WRAPPER = 196, 1709 1703 1710 1704 /* 802.11ad */ 1711 1705 WLAN_EID_NON_TX_BSSID_CAP = 83,
+1 -1
net/mac80211/ibss.c
··· 914 914 return; 915 915 916 916 ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, 917 - &elems); 917 + false, &elems); 918 918 919 919 ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems); 920 920 }
+4 -3
net/mac80211/ieee80211_i.h
··· 1179 1179 const struct ieee80211_rann_ie *rann; 1180 1180 const struct ieee80211_channel_sw_ie *ch_switch_ie; 1181 1181 const struct ieee80211_ext_chansw_ie *ext_chansw_ie; 1182 + const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie; 1182 1183 const u8 *country_elem; 1183 1184 const u8 *pwr_constr_elem; 1184 1185 const struct ieee80211_timeout_interval_ie *timeout_int; ··· 1491 1490 ieee80211_tx_skb_tid(sdata, skb, 7); 1492 1491 } 1493 1492 1494 - u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, 1493 + u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, bool action, 1495 1494 struct ieee802_11_elems *elems, 1496 1495 u64 filter, u32 crc); 1497 - static inline void ieee802_11_parse_elems(u8 *start, size_t len, 1496 + static inline void ieee802_11_parse_elems(u8 *start, size_t len, bool action, 1498 1497 struct ieee802_11_elems *elems) 1499 1498 { 1500 - ieee802_11_parse_elems_crc(start, len, elems, 0, 0); 1499 + ieee802_11_parse_elems_crc(start, len, action, elems, 0, 0); 1501 1500 } 1502 1501 1503 1502 u32 ieee80211_mandatory_rates(struct ieee80211_local *local,
+2 -2
net/mac80211/mesh.c
··· 838 838 if (baselen > len) 839 839 return; 840 840 841 - ieee802_11_parse_elems(pos, len - baselen, &elems); 841 + ieee802_11_parse_elems(pos, len - baselen, false, &elems); 842 842 843 843 /* 802.11-2012 10.1.4.3.2 */ 844 844 if ((!ether_addr_equal(mgmt->da, sdata->vif.addr) && ··· 899 899 return; 900 900 901 901 ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, 902 - &elems); 902 + false, &elems); 903 903 904 904 /* ignore non-mesh or secure / unsecure mismatch */ 905 905 if ((!elems.mesh_id || !elems.mesh_config) ||
+1 -1
net/mac80211/mesh_hwmp.c
··· 880 880 881 881 baselen = (u8 *) mgmt->u.action.u.mesh_action.variable - (u8 *) mgmt; 882 882 ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable, 883 - len - baselen, &elems); 883 + len - baselen, false, &elems); 884 884 885 885 if (elems.preq) { 886 886 if (elems.preq_len != 37)
+1 -1
net/mac80211/mesh_plink.c
··· 687 687 baseaddr += 4; 688 688 baselen += 4; 689 689 } 690 - ieee802_11_parse_elems(baseaddr, len - baselen, &elems); 690 + ieee802_11_parse_elems(baseaddr, len - baselen, true, &elems); 691 691 692 692 if (!elems.peering) { 693 693 mpl_dbg(sdata,
+8 -8
net/mac80211/mlme.c
··· 2203 2203 u32 tx_flags = 0; 2204 2204 2205 2205 pos = mgmt->u.auth.variable; 2206 - ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); 2206 + ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems); 2207 2207 if (!elems.challenge) 2208 2208 return; 2209 2209 auth_data->expected_transaction = 4; ··· 2468 2468 } 2469 2469 2470 2470 pos = mgmt->u.assoc_resp.variable; 2471 - ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); 2471 + ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems); 2472 2472 2473 2473 if (!elems.supp_rates) { 2474 2474 sdata_info(sdata, "no SuppRates element in AssocResp\n"); ··· 2637 2637 capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14)))); 2638 2638 2639 2639 pos = mgmt->u.assoc_resp.variable; 2640 - ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); 2640 + ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems); 2641 2641 2642 2642 if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY && 2643 2643 elems.timeout_int && ··· 2760 2760 return; 2761 2761 2762 2762 ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, 2763 - &elems); 2763 + false, &elems); 2764 2764 2765 2765 ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems); 2766 2766 ··· 2843 2843 if (ifmgd->assoc_data && ifmgd->assoc_data->need_beacon && 2844 2844 ether_addr_equal(mgmt->bssid, ifmgd->assoc_data->bss->bssid)) { 2845 2845 ieee802_11_parse_elems(mgmt->u.beacon.variable, 2846 - len - baselen, &elems); 2846 + len - baselen, false, &elems); 2847 2847 2848 2848 ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems); 2849 2849 ifmgd->assoc_data->have_beacon = true; ··· 2953 2953 2954 2954 ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4); 2955 2955 ncrc = ieee802_11_parse_elems_crc(mgmt->u.beacon.variable, 2956 - len - baselen, &elems, 2956 + len - baselen, false, &elems, 2957 2957 care_about_ies, ncrc); 2958 2958 2959 2959 if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) { ··· 3141 3141 3142 3142 ieee802_11_parse_elems( 3143 3143 mgmt->u.action.u.chan_switch.variable, 3144 - ies_len, &elems); 3144 + ies_len, true, &elems); 3145 3145 3146 3146 if (elems.parse_error) 3147 3147 break; ··· 3159 3159 3160 3160 ieee802_11_parse_elems( 3161 3161 mgmt->u.action.u.ext_chan_switch.variable, 3162 - ies_len, &elems); 3162 + ies_len, true, &elems); 3163 3163 3164 3164 if (elems.parse_error) 3165 3165 break;
+1 -1
net/mac80211/scan.c
··· 181 181 if (baselen > skb->len) 182 182 return; 183 183 184 - ieee802_11_parse_elems(elements, skb->len - baselen, &elems); 184 + ieee802_11_parse_elems(elements, skb->len - baselen, false, &elems); 185 185 186 186 channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq); 187 187
+35 -1
net/mac80211/util.c
··· 661 661 } 662 662 EXPORT_SYMBOL(ieee80211_queue_delayed_work); 663 663 664 - u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, 664 + u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, bool action, 665 665 struct ieee802_11_elems *elems, 666 666 u64 filter, u32 crc) 667 667 { ··· 669 669 u8 *pos = start; 670 670 bool calc_crc = filter != 0; 671 671 DECLARE_BITMAP(seen_elems, 256); 672 + const u8 *ie; 672 673 673 674 bitmap_zero(seen_elems, 256); 674 675 memset(elems, 0, sizeof(*elems)); ··· 718 717 case WLAN_EID_PWR_CONSTRAINT: 719 718 case WLAN_EID_TIMEOUT_INTERVAL: 720 719 case WLAN_EID_SECONDARY_CHANNEL_OFFSET: 720 + case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: 721 + /* 722 + * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible 723 + * that if the content gets bigger it might be needed more than once 724 + */ 721 725 if (test_bit(id, seen_elems)) { 722 726 elems->parse_error = true; 723 727 left -= elen; ··· 883 877 break; 884 878 } 885 879 elems->sec_chan_offs = (void *)pos; 880 + break; 881 + case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: 882 + if (!action || 883 + elen != sizeof(*elems->wide_bw_chansw_ie)) { 884 + elem_parse_failed = true; 885 + break; 886 + } 887 + elems->wide_bw_chansw_ie = (void *)pos; 888 + break; 889 + case WLAN_EID_CHANNEL_SWITCH_WRAPPER: 890 + if (action) { 891 + elem_parse_failed = true; 892 + break; 893 + } 894 + /* 895 + * This is a bit tricky, but as we only care about 896 + * the wide bandwidth channel switch element, so 897 + * just parse it out manually. 898 + */ 899 + ie = cfg80211_find_ie(WLAN_EID_WIDE_BW_CHANNEL_SWITCH, 900 + pos, elen); 901 + if (ie) { 902 + if (ie[1] == sizeof(*elems->wide_bw_chansw_ie)) 903 + elems->wide_bw_chansw_ie = 904 + (void *)(ie + 2); 905 + else 906 + elem_parse_failed = true; 907 + } 886 908 break; 887 909 case WLAN_EID_COUNTRY: 888 910 elems->country_elem = pos;