Merge tag 'for-f2fs-4.1' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk...
[firefly-linux-kernel-4.4.55.git] / net / wireless / scan.c
index c705c3e2b7510dd818bf0599db0a0ec887a43c2f..3a50aa2553bfd777cce2657eee38f0c3a7b34081 100644 (file)
@@ -531,24 +531,78 @@ static int cmp_bss(struct cfg80211_bss *a,
        }
 }
 
+static bool cfg80211_bss_type_match(u16 capability,
+                                   enum ieee80211_band band,
+                                   enum ieee80211_bss_type bss_type)
+{
+       bool ret = true;
+       u16 mask, val;
+
+       if (bss_type == IEEE80211_BSS_TYPE_ANY)
+               return ret;
+
+       if (band == IEEE80211_BAND_60GHZ) {
+               mask = WLAN_CAPABILITY_DMG_TYPE_MASK;
+               switch (bss_type) {
+               case IEEE80211_BSS_TYPE_ESS:
+                       val = WLAN_CAPABILITY_DMG_TYPE_AP;
+                       break;
+               case IEEE80211_BSS_TYPE_PBSS:
+                       val = WLAN_CAPABILITY_DMG_TYPE_PBSS;
+                       break;
+               case IEEE80211_BSS_TYPE_IBSS:
+                       val = WLAN_CAPABILITY_DMG_TYPE_IBSS;
+                       break;
+               default:
+                       return false;
+               }
+       } else {
+               mask = WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS;
+               switch (bss_type) {
+               case IEEE80211_BSS_TYPE_ESS:
+                       val = WLAN_CAPABILITY_ESS;
+                       break;
+               case IEEE80211_BSS_TYPE_IBSS:
+                       val = WLAN_CAPABILITY_IBSS;
+                       break;
+               case IEEE80211_BSS_TYPE_MBSS:
+                       val = 0;
+                       break;
+               default:
+                       return false;
+               }
+       }
+
+       ret = ((capability & mask) == val);
+       return ret;
+}
+
 /* Returned bss is reference counted and must be cleaned up appropriately. */
 struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
                                      struct ieee80211_channel *channel,
                                      const u8 *bssid,
                                      const u8 *ssid, size_t ssid_len,
