cfg80211: off by one in nl80211_trigger_scan()
[firefly-linux-kernel-4.4.55.git] / net / wireless / core.c
index bea0d80710c8cd0fcd6a7948a3d46fca8f9b741c..645437cfc464d505a3673870f8c0a6eb73287535 100644 (file)
@@ -366,11 +366,12 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
 
        mutex_init(&rdev->mtx);
        mutex_init(&rdev->devlist_mtx);
+       mutex_init(&rdev->sched_scan_mtx);
        INIT_LIST_HEAD(&rdev->netdev_list);
        spin_lock_init(&rdev->bss_lock);
        INIT_LIST_HEAD(&rdev->bss_list);
        INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done);
-
+       INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results);
 #ifdef CONFIG_CFG80211_WEXT
        rdev->wiphy.wext = &cfg80211_wext_handler;
 #endif
@@ -416,6 +417,67 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
 }
 EXPORT_SYMBOL(wiphy_new);
 
+static int wiphy_verify_combinations(struct wiphy *wiphy)
+{
+       const struct ieee80211_iface_combination *c;
+       int i, j;
+
+       /* If we have combinations enforce them */
+       if (wiphy->n_iface_combinations)
+               wiphy->flags |= WIPHY_FLAG_ENFORCE_COMBINATIONS;
+
+       for (i = 0; i < wiphy->n_iface_combinations; i++) {
+               u32 cnt = 0;
+               u16 all_iftypes = 0;
+
+               c = &wiphy->iface_combinations[i];
+
+               /* Combinations with just one interface aren't real */
+               if (WARN_ON(c->max_interfaces < 2))
+                       return -EINVAL;
+
+               /* Need at least one channel */
+               if (WARN_ON(!c->num_different_channels))
+                       return -EINVAL;
+
+               if (WARN_ON(!c->n_limits))
+                       return -EINVAL;
+
+               for (j = 0; j < c->n_limits; j++) {
+                       u16 types = c->limits[j].types;
+
+                       /*
+                        * interface types shouldn't overlap, this is
+                        * used in cfg80211_can_change_interface()
+                        */
+                       if (WARN_ON(types & all_iftypes))
+                               return -EINVAL;
+                       all_iftypes |= types;
+
+                       if (WARN_ON(!c->limits[j].max))
+                               return -EINVAL;
+
+                       /* Shouldn't list software iftypes in combinations! */
+                       if (WARN_ON(wiphy->software_iftypes & types))
+                               return -EINVAL;
+
+                       cnt += c->limits[j].max;
+                       /*
+                        * Don't advertise an unsupported type
+                        * in a combination.
+                        */
+                       if (WARN_ON((wiphy->interface_modes & types) != types))
+                               return -EINVAL;
+               }
+
+               /* You can't even choose that many! */
+               if (WARN_ON(cnt < c->max_interfaces))
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
 int wiphy_register(struct wiphy *wiphy)
 {
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
@@ -426,6 +488,10 @@ int wiphy_register(struct wiphy *wiphy)
        int i;
        u16 ifmodes = wiphy->interface_modes;
 
+       if (WARN_ON((wiphy->wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
+                   !(wiphy->wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY)))
+               return -EINVAL;
+
        if (WARN_ON(wiphy->addresses && !wiphy->n_addresses))
                return -EINVAL;
 
@@ -444,6 +510,10 @@ int wiphy_register(struct wiphy *wiphy)
        if (WARN_ON(ifmodes != wiphy->interface_modes))
                wiphy->interface_modes = ifmodes;
 
+       res = wiphy_verify_combinations(wiphy);
+       if (res)
+               return res;
+
        /* sanity check supported bands/channels */
        for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
                sband = wiphy->bands[band];
@@ -636,6 +706,7 @@ void cfg80211_dev_free(struct cfg80211_registered_device *rdev)
        rfkill_destroy(rdev->rfkill);
        mutex_destroy(&rdev->mtx);
        mutex_destroy(&rdev->devlist_mtx);
+       mutex_destroy(&rdev->sched_scan_mtx);
        list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list)
                cfg80211_put_bss(&scan->pub);
        cfg80211_rdev_free_wowlan(rdev);
@@ -674,6 +745,15 @@ static void wdev_cleanup_work(struct work_struct *work)
 
        cfg80211_unlock_rdev(rdev);
 
+       mutex_lock(&rdev->sched_scan_mtx);
+
+       if (WARN_ON(rdev->sched_scan_req &&
+                   rdev->sched_scan_req->dev == wdev->netdev)) {
+               __cfg80211_stop_sched_scan(rdev, false);
+       }
+
+       mutex_unlock(&rdev->sched_scan_mtx);
+
        mutex_lock(&rdev->devlist_mtx);
        rdev->opencount--;
        mutex_unlock(&rdev->devlist_mtx);
@@ -693,6 +773,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
        struct net_device *dev = ndev;
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct cfg80211_registered_device *rdev;
+       int ret;
 
        if (!wdev)
                return NOTIFY_DONE;
@@ -759,6 +840,10 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
                        break;
                case NL80211_IFTYPE_P2P_CLIENT:
                case NL80211_IFTYPE_STATION:
+                       mutex_lock(&rdev->sched_scan_mtx);
+                       __cfg80211_stop_sched_scan(rdev, false);
+                       mutex_unlock(&rdev->sched_scan_mtx);
+
                        wdev_lock(wdev);
 #ifdef CONFIG_CFG80211_WEXT
                        kfree(wdev->wext.ie);
@@ -777,6 +862,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
                default:
                        break;
                }
+               wdev->beacon_interval = 0;
                break;
        case NETDEV_DOWN:
                dev_hold(dev);
@@ -836,7 +922,8 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
                 * Configure power management to the driver here so that its
                 * correctly set also after interface type changes etc.
                 */
-               if (wdev->iftype == NL80211_IFTYPE_STATION &&
+               if ((wdev->iftype == NL80211_IFTYPE_STATION ||
+                    wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) &&
                    rdev->ops->set_power_mgmt)
                        if (rdev->ops->set_power_mgmt(wdev->wiphy, dev,
                                                      wdev->ps,
@@ -883,6 +970,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
                        return notifier_from_errno(-EOPNOTSUPP);
                if (rfkill_blocked(rdev->rfkill))
                        return notifier_from_errno(-ERFKILL);
+               ret = cfg80211_can_add_interface(rdev, wdev->iftype);
+               if (ret)
+                       return notifier_from_errno(ret);
                break;
        }