]> Pileus Git - ~andy/linux/blobdiff - net/mac80211/ibss.c
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless
[~andy/linux] / net / mac80211 / ibss.c
index 539d4a11b47bc9be2d30625ef0d79c97c9d9cdd5..170f9a7fa3190afee4148bb4caf0c817a53b7991 100644 (file)
@@ -44,7 +44,6 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
        struct ieee80211_local *local = sdata->local;
        int rates, i;
-       struct sk_buff *skb;
        struct ieee80211_mgmt *mgmt;
        u8 *pos;
        struct ieee80211_supported_band *sband;
@@ -52,20 +51,14 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        u32 bss_change;
        u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
        struct cfg80211_chan_def chandef;
+       struct beacon_data *presp;
+       int frame_len;
 
        lockdep_assert_held(&ifibss->mtx);
 
        /* Reset own TSF to allow time synchronization work. */
        drv_reset_tsf(local, sdata);
 
-       skb = ifibss->skb;
-       RCU_INIT_POINTER(ifibss->presp, NULL);
-       synchronize_rcu();
-       skb->data = skb->head;
-       skb->len = 0;
-       skb_reset_tail_pointer(skb);
-       skb_reserve(skb, sdata->local->hw.extra_tx_headroom);
-
        if (!ether_addr_equal(ifibss->bssid, bssid))
                sta_info_flush(sdata);
 
@@ -73,10 +66,19 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        if (sdata->vif.bss_conf.ibss_joined) {
                sdata->vif.bss_conf.ibss_joined = false;
                sdata->vif.bss_conf.ibss_creator = false;
+               sdata->vif.bss_conf.enable_beacon = false;
                netif_carrier_off(sdata->dev);
-               ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IBSS);
+               ieee80211_bss_info_change_notify(sdata,
+                                                BSS_CHANGED_IBSS |
+                                                BSS_CHANGED_BEACON_ENABLED);
        }
 
+       presp = rcu_dereference_protected(ifibss->presp,
+                                         lockdep_is_held(&ifibss->mtx));
+       rcu_assign_pointer(ifibss->presp, NULL);
+       if (presp)
+               kfree_rcu(presp, rcu_head);
+
        sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
 
        cfg80211_chandef_create(&chandef, chan, ifibss->channel_type);
@@ -98,19 +100,24 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
 
        sband = local->hw.wiphy->bands[chan->band];
 
-       /* build supported rates array */
-       pos = supp_rates;
-       for (i = 0; i < sband->n_bitrates; i++) {
-               int rate = sband->bitrates[i].bitrate;
-               u8 basic = 0;
-               if (basic_rates & BIT(i))
-                       basic = 0x80;
-               *pos++ = basic | (u8) (rate / 5);
-       }
-
        /* Build IBSS probe response */
-       mgmt = (void *) skb_put(skb, 24 + sizeof(mgmt->u.beacon));
-       memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon));
+       frame_len = sizeof(struct ieee80211_hdr_3addr) +
+                   12 /* struct ieee80211_mgmt.u.beacon */ +
+                   2 + IEEE80211_MAX_SSID_LEN /* max SSID */ +
+                   2 + 8 /* max Supported Rates */ +
+                   3 /* max DS params */ +
+                   4 /* IBSS params */ +
+                   2 + (IEEE80211_MAX_SUPP_RATES - 8) +
+                   2 + sizeof(struct ieee80211_ht_cap) +
+                   2 + sizeof(struct ieee80211_ht_operation) +
+                   ifibss->ie_len;
+       presp = kzalloc(sizeof(*presp) + frame_len, GFP_KERNEL);
+       if (!presp)
+               return;
+
+       presp->head = (void *)(presp + 1);
+
+       mgmt = (void *) presp->head;
        mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
                                          IEEE80211_STYPE_PROBE_RESP);
        eth_broadcast_addr(mgmt->da);
@@ -120,27 +127,30 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        mgmt->u.beacon.timestamp = cpu_to_le64(tsf);
        mgmt->u.beacon.capab_info = cpu_to_le16(capability);
 
-       pos = skb_put(skb, 2 + ifibss->ssid_len);
+       pos = (u8 *)mgmt + offsetof(struct ieee80211_mgmt, u.beacon.variable);
+
        *pos++ = WLAN_EID_SSID;
        *pos++ = ifibss->ssid_len;
        memcpy(pos, ifibss->ssid, ifibss->ssid_len);
+       pos += ifibss->ssid_len;
 