-                                     u16 capa_mask, u16 capa_val)
+                                     enum ieee80211_bss_type bss_type,
+                                     enum ieee80211_privacy privacy)
 {
        struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct cfg80211_internal_bss *bss, *res = NULL;
        unsigned long now = jiffies;
+       int bss_privacy;
 
-       trace_cfg80211_get_bss(wiphy, channel, bssid, ssid, ssid_len, capa_mask,
-                              capa_val);
+       trace_cfg80211_get_bss(wiphy, channel, bssid, ssid, ssid_len, bss_type,
+                              privacy);
 
        spin_lock_bh(&rdev->bss_lock);
 
        list_for_each_entry(bss, &rdev->bss_list, list) {
-               if ((bss->pub.capability & capa_mask) != capa_val)
+               if (!cfg80211_bss_type_match(bss->pub.capability,
+                                            bss->pub.channel->band, bss_type))
+                       continue;
+
+               bss_privacy = (bss->pub.capability & WLAN_CAPABILITY_PRIVACY);
+               if ((privacy == IEEE80211_PRIVACY_ON && !bss_privacy) ||
+                   (privacy == IEEE80211_PRIVACY_OFF && bss_privacy))
                        continue;
                if (channel && bss->pub.channel != channel)
                        continue;
@@ -896,6 +950,7 @@ cfg80211_inform_bss_width(struct wiphy *wiphy,
        struct cfg80211_bss_ies *ies;
        struct ieee80211_channel *channel;
        struct cfg80211_internal_bss tmp = {}, *res;
+       int bss_type;
        bool signal_valid;
 
        if (WARN_ON(!wiphy))
@@ -950,8 +1005,15 @@ cfg80211_inform_bss_width(struct wiphy *wiphy,
        if (!res)
                return NULL;
 
-       if (res->pub.capability & WLAN_CAPABILITY_ESS)
-               regulatory_hint_found_beacon(wiphy, channel, gfp);
+       if (channel->band == IEEE80211_BAND_60GHZ) {
+               bss_type = res->pub.capability & WLAN_CAPABILITY_DMG_TYPE_MASK;
+               if (bss_type == WLAN_CAPABILITY_DMG_TYPE_AP ||
+                   bss_type == WLAN_CAPABILITY_DMG_TYPE_PBSS)
+                       regulatory_hint_found_beacon(wiphy, channel, gfp);
+       } else {
+               if (res->pub.capability & WLAN_CAPABILITY_ESS)
+                       regulatory_hint_found_beacon(wiphy, channel, gfp);
+       }
 
        trace_cfg80211_return_bss(&res->pub);
        /* cfg80211_bss_update gives us a referenced result */
@@ -973,6 +1035,7 @@ cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
        bool signal_valid;
        size_t ielen = len - offsetof(struct ieee80211_mgmt,
                                      u.probe_resp.variable);
+       int bss_type;
 
        BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) !=
                        offsetof(struct ieee80211_mgmt, u.beacon.variable));
@@ -1025,8 +1088,15 @@ cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
        if (!res)
                return NULL;
 
-       if (res->pub.capability & WLAN_CAPABILITY_ESS)
-               regulatory_hint_found_beacon(wiphy, channel, gfp);
+       if (channel->band == IEEE80211_BAND_60GHZ) {
+               bss_type = res->pub.capability & WLAN_CAPABILITY_DMG_TYPE_MASK;
+               if (bss_type == WLAN_CAPABILITY_DMG_TYPE_AP ||
+                   bss_type == WLAN_CAPABILITY_DMG_TYPE_PBSS)
+                       regulatory_hint_found_beacon(wiphy, channel, gfp);
+       } else {
+               if (res->pub.capability & WLAN_CAPABILITY_ESS)
+                       regulatory_hint_found_beacon(wiphy, channel, gfp);
+       }
 
        trace_cfg80211_return_bss(&res->pub);
        /* cfg80211_bss_update gives us a referenced result */
@@ -1237,17 +1307,17 @@ int cfg80211_wext_siwscan(struct net_device *dev,
        kfree(creq);
        return err;
 }
-EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan);
+EXPORT_WEXT_HANDLER(cfg80211_wext_siwscan);
 
-static void ieee80211_scan_add_ies(struct iw_request_info *info,
-                                  const struct cfg80211_bss_ies *ies,
-                                  char **current_ev, char *end_buf)
+static char *ieee80211_scan_add_ies(struct iw_request_info *info,
+                                   const struct cfg80211_bss_ies *ies,
+                                   char *current_ev, char *end_buf)
 {
        const u8 *pos, *end, *next;
        struct iw_event iwe;
 
        if (!ies)
-               return;
+               return current_ev;
 
        /*
         * If needed, fragment the IEs buffer (at IE boundaries) into short
@@ -1264,10 +1334,11 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info,
                memset(&iwe, 0, sizeof(iwe));
                iwe.cmd = IWEVGENIE;
                iwe.u.data.length = next - pos;
-               *current_ev = iwe_stream_add_point(info, *current_ev,
-                                                  end_buf, &iwe,
-                                                  (void *)pos);
-
+               current_ev = iwe_stream_add_point_check(info, current_ev,
+                                                       end_buf, &iwe,
+                                                       (void *)pos);
+               if (IS_ERR(current_ev))
+                       return current_ev;
                pos = next;
        }
 
@@ -1275,10 +1346,14 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info,
                memset(&iwe, 0, sizeof(iwe));
                iwe.cmd = IWEVGENIE;
                iwe.u.data.length = end - pos;
-               *current_ev = iwe_stream_add_point(info, *current_ev,
-                                                  end_buf, &iwe,
-                                                  (void *)pos);
+               current_ev = iwe_stream_add_point_check(info, current_ev,
+                                                       end_buf, &iwe,
+                                                       (void *)pos);
+               if (IS_ERR(current_ev))
+                       return current_ev;
        }
+
+       return current_ev;
 }
 
 static char *
@@ -1289,7 +1364,8 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
        const struct cfg80211_bss_ies *ies;
        struct iw_event iwe;
        const u8 *ie;
-       u8 *buf, *cfg, *p;
+       u8 buf[50];
+       u8 *cfg, *p, *tmp;
        int rem, i, sig;
        bool ismesh = false;
 
@@ -1297,22 +1373,28 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
        iwe.cmd = SIOCGIWAP;
        iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
        memcpy(iwe.u.ap_addr.sa_data, bss->pub.bssid, ETH_ALEN);
-       current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
-                                         IW_EV_ADDR_LEN);
+       current_ev = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
+                                               IW_EV_ADDR_LEN);
+       if (IS_ERR(current_ev))
+               return current_ev;
 
        memset(&iwe, 0, sizeof(iwe));
        iwe.cmd = SIOCGIWFREQ;
        iwe.u.freq.m = ieee80211_frequency_to_channel(bss->pub.channel->center_freq);
        iwe.u.freq.e = 0;
-       current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
-                                         IW_EV_FREQ_LEN);
+       current_ev = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
+                                               IW_EV_FREQ_LEN);
+       if (IS_ERR(current_ev))
+               return current_ev;
 
        memset(&iwe, 0, sizeof(iwe));
        iwe.cmd = SIOCGIWFREQ;
        iwe.u.freq.m = bss->pub.channel->center_freq;
        iwe.u.freq.e = 6;
-       current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
-                                         IW_EV_FREQ_LEN);
+       current_ev = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
+                                               IW_EV_FREQ_LEN);
+       if (IS_ERR(current_ev))
+               return current_ev;
 
        if (wiphy->signal_type != CFG80211_SIGNAL_TYPE_NONE) {
                memset(&iwe, 0, sizeof(iwe));
@@ -1341,8 +1423,11 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
                        /* not reached */
                        break;
                }
-               current_ev = iwe_stream_add_event(info, current_ev, end_buf,
-                                                 &iwe, IW_EV_QUAL_LEN);
+               current_ev = iwe_stream_add_event_check(info, current_ev,
+                                                       end_buf, &iwe,
+                                                       IW_EV_QUAL_LEN);
+               if (IS_ERR(current_ev))
+                       return current_ev;
        }
 
        memset(&iwe, 0, sizeof(iwe));
