Merge remote-tracking branch 'wireless-next/master' into mac80211-next
[firefly-linux-kernel-4.4.55.git] / net / mac80211 / rx.c
index 674eac1f996c9ddd664f2f70022514efc228b109..5a2afe9583a806d5f9059c390e0cc70ae7799e22 100644 (file)
@@ -638,6 +638,27 @@ static int ieee80211_get_mmie_keyidx(struct sk_buff *skb)
        return le16_to_cpu(mmie->key_id);
 }
 
+static int iwl80211_get_cs_keyid(const struct ieee80211_cipher_scheme *cs,
+                                struct sk_buff *skb)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       __le16 fc;
+       int hdrlen;
+       u8 keyid;
+
+       fc = hdr->frame_control;
+       hdrlen = ieee80211_hdrlen(fc);
+
+       if (skb->len < hdrlen + cs->hdr_len)
+               return -EINVAL;
+
+       skb_copy_bits(skb, hdrlen + cs->key_idx_off, &keyid, 1);
+       keyid &= cs->key_idx_mask;
+       keyid >>= cs->key_idx_shift;
+
+       return keyid;
+}
+
 static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
 {
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
@@ -729,9 +750,7 @@ static void ieee80211_release_reorder_frames(struct ieee80211_sub_if_data *sdata
        lockdep_assert_held(&tid_agg_rx->reorder_lock);
 
        while (ieee80211_sn_less(tid_agg_rx->head_seq_num, head_seq_num)) {
-               index = ieee80211_sn_sub(tid_agg_rx->head_seq_num,
-                                        tid_agg_rx->ssn) %
-                                                       tid_agg_rx->buf_size;
+               index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;
                ieee80211_release_reorder_frame(sdata, tid_agg_rx, index,
                                                frames);
        }
@@ -757,8 +776,7 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
        lockdep_assert_held(&tid_agg_rx->reorder_lock);
 
        /* release the buffer until next missing frame */
-       index = ieee80211_sn_sub(tid_agg_rx->head_seq_num,
-                                tid_agg_rx->ssn) % tid_agg_rx->buf_size;
+       index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;
        if (!tid_agg_rx->reorder_buf[index] &&
            tid_agg_rx->stored_mpdu_num) {
                /*
@@ -793,15 +811,11 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
        } else while (tid_agg_rx->reorder_buf[index]) {
                ieee80211_release_reorder_frame(sdata, tid_agg_rx, index,
                                                frames);
-               index = ieee80211_sn_sub(tid_agg_rx->head_seq_num,
-                                        tid_agg_rx->ssn) %
-                                                       tid_agg_rx->buf_size;
+               index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;
        }
 
        if (tid_agg_rx->stored_mpdu_num) {
-               j = index = ieee80211_sn_sub(tid_agg_rx->head_seq_num,
-                                            tid_agg_rx->ssn) %
-                                                       tid_agg_rx->buf_size;
+               j = index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;
 
                for (; j != (index - 1) % tid_agg_rx->buf_size;
                     j = (j + 1) % tid_agg_rx->buf_size) {
@@ -861,8 +875,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
 
        /* Now the new frame is always in the range of the reordering buffer */
 
-       index = ieee80211_sn_sub(mpdu_seq_num,
-                                tid_agg_rx->ssn) % tid_agg_rx->buf_size;
+       index = mpdu_seq_num % tid_agg_rx->buf_size;
 
        /* check if we already stored this frame */
        if (tid_agg_rx->reorder_buf[index]) {
@@ -911,7 +924,8 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,
        u16 sc;
        u8 tid, ack_policy;
 
-       if (!ieee80211_is_data_qos(hdr->frame_control))
+       if (!ieee80211_is_data_qos(hdr->frame_control) ||
+           is_multicast_ether_addr(hdr->addr1))
                goto dont_reorder;
 
        /*
@@ -995,8 +1009,9 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
                                rx->sta->num_duplicates++;
                        }
                        return RX_DROP_UNUSABLE;
-               } else
+               } else if (!(status->flag & RX_FLAG_AMSDU_MORE)) {
                        rx->sta->last_seq_ctrl[rx->seqno_idx] = hdr->seq_ctrl;
+               }
        }
 
        if (unlikely(rx->skb->len < 16)) {
@@ -1367,6 +1382,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
        struct ieee80211_key *sta_ptk = NULL;
        int mmie_keyidx = -1;
        __le16 fc;
+       const struct ieee80211_cipher_scheme *cs = NULL;
 
        /*
         * Key selection 101
@@ -1404,11 +1420,19 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
 
        /* start without a key */
        rx->key = NULL;
+       fc = hdr->frame_control;
 
-       if (rx->sta)
-               sta_ptk = rcu_dereference(rx->sta->ptk);
+       if (rx->sta) {
+               int keyid = rx->sta->ptk_idx;
 
-       fc = hdr->frame_control;
+               if (ieee80211_has_protected(fc) && rx->sta->cipher_scheme) {
+                       cs = rx->sta->cipher_scheme;
+                       keyid = iwl80211_get_cs_keyid(cs, rx->skb);
+                       if (unlikely(keyid < 0))
+                               return RX_DROP_UNUSABLE;
+               }
+               sta_ptk = rcu_dereference(rx->sta->ptk[keyid]);
+       }
 
        if (!ieee80211_has_protected(fc))
                mmie_keyidx = ieee80211_get_mmie_keyidx(rx->skb);
@@ -1470,6 +1494,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
                return RX_CONTINUE;
        } else {
                u8 keyid;
+
                /*
                 * The device doesn't give us the IV so we won't be
                 * able to look up the key. That's ok though, we
@@ -1485,15 +1510,21 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
 
                hdrlen = ieee80211_hdrlen(fc);
 
-               if (rx->skb->len < 8 + hdrlen)
-                       return RX_DROP_UNUSABLE; /* TODO: count this? */
+               if (cs) {
+                       keyidx = iwl80211_get_cs_keyid(cs, rx->skb);
 
-               /*
-                * no need to call ieee80211_wep_get_keyidx,
-                * it verifies a bunch of things we've done already
-                */
-               skb_copy_bits(rx->skb, hdrlen + 3, &keyid, 1);
-               keyidx = keyid >> 6;
+                       if (unlikely(keyidx < 0))
+                               return RX_DROP_UNUSABLE;
+               } else {
+                       if (rx->skb->len < 8 + hdrlen)
+                               return RX_DROP_UNUSABLE; /* TODO: count this? */
+                       /*
+                        * no need to call ieee80211_wep_get_keyidx,
+                        * it verifies a bunch of things we've done already
+                        */
+                       skb_copy_bits(rx->skb, hdrlen + 3, &keyid, 1);
+                       keyidx = keyid >> 6;
+               }
 
                /* check per-station GTK first, if multicast packet */
                if (is_multicast_ether_addr(hdr->addr1) && rx->sta)
@@ -1541,11 +1572,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
                result = ieee80211_crypto_aes_cmac_decrypt(rx);
                break;
        default:
-               /*
-                * We can reach here only with HW-only algorithms
-                * but why didn't it decrypt the frame?!
-                */
-               return RX_DROP_UNUSABLE;
+               result = ieee80211_crypto_hw_decrypt(rx);
        }
 
        /* the hdr variable is invalid after the decrypt handlers */
@@ -1936,20 +1963,17 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
                }
        }
 
-       if (skb) {
-               int align __maybe_unused;
-
 #ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
-               /*
-                * 'align' will only take the values 0 or 2 here
-                * since all frames are required to be aligned
-                * to 2-byte boundaries when being passed to
-                * mac80211; the code here works just as well if
-                * that isn't true, but mac80211 assumes it can
-                * access fields as 2-byte aligned (e.g. for
-                * compare_ether_addr)
+       if (skb) {
+               /* 'align' will only take the values 0 or 2 here since all
+                * frames are required to be aligned to 2-byte boundaries
+                * when being passed to mac80211; the code here works just
+                * as well if that isn't true, but mac80211 assumes it can
+                * access fields as 2-byte aligned (e.g. for ether_addr_equal)
                 */
-               align = ((unsigned long)(skb->data + sizeof(struct ethhdr))) & 3;
+               int align;
+
+               align = (unsigned long)(skb->data + sizeof(struct ethhdr)) & 3;
                if (align) {
                        if (WARN_ON(skb_headroom(skb) < 3)) {
                                dev_kfree_skb(skb);
@@ -1962,14 +1986,14 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
                                skb_set_tail_pointer(skb, len);
                        }
                }
+       }
 #endif
 
-               if (skb) {
-                       /* deliver to local stack */
-                       skb->protocol = eth_type_trans(skb, dev);
-                       memset(skb->cb, 0, sizeof(skb->cb));
-                       netif_receive_skb(skb);
-               }
+       if (skb) {
+               /* deliver to local stack */
+               skb->protocol = eth_type_trans(skb, dev);
+               memset(skb->cb, 0, sizeof(skb->cb));
+               netif_receive_skb(skb);
        }
 
        if (xmit_skb) {
@@ -2055,7 +2079,6 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
        struct ieee80211_sub_if_data *sdata = rx->sdata;
        struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
-       __le16 reason = cpu_to_le16(WLAN_REASON_MESH_PATH_NOFORWARD);
        u16 q, hdrlen;
 
        hdr = (struct ieee80211_hdr *) skb->data;
@@ -2163,7 +2186,9 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
        } else {
                /* unable to resolve next hop */
                mesh_path_error_tx(sdata, ifmsh->mshcfg.element_ttl,
-                                  fwd_hdr->addr3, 0, reason, fwd_hdr->addr2);
+                                  fwd_hdr->addr3, 0,
+                                  WLAN_REASON_MESH_PATH_NOFORWARD,
+                                  fwd_hdr->addr2);
                IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_no_route);
                kfree_skb(fwd_skb);
                return RX_DROP_MONITOR;
