OSDN Git Service

Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[uclinux-h8/linux.git] / net / wireless / util.c
index 8edce22..88725f8 100644 (file)
@@ -13,6 +13,7 @@
 #include <net/dsfield.h>
 #include <linux/if_vlan.h>
 #include <linux/mpls.h>
+#include <linux/gcd.h>
 #include "core.h"
 #include "rdev-ops.h"
 
@@ -420,8 +421,8 @@ unsigned int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
 }
 EXPORT_SYMBOL(ieee80211_get_mesh_hdrlen);
 
-static int __ieee80211_data_to_8023(struct sk_buff *skb, struct ethhdr *ehdr,
-                                   const u8 *addr, enum nl80211_iftype iftype)
+int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr,
+                                 const u8 *addr, enum nl80211_iftype iftype)
 {
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
        struct {
@@ -525,13 +526,7 @@ static int __ieee80211_data_to_8023(struct sk_buff *skb, struct ethhdr *ehdr,
 
        return 0;
 }
-
-int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
-                          enum nl80211_iftype iftype)
-{
-       return __ieee80211_data_to_8023(skb, NULL, addr, iftype);
-}
-EXPORT_SYMBOL(ieee80211_data_to_8023);
+EXPORT_SYMBOL(ieee80211_data_to_8023_exthdr);
 
 int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr,
                             enum nl80211_iftype iftype,
@@ -746,24 +741,18 @@ __ieee80211_amsdu_copy(struct sk_buff *skb, unsigned int hlen,
 void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
                              const u8 *addr, enum nl80211_iftype iftype,
                              const unsigned int extra_headroom,
-                             bool has_80211_header)
+                             const u8 *check_da, const u8 *check_sa)
 {
        unsigned int hlen = ALIGN(extra_headroom, 4);
        struct sk_buff *frame = NULL;
        u16 ethertype;
        u8 *payload;
-       int offset = 0, remaining, err;
+       int offset = 0, remaining;
        struct ethhdr eth;
        bool reuse_frag = skb->head_frag && !skb_has_frag_list(skb);
        bool reuse_skb = false;
        bool last = false;
 
-       if (has_80211_header) {
-               err = __ieee80211_data_to_8023(skb, &eth, addr, iftype);
-               if (err)
-                       goto out;
-       }
-
        while (!last) {
                unsigned int subframe_len;
                int len;
@@ -780,8 +769,17 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
                        goto purge;
 
                offset += sizeof(struct ethhdr);
-               /* reuse skb for the last subframe */
                last = remaining <= subframe_len + padding;
+
+               /* FIXME: should we really accept multicast DA? */
+               if ((check_da && !is_multicast_ether_addr(eth.h_dest) &&
+                    !ether_addr_equal(check_da, eth.h_dest)) ||
+                   (check_sa && !ether_addr_equal(check_sa, eth.h_source))) {
+                       offset += len + padding;
+                       continue;
+               }
+
+               /* reuse skb for the last subframe */
                if (!skb_is_nonlinear(skb) && !reuse_frag && last) {
                        skb_pull(skb, offset);
                        frame = skb;
@@ -819,7 +817,6 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
 
  purge:
        __skb_queue_purge(list);
- out:
        dev_kfree_skb(skb);
 }
 EXPORT_SYMBOL(ieee80211_amsdu_to_8023s);
@@ -1381,6 +1378,25 @@ static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id)
        return false;
 }
 
+static size_t skip_ie(const u8 *ies, size_t ielen, size_t pos)
+{
+       /* we assume a validly formed IEs buffer */
+       u8 len = ies[pos + 1];
+
+       pos += 2 + len;
+
+       /* the IE itself must have 255 bytes for fragments to follow */
+       if (len < 255)
+               return pos;
+
+       while (pos < ielen && ies[pos] == WLAN_EID_FRAGMENT) {
+               len = ies[pos + 1];
+               pos += 2 + len;
+       }
+
+       return pos;
+}
+
 size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
                              const u8 *ids, int n_ids,
                              const u8 *after_ric, int n_after_ric,
@@ -1390,14 +1406,14 @@ size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
 
        while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) {
                if (ies[pos] == WLAN_EID_RIC_DATA && n_after_ric) {
-                       pos += 2 + ies[pos + 1];
+                       pos = skip_ie(ies, ielen, pos);
 
                        while (pos < ielen &&
                               !ieee80211_id_in_list(after_ric, n_after_ric,
                                                     ies[pos]))
-                               pos += 2 + ies[pos + 1];
+                               pos = skip_ie(ies, ielen, pos);
                } else {
-                       pos += 2 + ies[pos + 1];
+                       pos = skip_ie(ies, ielen, pos);
                }
        }
 
@@ -1558,31 +1574,57 @@ bool ieee80211_chandef_to_operating_class(struct cfg80211_chan_def *chandef,
 }
 EXPORT_SYMBOL(ieee80211_chandef_to_operating_class);
 