@@ -1352,8 +1437,10 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
        else
                iwe.u.data.flags = IW_ENCODE_DISABLED;
        iwe.u.data.length = 0;
-       current_ev = iwe_stream_add_point(info, current_ev, end_buf,
-                                         &iwe, "");
+       current_ev = iwe_stream_add_point_check(info, current_ev, end_buf,
+                                               &iwe, "");
+       if (IS_ERR(current_ev))
+               return current_ev;
 
        rcu_read_lock();
        ies = rcu_dereference(bss->pub.ies);
@@ -1371,66 +1458,91 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
                        iwe.cmd = SIOCGIWESSID;
                        iwe.u.data.length = ie[1];
                        iwe.u.data.flags = 1;
-                       current_ev = iwe_stream_add_point(info, current_ev, end_buf,
-                                                         &iwe, (u8 *)ie + 2);
+                       current_ev = iwe_stream_add_point_check(info,
+                                                               current_ev,
+                                                               end_buf, &iwe,
+                                                               (u8 *)ie + 2);
+                       if (IS_ERR(current_ev))
+                               goto unlock;
                        break;
                case WLAN_EID_MESH_ID:
                        memset(&iwe, 0, sizeof(iwe));
                        iwe.cmd = SIOCGIWESSID;
                        iwe.u.data.length = ie[1];
                        iwe.u.data.flags = 1;
-                       current_ev = iwe_stream_add_point(info, current_ev, end_buf,
-                                                         &iwe, (u8 *)ie + 2);
+                       current_ev = iwe_stream_add_point_check(info,
+                                                               current_ev,
+                                                               end_buf, &iwe,
+                                                               (u8 *)ie + 2);
+                       if (IS_ERR(current_ev))
+                               goto unlock;
                        break;
                case WLAN_EID_MESH_CONFIG:
                        ismesh = true;
                        if (ie[1] != sizeof(struct ieee80211_meshconf_ie))
                                break;