-       rates = sband->n_bitrates;
-       if (rates > 8)
-               rates = 8;
-       pos = skb_put(skb, 2 + rates);
+       rates = min_t(int, 8, sband->n_bitrates);
        *pos++ = WLAN_EID_SUPP_RATES;
        *pos++ = rates;
-       memcpy(pos, supp_rates, rates);
+       for (i = 0; i < rates; i++) {
+               int rate = sband->bitrates[i].bitrate;
+               u8 basic = 0;
+               if (basic_rates & BIT(i))
+                       basic = 0x80;
+               *pos++ = basic | (u8) (rate / 5);
+       }
 
        if (sband->band == IEEE80211_BAND_2GHZ) {
-               pos = skb_put(skb, 2 + 1);
                *pos++ = WLAN_EID_DS_PARAMS;
                *pos++ = 1;
                *pos++ = ieee80211_frequency_to_channel(chan->center_freq);
        }
 
-       pos = skb_put(skb, 2 + 2);
        *pos++ = WLAN_EID_IBSS_PARAMS;
        *pos++ = 2;
        /* FIX: set ATIM window based on scan results */
@@ -148,23 +158,25 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        *pos++ = 0;
 
        if (sband->n_bitrates > 8) {
-               rates = sband->n_bitrates - 8;
-               pos = skb_put(skb, 2 + rates);
                *pos++ = WLAN_EID_EXT_SUPP_RATES;
-               *pos++ = rates;
-               memcpy(pos, &supp_rates[8], rates);
+               *pos++ = sband->n_bitrates - 8;
+               for (i = 8; i < sband->n_bitrates; i++) {
+                       int rate = sband->bitrates[i].bitrate;
+                       u8 basic = 0;
+                       if (basic_rates & BIT(i))
+                               basic = 0x80;
+                       *pos++ = basic | (u8) (rate / 5);
+               }
        }
 
-       if (ifibss->ie_len)
-               memcpy(skb_put(skb, ifibss->ie_len),
-                      ifibss->ie, ifibss->ie_len);
+       if (ifibss->ie_len) {
+               memcpy(pos, ifibss->ie, ifibss->ie_len);
+               pos += ifibss->ie_len;
+       }
 
        /* add HT capability and information IEs */
        if (chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
            sband->ht_cap.ht_supported) {
-               pos = skb_put(skb, 4 +
-                                  sizeof(struct ieee80211_ht_cap) +
-                                  sizeof(struct ieee80211_ht_operation));
                pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap,
                                                sband->ht_cap.cap);
                /*
@@ -177,7 +189,6 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        }
 
        if (local->hw.queues >= IEEE80211_NUM_ACS) {
-               pos = skb_put(skb, 9);
                *pos++ = WLAN_EID_VENDOR_SPECIFIC;
                *pos++ = 7; /* len */
                *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
@@ -189,11 +200,17 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
                *pos++ = 0; /* U-APSD no in use */
        }
 
-       rcu_assign_pointer(ifibss->presp, skb);
+       presp->head_len = pos - presp->head;
+       if (WARN_ON(presp->head_len > frame_len))
+               return;
+
+       rcu_assign_pointer(ifibss->presp, presp);
 
        sdata->vif.bss_conf.enable_beacon = true;
        sdata->vif.bss_conf.beacon_int = beacon_int;
        sdata->vif.bss_conf.basic_rates = basic_rates;
+       sdata->vif.bss_conf.ssid_len = ifibss->ssid_len;
+       memcpy(sdata->vif.bss_conf.ssid, ifibss->ssid, ifibss->ssid_len);
        bss_change = BSS_CHANGED_BEACON_INT;
        bss_change |= ieee80211_reset_erp_info(sdata);
        bss_change |= BSS_CHANGED_BSSID;
@@ -202,6 +219,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        bss_change |= BSS_CHANGED_BASIC_RATES;
        bss_change |= BSS_CHANGED_HT;
        bss_change |= BSS_CHANGED_IBSS;