-int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
-                                u32 beacon_int)
+static void cfg80211_calculate_bi_data(struct wiphy *wiphy, u32 new_beacon_int,
+                                      u32 *beacon_int_gcd,
+                                      bool *beacon_int_different)
 {
        struct wireless_dev *wdev;
-       int res = 0;
 
-       if (beacon_int < 10 || beacon_int > 10000)
-               return -EINVAL;
+       *beacon_int_gcd = 0;
+       *beacon_int_different = false;
 
-       list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+       list_for_each_entry(wdev, &wiphy->wdev_list, list) {
                if (!wdev->beacon_interval)
                        continue;
-               if (wdev->beacon_interval != beacon_int) {
-                       res = -EINVAL;
-                       break;
+
+               if (!*beacon_int_gcd) {
+                       *beacon_int_gcd = wdev->beacon_interval;
+                       continue;
                }
+
+               if (wdev->beacon_interval == *beacon_int_gcd)
+                       continue;
+
+               *beacon_int_different = true;
+               *beacon_int_gcd = gcd(*beacon_int_gcd, wdev->beacon_interval);
        }
 
-       return res;
+       if (new_beacon_int && *beacon_int_gcd != new_beacon_int) {
+               if (*beacon_int_gcd)
+                       *beacon_int_different = true;
+               *beacon_int_gcd = gcd(*beacon_int_gcd, new_beacon_int);
+       }
+}
+
+int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
+                                enum nl80211_iftype iftype, u32 beacon_int)
+{
+       /*
+        * This is just a basic pre-condition check; if interface combinations
+        * are possible the driver must already be checking those with a call
+        * to cfg80211_check_combinations(), in which case we'll validate more
+        * through the cfg80211_calculate_bi_data() call and code in
+        * cfg80211_iter_combinations().
+        */
+
+       if (beacon_int < 10 || beacon_int > 10000)
+               return -EINVAL;
+
+       return 0;
 }
 
 int cfg80211_iter_combinations(struct wiphy *wiphy,
-                              const int num_different_channels,
-                              const u8 radar_detect,
-                              const int iftype_num[NUM_NL80211_IFTYPES],
+                              struct iface_combination_params *params,
                               void (*iter)(const struct ieee80211_iface_combination *c,
                                            void *data),
                               void *data)
@@ -1592,8 +1634,23 @@ int cfg80211_iter_combinations(struct wiphy *wiphy,
        int i, j, iftype;
        int num_interfaces = 0;
        u32 used_iftypes = 0;
+       u32 beacon_int_gcd;
+       bool beacon_int_different;
 
-       if (radar_detect) {
+       /*
+        * This is a bit strange, since the iteration used to rely only on
+        * the data given by the driver, but here it now relies on context,
+        * in form of the currently operating interfaces.
+        * This is OK for all current users, and saves us from having to
+        * push the GCD calculations into all the drivers.
+        * In the future, this should probably rely more on data that's in
+        * cfg80211 already - the only thing not would appear to be any new
+        * interfaces (while being brought up) and channel/radar data.
+        */
+       cfg80211_calculate_bi_data(wiphy, params->new_beacon_int,
+                                  &beacon_int_gcd, &beacon_int_different);
+
+       if (params->radar_detect) {
                rcu_read_lock();
                regdom = rcu_dereference(cfg80211_regdomain);
                if (regdom)
@@ -1602,8 +1659,8 @@ int cfg80211_iter_combinations(struct wiphy *wiphy,
        }
 
        for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) {
-               num_interfaces += iftype_num[iftype];
-               if (iftype_num[iftype] > 0 &&
+               num_interfaces += params->iftype_num[iftype];
+               if (params->iftype_num[iftype] > 0 &&
                    !(wiphy->software_iftypes & BIT(iftype)))
                        used_iftypes |= BIT(iftype);
        }
@@ -1617,7 +1674,7 @@ int cfg80211_iter_combinations(struct wiphy *wiphy,
 
                if (num_interfaces > c->max_interfaces)
                        continue;
-               if (num_different_channels > c->num_different_channels)
+               if (params->num_different_channels > c->num_different_channels)
                        continue;
 
                limits = kmemdup(c->limits, sizeof(limits[0]) * c->n_limits,
@@ -1632,16 +1689,17 @@ int cfg80211_iter_combinations(struct wiphy *wiphy,
                                all_iftypes |= limits[j].types;
                                if (!(limits[j].types & BIT(iftype)))
                                        continue;
-                               if (limits[j].max < iftype_num[iftype])
+                               if (limits[j].max < params->iftype_num[iftype])
                                        goto cont;
-                               limits[j].max -= iftype_num[iftype];
+                               limits[j].max -= params->iftype_num[iftype];
                        }
                }
 
-               if (radar_detect != (c->radar_detect_widths & radar_detect))
+               if (params->radar_detect !=
+                       (c->radar_detect_widths & params->radar_detect))
                        goto cont;
 
-               if (radar_detect && c->radar_detect_regions &&
+               if (params->radar_detect && c->radar_detect_regions &&
                    !(c->radar_detect_regions & BIT(region)))
                        goto cont;
 
@@ -1653,6 +1711,14 @@ int cfg80211_iter_combinations(struct wiphy *wiphy,
                if ((all_iftypes & used_iftypes) != used_iftypes)
                        goto cont;
 
+               if (beacon_int_gcd) {
+                       if (c->beacon_int_min_gcd &&
+                           beacon_int_gcd < c->beacon_int_min_gcd)
+                               goto cont;
+                       if (!c->beacon_int_min_gcd && beacon_int_different)
+                               goto cont;
+               }
+
                /* This combination covered all interface types and
                 * supported the requested numbers, so we're good.
                 */
@@ -1675,14 +1741,11 @@ cfg80211_iter_sum_ifcombs(const struct ieee80211_iface_combination *c,
 }
 
 int cfg80211_check_combinations(struct wiphy *wiphy,
-                               const int num_different_channels,
-                               const u8 radar_detect,
-                               const int iftype_num[NUM_NL80211_IFTYPES])
+                               struct iface_combination_params *params)
 {
        int err, num = 0;
 
-       err = cfg80211_iter_combinations(wiphy, num_different_channels,
-                                        radar_detect, iftype_num,
+       err = cfg80211_iter_combinations(wiphy, params,
                                         cfg80211_iter_sum_ifcombs, &num);
        if (err)
                return err;