-                       buf = kmalloc(50, GFP_ATOMIC);
-                       if (!buf)
-                               break;
                        cfg = (u8 *)ie + 2;
                        memset(&iwe, 0, sizeof(iwe));
                        iwe.cmd = IWEVCUSTOM;
                        sprintf(buf, "Mesh Network Path Selection Protocol ID: "
                                "0x%02X", cfg[0]);
                        iwe.u.data.length = strlen(buf);
-                       current_ev = iwe_stream_add_point(info, current_ev,
-                                                         end_buf,
-                                                         &iwe, buf);
+                       current_ev = iwe_stream_add_point_check(info,
+                                                               current_ev,
+                                                               end_buf,
+                                                               &iwe, buf);
+                       if (IS_ERR(current_ev))
+                               goto unlock;
                        sprintf(buf, "Path Selection Metric ID: 0x%02X",
                                cfg[1]);
                        iwe.u.data.length = strlen(buf);
-                       current_ev = iwe_stream_add_point(info, current_ev,
-                                                         end_buf,
-                                                         &iwe, buf);
+                       current_ev = iwe_stream_add_point_check(info,
+                                                               current_ev,
+                                                               end_buf,
+                                                               &iwe, buf);
+                       if (IS_ERR(current_ev))
+                               goto unlock;
                        sprintf(buf, "Congestion Control Mode ID: 0x%02X",
                                cfg[2]);
                        iwe.u.data.length = strlen(buf);
-                       current_ev = iwe_stream_add_point(info, current_ev,
-                                                         end_buf,
-                                                         &iwe, buf);
+                       current_ev = iwe_stream_add_point_check(info,
+                                                               current_ev,
+                                                               end_buf,
+                                                               &iwe, buf);
+                       if (IS_ERR(current_ev))
+                               goto unlock;
                        sprintf(buf, "Synchronization ID: 0x%02X", cfg[3]);
                        iwe.u.data.length = strlen(buf);
-                       current_ev = iwe_stream_add_point(info, current_ev,
-                                                         end_buf,
-                                                         &iwe, buf);
+                       current_ev = iwe_stream_add_point_check(info,
+                                                               current_ev,
+                                                               end_buf,
+                                                               &iwe, buf);
+                       if (IS_ERR(current_ev))
+                               goto unlock;
                        sprintf(buf, "Authentication ID: 0x%02X", cfg[4]);
                        iwe.u.data.length = strlen(buf);
-                       current_ev = iwe_stream_add_point(info, current_ev,
-                                                         end_buf,
-                                                         &iwe, buf);
+                       current_ev = iwe_stream_add_point_check(info,
+                                                               current_ev,
+                                                               end_buf,
+                                                               &iwe, buf);
+                       if (IS_ERR(current_ev))
+                               goto unlock;
                        sprintf(buf, "Formation Info: 0x%02X", cfg[5]);
                        iwe.u.data.length = strlen(buf);
-                       current_ev = iwe_stream_add_point(info, current_ev,
-                                                         end_buf,
-                                                         &iwe, buf);
+                       current_ev = iwe_stream_add_point_check(info,
+                                                               current_ev,
+                                                               end_buf,
+                                                               &iwe, buf);
+                       if (IS_ERR(current_ev))
+                               goto unlock;
                        sprintf(buf, "Capabilities: 0x%02X", cfg[6]);
                        iwe.u.data.length = strlen(buf);
-                       current_ev = iwe_stream_add_point(info, current_ev,
-                                                         end_buf,
-                                                         &iwe, buf);
-                       kfree(buf);
+                       current_ev = iwe_stream_add_point_check(info,
+                                                               current_ev,
+                                                               end_buf,
+                                                               &iwe, buf);
+                       if (IS_ERR(current_ev))
+                               goto unlock;
                        break;
                case WLAN_EID_SUPP_RATES:
                case WLAN_EID_EXT_SUPP_RATES:
@@ -1445,8 +1557,14 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
                        for (i = 0; i < ie[1]; i++) {
                                iwe.u.bitrate.value =
                                        ((ie[i + 2] & 0x7f) * 500000);
+                               tmp = p;
                                p = iwe_stream_add_value(info, current_ev, p,
-                                               end_buf, &iwe, IW_EV_PARAM_LEN);
+                                                        end_buf, &iwe,
+                                                        IW_EV_PARAM_LEN);
+                               if (p == tmp) {
+                                       current_ev = ERR_PTR(-E2BIG);
+                                       goto unlock;
+                               }
                        }
                        current_ev = p;
                        break;
