mac80211: Save probe response data for bss
authorArik Nemtsov <arik@wizery.com>
Thu, 10 Nov 2011 09:28:57 +0000 (11:28 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 11 Nov 2011 17:32:51 +0000 (12:32 -0500)
Allow setting a probe response template for an interface operating in
AP mode. Low level drivers are notified about changes in the probe
response template and are able to retrieve a copy of the current probe
response. This data can, for example, be uploaded to hardware as a
template.

Signed-off-by: Guy Eilam <guy@wizery.com>
Signed-off-by: Arik Nemtsov <arik@wizery.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
include/net/mac80211.h
net/mac80211/cfg.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/tx.c
net/mac80211/util.c

index 2714646b298f835815a0d3d3430702e94e78d96c..0756049ae76d69c2a8cb9e2342deb868cefeaf94 100644 (file)
@@ -166,6 +166,7 @@ struct ieee80211_low_level_stats {
  *     that it is only ever disabled for station mode.
  * @BSS_CHANGED_IDLE: Idle changed for this BSS/interface.
  * @BSS_CHANGED_SSID: SSID changed for this BSS (AP mode)
+ * @BSS_CHANGED_AP_PROBE_RESP: Probe Response changed for this BSS (AP mode)
  */
 enum ieee80211_bss_change {
        BSS_CHANGED_ASSOC               = 1<<0,
@@ -184,6 +185,7 @@ enum ieee80211_bss_change {
        BSS_CHANGED_QOS                 = 1<<13,
        BSS_CHANGED_IDLE                = 1<<14,
        BSS_CHANGED_SSID                = 1<<15,
+       BSS_CHANGED_AP_PROBE_RESP       = 1<<16,
 
        /* when adding here, make sure to change ieee80211_reconfig */
 };
@@ -2674,6 +2676,19 @@ static inline struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
        return ieee80211_beacon_get_tim(hw, vif, NULL, NULL);
 }
 
+/**
+ * ieee80211_proberesp_get - retrieve a Probe Response template
+ * @hw: pointer obtained from ieee80211_alloc_hw().
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ *
+ * Creates a Probe Response template which can, for example, be uploaded to
+ * hardware. The destination address should be set by the caller.
+ *
+ * Can only be called in AP mode.
+ */
+struct sk_buff *ieee80211_proberesp_get(struct ieee80211_hw *hw,
+                                       struct ieee80211_vif *vif);
+
 /**
  * ieee80211_pspoll_get - retrieve a PS Poll template
  * @hw: pointer obtained from ieee80211_alloc_hw().
index 192f213cf43e9d89745ff53485bf8060f9373c24..c2416fbd1b27a3691ec341e745d54cdb2e8b94e7 100644 (file)
@@ -491,6 +491,31 @@ static void ieee80211_config_ap_ssid(struct ieee80211_sub_if_data *sdata,
                (params->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE);
 }
 
+static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
+                                   u8 *resp, size_t resp_len)
+{
+       struct sk_buff *new, *old;
+
+       if (!resp || !resp_len)
+               return -EINVAL;
+
+       old = sdata->u.ap.probe_resp;
+
+       new = dev_alloc_skb(resp_len);
+       if (!new)
+               return -ENOMEM;
+
+       memcpy(skb_put(new, resp_len), resp, resp_len);
+
+       rcu_assign_pointer(sdata->u.ap.probe_resp, new);
+       synchronize_rcu();
+
+       if (old)
+               dev_kfree_skb(old);
+
+       return 0;
+}
+
 /*
  * This handles both adding a beacon and setting new beacon info
  */
@@ -501,6 +526,7 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata,
        int new_head_len, new_tail_len;
        int size;
        int err = -EINVAL;
+       u32 changed = 0;
 
        old = rtnl_dereference(sdata->u.ap.beacon);
 
@@ -584,11 +610,17 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata,
 
        kfree(old);
 
+       err = ieee80211_set_probe_resp(sdata, params->probe_resp,
+                                      params->probe_resp_len);
+       if (!err)
+               changed |= BSS_CHANGED_AP_PROBE_RESP;
+
        ieee80211_config_ap_ssid(sdata, params);
+       changed |= BSS_CHANGED_BEACON_ENABLED |
+                  BSS_CHANGED_BEACON |
+                  BSS_CHANGED_SSID;
 
-       ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED |
-                                               BSS_CHANGED_BEACON |
-                                               BSS_CHANGED_SSID);
+       ieee80211_bss_info_change_notify(sdata, changed);
        return 0;
 }
 