@@ -2402,7 +2427,8 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
                return RX_DROP_UNUSABLE;
 
        if (!rx->sta && mgmt->u.action.category != WLAN_CATEGORY_PUBLIC &&
-           mgmt->u.action.category != WLAN_CATEGORY_SELF_PROTECTED)
+           mgmt->u.action.category != WLAN_CATEGORY_SELF_PROTECTED &&
+           mgmt->u.action.category != WLAN_CATEGORY_SPECTRUM_MGMT)
                return RX_DROP_UNUSABLE;
 
        if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))
@@ -2566,31 +2592,49 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
 
                goto queue;
        case WLAN_CATEGORY_SPECTRUM_MGMT:
-               if (status->band != IEEE80211_BAND_5GHZ)
-                       break;
-
-               if (sdata->vif.type != NL80211_IFTYPE_STATION)
-                       break;
-
                /* verify action_code is present */
                if (len < IEEE80211_MIN_ACTION_SIZE + 1)
                        break;
 
                switch (mgmt->u.action.u.measurement.action_code) {
                case WLAN_ACTION_SPCT_MSR_REQ:
+                       if (status->band != IEEE80211_BAND_5GHZ)
+                               break;
+
                        if (len < (IEEE80211_MIN_ACTION_SIZE +
                                   sizeof(mgmt->u.action.u.measurement)))
                                break;
+
+                       if (sdata->vif.type != NL80211_IFTYPE_STATION)
+                               break;
+
                        ieee80211_process_measurement_req(sdata, mgmt, len);
                        goto handled;
-               case WLAN_ACTION_SPCT_CHL_SWITCH:
-                       if (sdata->vif.type != NL80211_IFTYPE_STATION)
+               case WLAN_ACTION_SPCT_CHL_SWITCH: {
+                       u8 *bssid;
+                       if (len < (IEEE80211_MIN_ACTION_SIZE +
+                                  sizeof(mgmt->u.action.u.chan_switch)))
+                               break;
+
+                       if (sdata->vif.type != NL80211_IFTYPE_STATION &&
+                           sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+                           sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
                                break;
 
-                       if (!ether_addr_equal(mgmt->bssid, sdata->u.mgd.bssid))
+                       if (sdata->vif.type == NL80211_IFTYPE_STATION)
+                               bssid = sdata->u.mgd.bssid;
+                       else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+                               bssid = sdata->u.ibss.bssid;
+                       else if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
+                               bssid = mgmt->sa;
+                       else
+                               break;
+
+                       if (!ether_addr_equal(mgmt->bssid, bssid))
                                break;
 
                        goto queue;
+                       }
                }
                break;
        case WLAN_CATEGORY_SA_QUERY: