]> Pileus Git - ~andy/linux/blobdiff - net/mac80211/rx.c
Merge tag 'fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm...
[~andy/linux] / net / mac80211 / rx.c
index 2b0debb0422b91d89a0a7982b94726f3b9ceae11..3e57f96c9666daf4b420cfd663864878bef34204 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]) {
@@ -1115,6 +1128,13 @@ static void sta_ps_end(struct sta_info *sta)
               sta->sta.addr, sta->sta.aid);
 
        if (test_sta_flag(sta, WLAN_STA_PS_DRIVER)) {
+               /*
+                * Clear the flag only if the other one is still set
+                * so that the TX path won't start TX'ing new frames
+                * directly ... In the case that the driver flag isn't
+                * set ieee80211_sta_ps_deliver_wakeup() will clear it.
+                */
+               clear_sta_flag(sta, WLAN_STA_PS_STA);
                ps_dbg(sta->sdata, "STA %pM aid %d driver-ps-blocked\n",
                       sta->sta.addr, sta->sta.aid);
                return;
@@ -1369,6 +1389,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
@@ -1406,11 +1427,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);
@@ -1472,6 +1501,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
@@ -1487,15 +1517,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)
@@ -1543,11 +1579,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 */
@@ -1938,20 +1970,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);
@@ -1964,14 +1993,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) {
@@ -2057,7 +2086,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;
@@ -2165,7 +2193,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;
@@ -3053,8 +3083,8 @@ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)
 
 /* main receive path */
 
-static int prepare_for_handlers(struct ieee80211_rx_data *rx,
-                               struct ieee80211_hdr *hdr)
+static bool prepare_for_handlers(struct ieee80211_rx_data *rx,
+                                struct ieee80211_hdr *hdr)
 {
        struct ieee80211_sub_if_data *sdata = rx->sdata;
        struct sk_buff *skb = rx->skb;
@@ -3065,29 +3095,29 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_STATION:
                if (!bssid && !sdata->u.mgd.use_4addr)
-                       return 0;
+                       return false;
                if (!multicast &&
                    !ether_addr_equal(sdata->vif.addr, hdr->addr1)) {
                        if (!(sdata->dev->flags & IFF_PROMISC) ||
                            sdata->u.mgd.use_4addr)
-                               return 0;
+                               return false;
                        status->rx_flags &= ~IEEE80211_RX_RA_MATCH;
                }
                break;
        case NL80211_IFTYPE_ADHOC:
                if (!bssid)
-                       return 0;
+                       return false;
                if (ether_addr_equal(sdata->vif.addr, hdr->addr2) ||
                    ether_addr_equal(sdata->u.ibss.bssid, hdr->addr2))
-                       return 0;
+                       return false;
                if (ieee80211_is_beacon(hdr->frame_control)) {
-                       return 1;
+                       return true;
                } else if (!ieee80211_bssid_match(bssid, sdata->u.ibss.bssid)) {
-                       return 0;
+                       return false;
                } else if (!multicast &&
                           !ether_addr_equal(sdata->vif.addr, hdr->addr1)) {
                        if (!(sdata->dev->flags & IFF_PROMISC))
-                               return 0;
+                               return false;
                        status->rx_flags &= ~IEEE80211_RX_RA_MATCH;
                } else if (!rx->sta) {
                        int rate_idx;
@@ -3103,7 +3133,7 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,
                if (!multicast &&
                    !ether_addr_equal(sdata->vif.addr, hdr->addr1)) {
                        if (!(sdata->dev->flags & IFF_PROMISC))
-                               return 0;
+                               return false;
 
                        status->rx_flags &= ~IEEE80211_RX_RA_MATCH;
                }
@@ -3112,7 +3142,7 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,
        case NL80211_IFTYPE_AP:
                if (!bssid) {
                        if (!ether_addr_equal(sdata->vif.addr, hdr->addr1))
-                               return 0;
+                               return false;
                } else if (!ieee80211_bssid_match(bssid, sdata->vif.addr)) {
                        /*
                         * Accept public action frames even when the
@@ -3122,26 +3152,26 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,
                         */
                        if (!multicast &&
                            !ether_addr_equal(sdata->vif.addr, hdr->addr1))
-                               return 0;
+                               return false;
                        if (ieee80211_is_public_action(hdr, skb->len))
-                               return 1;
+                               return true;
                        if (!ieee80211_is_beacon(hdr->frame_control))
-                               return 0;
+                               return false;
                        status->rx_flags &= ~IEEE80211_RX_RA_MATCH;
                }
                break;
        case NL80211_IFTYPE_WDS:
                if (bssid || !ieee80211_is_data(hdr->frame_control))
-                       return 0;
+                       return false;
                if (!ether_addr_equal(sdata->u.wds.remote_addr, hdr->addr2))
-                       return 0;
+                       return false;
                break;
        case NL80211_IFTYPE_P2P_DEVICE:
                if (!ieee80211_is_public_action(hdr, skb->len) &&
                    !ieee80211_is_probe_req(hdr->frame_control) &&
                    !ieee80211_is_probe_resp(hdr->frame_control) &&
                    !ieee80211_is_beacon(hdr->frame_control))
-                       return 0;
+                       return false;
                if (!ether_addr_equal(sdata->vif.addr, hdr->addr1) &&
                    !multicast)
                        status->rx_flags &= ~IEEE80211_RX_RA_MATCH;
@@ -3152,7 +3182,7 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,
                break;
        }
 
-       return 1;
+       return true;
 }
 
 /*
@@ -3168,13 +3198,11 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
        struct ieee80211_sub_if_data *sdata = rx->sdata;
        struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
        struct ieee80211_hdr *hdr = (void *)skb->data;
-       int prepares;
 
        rx->skb = skb;
        status->rx_flags |= IEEE80211_RX_RA_MATCH;
-       prepares = prepare_for_handlers(rx, hdr);
 
-       if (!prepares)
+       if (!prepare_for_handlers(rx, hdr))
                return false;
 
        if (!consume) {