+       bss_change |= BSS_CHANGED_SSID;
 
        /*
         * In 5 GHz/802.11a, we can always use short slot time.
@@ -227,7 +245,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
                  round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL));
 
        bss = cfg80211_inform_bss_frame(local->hw.wiphy, chan,
-                                       mgmt, skb->len, 0, GFP_KERNEL);
+                                       mgmt, presp->head_len, 0, GFP_KERNEL);
        cfg80211_put_bss(local->hw.wiphy, bss);
        netif_carrier_on(sdata->dev);
        cfg80211_ibss_joined(sdata->dev, ifibss->bssid, GFP_KERNEL);
@@ -448,7 +466,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
        bool rates_updated = false;
 
-       if (elems->ds_params && elems->ds_params_len == 1)
+       if (elems->ds_params)
                freq = ieee80211_channel_to_frequency(elems->ds_params[0],
                                                      band);
        else
@@ -822,8 +840,7 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_local *local = sdata->local;
        int tx_last_beacon, len = req->len;
        struct sk_buff *skb;
-       struct ieee80211_mgmt *resp;
-       struct sk_buff *presp;
+       struct beacon_data *presp;
        u8 *pos, *end;
 
        lockdep_assert_held(&ifibss->mtx);
@@ -864,13 +881,15 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
        }
 
        /* Reply with ProbeResp */
-       skb = skb_copy(presp, GFP_KERNEL);
+       skb = dev_alloc_skb(local->tx_headroom + presp->head_len);
        if (!skb)
                return;
 
-       resp = (struct ieee80211_mgmt *) skb->data;
-       memcpy(resp->da, mgmt->sa, ETH_ALEN);
-       ibss_dbg(sdata, "Sending ProbeResp to %pM\n", resp->da);
+       skb_reserve(skb, local->tx_headroom);
+       memcpy(skb_put(skb, presp->head_len), presp->head, presp->head_len);
+
+       memcpy(((struct ieee80211_mgmt *) skb->data)->da, mgmt->sa, ETH_ALEN);
+       ibss_dbg(sdata, "Sending ProbeResp to %pM\n", mgmt->sa);
        IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
        ieee80211_tx_skb(sdata, skb);
 }
@@ -895,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);
 }
@@ -1020,23 +1039,8 @@ void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local)
 int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
                        struct cfg80211_ibss_params *params)
 {
-       struct sk_buff *skb;
        u32 changed = 0;
 
-       skb = dev_alloc_skb(sdata->local->hw.extra_tx_headroom +
-                           sizeof(struct ieee80211_hdr_3addr) +
-                           12 /* struct ieee80211_mgmt.u.beacon */ +
-                           2 + IEEE80211_MAX_SSID_LEN /* max SSID */ +
-                           2 + 8 /* max Supported Rates */ +
-                           3 /* max DS params */ +
-                           4 /* IBSS params */ +
-                           2 + (IEEE80211_MAX_SUPP_RATES - 8) +
-                           2 + sizeof(struct ieee80211_ht_cap) +
-                           2 + sizeof(struct ieee80211_ht_operation) +
-                           params->ie_len);
-       if (!skb)
-               return -ENOMEM;
-
        mutex_lock(&sdata->u.ibss.mtx);
 
        if (params->bssid) {
@@ -1065,7 +1069,6 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
                        sdata->u.ibss.ie_len = params->ie_len;
        }
 
-       sdata->u.ibss.skb = skb;
        sdata->u.ibss.state = IEEE80211_IBSS_MLME_SEARCH;
        sdata->u.ibss.ibss_join_req = jiffies;
 
@@ -1101,13 +1104,13 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
 
 int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
 {
-       struct sk_buff *skb;
        struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
        struct ieee80211_local *local = sdata->local;
        struct cfg80211_bss *cbss;
        u16 capability;
        int active_ibss;
        struct sta_info *sta;
+       struct beacon_data *presp;
 
        mutex_lock(&sdata->u.ibss.mtx);
 
@@ -1153,17 +1156,18 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
 
        /* remove beacon */
        kfree(sdata->u.ibss.ie);
-       skb = rcu_dereference_protected(sdata->u.ibss.presp,
-                                       lockdep_is_held(&sdata->u.ibss.mtx));
+       presp = rcu_dereference_protected(ifibss->presp,
+                                         lockdep_is_held(&sdata->u.ibss.mtx));
        RCU_INIT_POINTER(sdata->u.ibss.presp, NULL);
        sdata->vif.bss_conf.ibss_joined = false;
        sdata->vif.bss_conf.ibss_creator = false;
        sdata->vif.bss_conf.enable_beacon = false;
+       sdata->vif.bss_conf.ssid_len = 0;
        clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
        ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED |
                                                BSS_CHANGED_IBSS);
        synchronize_rcu();
-       kfree_skb(skb);
+       kfree(presp);
 
        skb_queue_purge(&sdata->skb_queue);