mac80211: parse VHT channel switch IEs
authorJohannes Berg <johannes.berg@intel.com>
Tue, 26 Mar 2013 13:54:16 +0000 (14:54 +0100)
committerJohannes Berg <johannes.berg@intel.com>
Tue, 16 Apr 2013 13:29:45 +0000 (15:29 +0200)
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>
include/linux/ieee80211.h
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/mesh.c
net/mac80211/mesh_hwmp.c
net/mac80211/mesh_plink.c
net/mac80211/mlme.c
net/mac80211/scan.c
net/mac80211/util.c

index ce07161c873504b3ff8222d2ba3bf27830490dfa..06b0ed0154a4b779e637eaee48ec84cb8818498e 100644 (file)
@@ -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,
index b7bf6d76f1d91bb8fd41be910ac0919cdf44f7b4..170f9a7fa3190afee4148bb4caf0c817a53b7991 100644 (file)
@@ -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);
 }
index 8f240c0ec304b3d4a3b30272bc6bfc85100c932b..f4a65a340a521e969cd948e7c01198a0b2a9cb06 100644 (file)
@@ -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,
index 0acc2874d2943852efcc8ab835e8b7fe69e63af4..4b984765d62db5b07119cbf854d5d2afcd946ad4 100644 (file)
@@ -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) ||
index c82d5e6a24c03977ee40d7fbdf0e98ab6a210318..486819cd02cd7d03924e30d4e93f7ac201248b4c 100644 (file)
@@ -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)
index cdd41835334d18c2676aa6a81294aace6f59546e..09bebed99416d1f848dd95f98cf488de20a36fdf 100644 (file)
@@ -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,
index c53aedb47a6a2dcd4e3a0b30da3c392e39825501..3e0421265bfed44c73b59ef07d6086a512beb832 100644 (file)
@@ -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;
index 33fbf104569047826078c4d528c5b7d0e14abdd2..99b103921a4befecdaa8d95a1391d3f040479fb1 100644 (file)
@@ -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);
 
index 155056c90edf6d892deb1af596204d2d77aa46ae..3f87fa468b1fc18d0ab00079166da5bf053aa738 100644 (file)
@@ -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;