index 873d681a8e5259af6c8513d17199ced2ab4df548..068cc92d16aa8dea3f33ce2b7793f87aa7407798 100644 (file)
@@ -232,6 +232,7 @@ struct beacon_data {
 
 struct ieee80211_if_ap {
        struct beacon_data __rcu *beacon;
+       struct sk_buff __rcu *probe_resp;
 
        struct list_head vlans;
 
index 7b0c25bf8bbf36b65a0406133cec6cb411cf4b01..12a6d4bb5d3736b348996bac224b43d5651873a1 100644 (file)
@@ -462,15 +462,19 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
                struct ieee80211_sub_if_data *vlan, *tmpsdata;
                struct beacon_data *old_beacon =
                        rtnl_dereference(sdata->u.ap.beacon);
+               struct sk_buff *old_probe_resp =
+                       rtnl_dereference(sdata->u.ap.probe_resp);
 
                /* sdata_running will return false, so this will disable */
                ieee80211_bss_info_change_notify(sdata,
                                                 BSS_CHANGED_BEACON_ENABLED);
 
-               /* remove beacon */
+               /* remove beacon and probe response */
                RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
+               RCU_INIT_POINTER(sdata->u.ap.probe_resp, NULL);
                synchronize_rcu();
                kfree(old_beacon);
+               kfree(old_probe_resp);
 
                /* down all dependent devices, that is VLANs */
                list_for_each_entry_safe(vlan, tmpsdata, &sdata->u.ap.vlans,
index ab6cb56bc74d253989feb65b945f3747c7963c39..2b413d38daa79f975d6321aaa43a012b87376adc 100644 (file)
@@ -2415,6 +2415,37 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
 }
 EXPORT_SYMBOL(ieee80211_beacon_get_tim);
 
+struct sk_buff *ieee80211_proberesp_get(struct ieee80211_hw *hw,
+                                       struct ieee80211_vif *vif)
+{
+       struct ieee80211_if_ap *ap = NULL;
+       struct sk_buff *presp = NULL, *skb = NULL;
+       struct ieee80211_hdr *hdr;
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+       if (sdata->vif.type != NL80211_IFTYPE_AP)
+               return NULL;
+
+       rcu_read_lock();
+
+       ap = &sdata->u.ap;
+       presp = rcu_dereference(ap->probe_resp);
+       if (!presp)
+               goto out;
+
+       skb = skb_copy(presp, GFP_ATOMIC);
+       if (!skb)
+               goto out;
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+       memset(hdr->addr1, 0, sizeof(hdr->addr1));
+
+out:
+       rcu_read_unlock();
+       return skb;
+}
+EXPORT_SYMBOL(ieee80211_proberesp_get);
+
 struct sk_buff *ieee80211_pspoll_get(struct ieee80211_hw *hw,
                                     struct ieee80211_vif *vif)
 {
index 6ed0aa430789f411295eacefe5c4e3abfd388970..4cf25b0eea74797f1e89c8dd5fbedae508183994 100644 (file)
@@ -1071,7 +1071,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                        changed |= BSS_CHANGED_IBSS;
                        /* fall through */
                case NL80211_IFTYPE_AP:
-                       changed |= BSS_CHANGED_SSID;
+                       changed |= BSS_CHANGED_SSID |
+                                  BSS_CHANGED_AP_PROBE_RESP;
                        /* fall through */
                case NL80211_IFTYPE_MESH_POINT:
                        changed |= BSS_CHANGED_BEACON |