@@ -1465,31 +1583,35 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
                        iwe.u.mode = IW_MODE_MASTER;
                else
                        iwe.u.mode = IW_MODE_ADHOC;
-               current_ev = iwe_stream_add_event(info, current_ev, end_buf,
-                                                 &iwe, IW_EV_UINT_LEN);
-       }
-
-       buf = kmalloc(31, GFP_ATOMIC);
-       if (buf) {
-               memset(&iwe, 0, sizeof(iwe));
-               iwe.cmd = IWEVCUSTOM;
-               sprintf(buf, "tsf=%016llx", (unsigned long long)(ies->tsf));
-               iwe.u.data.length = strlen(buf);
-               current_ev = iwe_stream_add_point(info, current_ev, end_buf,
-                                                 &iwe, buf);
-               memset(&iwe, 0, sizeof(iwe));
-               iwe.cmd = IWEVCUSTOM;
-               sprintf(buf, " Last beacon: %ums ago",
-                       elapsed_jiffies_msecs(bss->ts));
-               iwe.u.data.length = strlen(buf);
-               current_ev = iwe_stream_add_point(info, current_ev,
-                                                 end_buf, &iwe, buf);
-               kfree(buf);
+               current_ev = iwe_stream_add_event_check(info, current_ev,
+                                                       end_buf, &iwe,
+                                                       IW_EV_UINT_LEN);
+               if (IS_ERR(current_ev))
+                       goto unlock;
        }
 
-       ieee80211_scan_add_ies(info, ies, &current_ev, end_buf);
+       memset(&iwe, 0, sizeof(iwe));
+       iwe.cmd = IWEVCUSTOM;
+       sprintf(buf, "tsf=%016llx", (unsigned long long)(ies->tsf));
+       iwe.u.data.length = strlen(buf);
+       current_ev = iwe_stream_add_point_check(info, current_ev, end_buf,
+                                               &iwe, buf);
+       if (IS_ERR(current_ev))
+               goto unlock;
+       memset(&iwe, 0, sizeof(iwe));
+       iwe.cmd = IWEVCUSTOM;
+       sprintf(buf, " Last beacon: %ums ago",
+               elapsed_jiffies_msecs(bss->ts));
+       iwe.u.data.length = strlen(buf);
+       current_ev = iwe_stream_add_point_check(info, current_ev,
+                                               end_buf, &iwe, buf);
+       if (IS_ERR(current_ev))
+               goto unlock;
+
+       current_ev = ieee80211_scan_add_ies(info, ies, current_ev, end_buf);
+
+ unlock:
        rcu_read_unlock();
-
        return current_ev;
 }
 
@@ -1501,19 +1623,27 @@ static int ieee80211_scan_results(struct cfg80211_registered_device *rdev,
        char *current_ev = buf;
        char *end_buf = buf + len;
        struct cfg80211_internal_bss *bss;
+       int err = 0;
 
        spin_lock_bh(&rdev->bss_lock);
        cfg80211_bss_expire(rdev);
 
        list_for_each_entry(bss, &rdev->bss_list, list) {
                if (buf + len - current_ev <= IW_EV_ADDR_LEN) {
-                       spin_unlock_bh(&rdev->bss_lock);
-                       return -E2BIG;
+                       err = -E2BIG;
+                       break;
                }
                current_ev = ieee80211_bss(&rdev->wiphy, info, bss,
                                           current_ev, end_buf);
+               if (IS_ERR(current_ev)) {
+                       err = PTR_ERR(current_ev);
+                       break;
+               }
        }
        spin_unlock_bh(&rdev->bss_lock);
+
+       if (err)
+               return err;
        return current_ev - buf;
 }
 
@@ -1545,5 +1675,5 @@ int cfg80211_wext_giwscan(struct net_device *dev,
 
        return res;
 }
-EXPORT_SYMBOL_GPL(cfg80211_wext_giwscan);
+EXPORT_WEXT_HANDLER(cfg80211_wext_giwscan);
 #endif