]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - net/mac80211/rx.c
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[mv-sheeva.git] / net / mac80211 / rx.c
index 6e2a7bcd8cb888ce24be03cc56cb2516ca95fade..fa0f37e4afe4901226b0ccd668ae6a88c136eeb2 100644 (file)
@@ -293,7 +293,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
                        skb2 = skb_clone(skb, GFP_ATOMIC);
                        if (skb2) {
                                skb2->dev = prev_dev;
-                               netif_rx(skb2);
+                               netif_receive_skb(skb2);
                        }
                }
 
@@ -304,7 +304,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
 
        if (prev_dev) {
                skb->dev = prev_dev;
-               netif_rx(skb);
+               netif_receive_skb(skb);
        } else
                dev_kfree_skb(skb);
 
@@ -719,16 +719,13 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,
 
        tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
 
-       spin_lock(&sta->lock);
-
-       if (!sta->ampdu_mlme.tid_active_rx[tid])
-               goto dont_reorder_unlock;
-
-       tid_agg_rx = sta->ampdu_mlme.tid_rx[tid];
+       tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]);
+       if (!tid_agg_rx)
+               goto dont_reorder;
 
        /* qos null data frames are excluded */
        if (unlikely(hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_NULLFUNC)))
-               goto dont_reorder_unlock;
+               goto dont_reorder;
 
        /* new, potentially un-ordered, ampdu frame - process it */
 
@@ -740,20 +737,22 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,
        /* if this mpdu is fragmented - terminate rx aggregation session */
        sc = le16_to_cpu(hdr->seq_ctrl);
        if (sc & IEEE80211_SCTL_FRAG) {
-               spin_unlock(&sta->lock);
-               __ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT,
-                                              WLAN_REASON_QSTA_REQUIRE_SETUP);
-               dev_kfree_skb(skb);
+               skb->pkt_type = IEEE80211_SDATA_QUEUE_TYPE_FRAME;
+               skb_queue_tail(&rx->sdata->skb_queue, skb);
+               ieee80211_queue_work(&local->hw, &rx->sdata->work);
                return;
        }
 
-       if (ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb, frames)) {
-               spin_unlock(&sta->lock);
+       /*
+        * No locking needed -- we will only ever process one
+        * RX packet at a time, and thus own tid_agg_rx. All
+        * other code manipulating it needs to (and does) make
+        * sure that we cannot get to it any more before doing
+        * anything with it.
+        */
+       if (ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb, frames))
                return;
-       }
 
- dont_reorder_unlock:
-       spin_unlock(&sta->lock);
  dont_reorder:
        __skb_queue_tail(frames, skb);
 }
@@ -825,6 +824,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
        ieee80211_rx_result result = RX_DROP_UNUSABLE;
        struct ieee80211_key *stakey = NULL;
        int mmie_keyidx = -1;
+       __le16 fc;
 
        /*
         * Key selection 101
@@ -866,13 +866,15 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
        if (rx->sta)
                stakey = rcu_dereference(rx->sta->key);
 
-       if (!ieee80211_has_protected(hdr->frame_control))
+       fc = hdr->frame_control;
+
+       if (!ieee80211_has_protected(fc))
                mmie_keyidx = ieee80211_get_mmie_keyidx(rx->skb);
 
        if (!is_multicast_ether_addr(hdr->addr1) && stakey) {
                rx->key = stakey;
                /* Skip decryption if the frame is not protected. */
-               if (!ieee80211_has_protected(hdr->frame_control))
+               if (!ieee80211_has_protected(fc))
                        return RX_CONTINUE;
        } else if (mmie_keyidx >= 0) {
                /* Broadcast/multicast robust management frame / BIP */
@@ -884,7 +886,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
                    mmie_keyidx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS)
                        return RX_DROP_MONITOR; /* unexpected BIP keyidx */
                rx->key = rcu_dereference(rx->sdata->keys[mmie_keyidx]);
-       } else if (!ieee80211_has_protected(hdr->frame_control)) {
+       } else if (!ieee80211_has_protected(fc)) {
                /*
                 * The frame was not protected, so skip decryption. However, we
                 * need to set rx->key if there is a key that could have been
@@ -892,7 +894,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
                 * have been expected.
                 */
                struct ieee80211_key *key = NULL;
-               if (ieee80211_is_mgmt(hdr->frame_control) &&
+               if (ieee80211_is_mgmt(fc) &&
                    is_multicast_ether_addr(hdr->addr1) &&
                    (key = rcu_dereference(rx->sdata->default_mgmt_key)))
                        rx->key = key;
@@ -914,7 +916,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
                    (status->flag & RX_FLAG_IV_STRIPPED))
                        return RX_CONTINUE;
 
-               hdrlen = ieee80211_hdrlen(hdr->frame_control);
+               hdrlen = ieee80211_hdrlen(fc);
 
                if (rx->skb->len < 8 + hdrlen)
                        return RX_DROP_UNUSABLE; /* TODO: count this? */
@@ -947,19 +949,17 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
 
        if (skb_linearize(rx->skb))
                return RX_DROP_UNUSABLE;
-
-       hdr = (struct ieee80211_hdr *)rx->skb->data;
-
-       /* Check for weak IVs if possible */
-       if (rx->sta && rx->key->conf.alg == ALG_WEP &&
-           ieee80211_is_data(hdr->frame_control) &&
-           (!(status->flag & RX_FLAG_IV_STRIPPED) ||
-            !(status->flag & RX_FLAG_DECRYPTED)) &&
-           ieee80211_wep_is_weak_iv(rx->skb, rx->key))
-               rx->sta->wep_weak_iv_count++;
+       /* the hdr variable is invalid now! */
 
        switch (rx->key->conf.alg) {
        case ALG_WEP:
+               /* Check for weak IVs if possible */
+               if (rx->sta && ieee80211_is_data(fc) &&
+                   (!(status->flag & RX_FLAG_IV_STRIPPED) ||
+                    !(status->flag & RX_FLAG_DECRYPTED)) &&
+                   ieee80211_wep_is_weak_iv(rx->skb, rx->key))
+                       rx->sta->wep_weak_iv_count++;
+
                result = ieee80211_crypto_wep_decrypt(rx);
                break;
        case ALG_TKIP:
@@ -1267,11 +1267,13 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
                                                 rx->queue, &(rx->skb));
                if (rx->key && rx->key->conf.alg == ALG_CCMP &&
                    ieee80211_has_protected(fc)) {
+                       int queue = ieee80211_is_mgmt(fc) ?
+                               NUM_RX_DATA_QUEUES : rx->queue;
                        /* Store CCMP PN so that we can verify that the next
                         * fragment has a sequential PN value. */
                        entry->ccmp = 1;
                        memcpy(entry->last_pn,
-                              rx->key->u.ccmp.rx_pn[rx->queue],
+                              rx->key->u.ccmp.rx_pn[queue],
                               CCMP_PN_LEN);
                }
                return RX_QUEUED;
@@ -1291,6 +1293,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
        if (entry->ccmp) {
                int i;
                u8 pn[CCMP_PN_LEN], *rpn;
+               int queue;
                if (!rx->key || rx->key->conf.alg != ALG_CCMP)
                        return RX_DROP_UNUSABLE;
                memcpy(pn, entry->last_pn, CCMP_PN_LEN);
@@ -1299,7 +1302,9 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
                        if (pn[i])
                                break;
                }
-               rpn = rx->key->u.ccmp.rx_pn[rx->queue];
+               queue = ieee80211_is_mgmt(fc) ?
+                       NUM_RX_DATA_QUEUES : rx->queue;
+               rpn = rx->key->u.ccmp.rx_pn[queue];
                if (memcmp(pn, rpn, CCMP_PN_LEN))
                        return RX_DROP_UNUSABLE;
                memcpy(entry->last_pn, pn, CCMP_PN_LEN);
@@ -1573,7 +1578,7 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
                        /* deliver to local stack */
                        skb->protocol = eth_type_trans(skb, dev);
                        memset(skb->cb, 0, sizeof(skb->cb));
-                       netif_rx(skb);
+                       netif_receive_skb(skb);
                }
        }
 
@@ -1818,17 +1823,24 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx, struct sk_buff_head *frames)
                return RX_CONTINUE;
 
        if (ieee80211_is_back_req(bar->frame_control)) {
+               struct {
+                       __le16 control, start_seq_num;
+               } __packed bar_data;
+
                if (!rx->sta)
                        return RX_DROP_MONITOR;
-               spin_lock(&rx->sta->lock);
-               tid = le16_to_cpu(bar->control) >> 12;
-               if (!rx->sta->ampdu_mlme.tid_active_rx[tid]) {
-                       spin_unlock(&rx->sta->lock);
+
+               if (skb_copy_bits(skb, offsetof(struct ieee80211_bar, control),
+                                 &bar_data, sizeof(bar_data)))
                        return RX_DROP_MONITOR;
-               }
-               tid_agg_rx = rx->sta->ampdu_mlme.tid_rx[tid];
 
-               start_seq_num = le16_to_cpu(bar->start_seq_num) >> 4;
+               tid = le16_to_cpu(bar_data.control) >> 12;
+
+               tid_agg_rx = rcu_dereference(rx->sta->ampdu_mlme.tid_rx[tid]);
+               if (!tid_agg_rx)
+                       return RX_DROP_MONITOR;
+
+               start_seq_num = le16_to_cpu(bar_data.start_seq_num) >> 4;
 
                /* reset session timer */
                if (tid_agg_rx->timeout)
@@ -1839,11 +1851,15 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx, struct sk_buff_head *frames)
                ieee80211_release_reorder_frames(hw, tid_agg_rx, start_seq_num,
                                                 frames);
                kfree_skb(skb);
-               spin_unlock(&rx->sta->lock);
                return RX_QUEUED;
        }
 
-       return RX_CONTINUE;
+       /*
+        * After this point, we only want management frames,
+        * so we can drop all remaining control frames to
+        * cooked monitor interfaces.
+        */
+       return RX_DROP_MONITOR;
 }
 
 static void ieee80211_process_sa_query_req(struct ieee80211_sub_if_data *sdata,
@@ -1939,23 +1955,23 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
                case WLAN_ACTION_ADDBA_REQ:
                        if (len < (IEEE80211_MIN_ACTION_SIZE +
                                   sizeof(mgmt->u.action.u.addba_req)))
-                               return RX_DROP_MONITOR;
-                       ieee80211_process_addba_request(local, rx->sta, mgmt, len);
-                       goto handled;
+                               goto invalid;
+                       break;
                case WLAN_ACTION_ADDBA_RESP:
                        if (len < (IEEE80211_MIN_ACTION_SIZE +
                                   sizeof(mgmt->u.action.u.addba_resp)))
-                               break;
-                       ieee80211_process_addba_resp(local, rx->sta, mgmt, len);
-                       goto handled;
+                               goto invalid;
+                       break;
                case WLAN_ACTION_DELBA:
                        if (len < (IEEE80211_MIN_ACTION_SIZE +
                                   sizeof(mgmt->u.action.u.delba)))
-                               break;
-                       ieee80211_process_delba(sdata, rx->sta, mgmt, len);
-                       goto handled;
+                               goto invalid;
+                       break;
+               default:
+                       goto invalid;
                }
-               break;
+
+               goto queue;
        case WLAN_CATEGORY_SPECTRUM_MGMT:
                if (local->hw.conf.channel->band != IEEE80211_BAND_5GHZ)
                        break;
@@ -1985,7 +2001,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
                        if (memcmp(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN))
                                break;
 
-                       return ieee80211_sta_rx_mgmt(sdata, rx->skb);
+                       goto queue;
                }
                break;
        case WLAN_CATEGORY_SA_QUERY:
@@ -2003,11 +2019,12 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
                break;
        case WLAN_CATEGORY_MESH_PLINK:
        case WLAN_CATEGORY_MESH_PATH_SEL:
-               if (ieee80211_vif_is_mesh(&sdata->vif))
-                       return ieee80211_mesh_rx_mgmt(sdata, rx->skb);
-               break;
+               if (!ieee80211_vif_is_mesh(&sdata->vif))
+                       break;
+               goto queue;
        }
 
+ invalid:
        /*
         * For AP mode, hostapd is responsible for handling any action
         * frames that we didn't handle, including returning unknown
@@ -2027,8 +2044,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
         */
        status = IEEE80211_SKB_RXCB(rx->skb);
 
-       if (sdata->vif.type == NL80211_IFTYPE_STATION &&
-           cfg80211_rx_action(rx->sdata->dev, status->freq,
+       if (cfg80211_rx_action(rx->sdata->dev, status->freq,
                               rx->skb->data, rx->skb->len,
                               GFP_ATOMIC))
                goto handled;
@@ -2040,11 +2056,11 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
        nskb = skb_copy_expand(rx->skb, local->hw.extra_tx_headroom, 0,
                               GFP_ATOMIC);
        if (nskb) {
-               struct ieee80211_mgmt *mgmt = (void *)nskb->data;
+               struct ieee80211_mgmt *nmgmt = (void *)nskb->data;
 
-               mgmt->u.action.category |= 0x80;
-               memcpy(mgmt->da, mgmt->sa, ETH_ALEN);
-               memcpy(mgmt->sa, rx->sdata->vif.addr, ETH_ALEN);
+               nmgmt->u.action.category |= 0x80;
+               memcpy(nmgmt->da, nmgmt->sa, ETH_ALEN);
+               memcpy(nmgmt->sa, rx->sdata->vif.addr, ETH_ALEN);
 
                memset(nskb->cb, 0, sizeof(nskb->cb));
 
@@ -2056,6 +2072,14 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
                rx->sta->rx_packets++;
        dev_kfree_skb(rx->skb);
        return RX_QUEUED;
+
+ queue:
+       rx->skb->pkt_type = IEEE80211_SDATA_QUEUE_TYPE_FRAME;
+       skb_queue_tail(&sdata->skb_queue, rx->skb);
+       ieee80211_queue_work(&local->hw, &sdata->work);
+       if (rx->sta)
+               rx->sta->rx_packets++;
+       return RX_QUEUED;
 }
 
 static ieee80211_rx_result debug_noinline
@@ -2063,10 +2087,15 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
 {
        struct ieee80211_sub_if_data *sdata = rx->sdata;
        ieee80211_rx_result rxs;
+       struct ieee80211_mgmt *mgmt = (void *)rx->skb->data;
+       __le16 stype;
 
        if (!(rx->flags & IEEE80211_RX_RA_MATCH))
                return RX_DROP_MONITOR;
 
+       if (rx->skb->len < 24)
+               return RX_DROP_MONITOR;
+
        if (ieee80211_drop_unencrypted_mgmt(rx))
                return RX_DROP_UNUSABLE;
 
@@ -2074,16 +2103,42 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
        if (rxs != RX_CONTINUE)
                return rxs;
 
-       if (ieee80211_vif_is_mesh(&sdata->vif))
-               return ieee80211_mesh_rx_mgmt(sdata, rx->skb);
+       stype = mgmt->frame_control & cpu_to_le16(IEEE80211_FCTL_STYPE);
 
-       if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
-               return ieee80211_ibss_rx_mgmt(sdata, rx->skb);
+       if (!ieee80211_vif_is_mesh(&sdata->vif) &&
+           sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+           sdata->vif.type != NL80211_IFTYPE_STATION)
+               return RX_DROP_MONITOR;
 
-       if (sdata->vif.type == NL80211_IFTYPE_STATION)
-               return ieee80211_sta_rx_mgmt(sdata, rx->skb);
+       switch (stype) {
+       case cpu_to_le16(IEEE80211_STYPE_BEACON):
+       case cpu_to_le16(IEEE80211_STYPE_PROBE_RESP):
+               /* process for all: mesh, mlme, ibss */
+               break;
+       case cpu_to_le16(IEEE80211_STYPE_DEAUTH):
+       case cpu_to_le16(IEEE80211_STYPE_DISASSOC):
+               /* process only for station */
+               if (sdata->vif.type != NL80211_IFTYPE_STATION)
+                       return RX_DROP_MONITOR;
+               break;
+       case cpu_to_le16(IEEE80211_STYPE_PROBE_REQ):
+       case cpu_to_le16(IEEE80211_STYPE_AUTH):
+               /* process only for ibss */
+               if (sdata->vif.type != NL80211_IFTYPE_ADHOC)
+                       return RX_DROP_MONITOR;
+               break;
+       default:
+               return RX_DROP_MONITOR;
+       }
 
-       return RX_DROP_MONITOR;
+       /* queue up frame and kick off work to process it */
+       rx->skb->pkt_type = IEEE80211_SDATA_QUEUE_TYPE_FRAME;
+       skb_queue_tail(&sdata->skb_queue, rx->skb);
+       ieee80211_queue_work(&rx->local->hw, &sdata->work);
+       if (rx->sta)
+               rx->sta->rx_packets++;
+
+       return RX_QUEUED;
 }
 
 static void ieee80211_rx_michael_mic_report(struct ieee80211_hdr *hdr,
@@ -2139,7 +2194,7 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,
                u8 rate_or_pad;
                __le16 chan_freq;
                __le16 chan_flags;
-       } __attribute__ ((packed)) *rthdr;
+       } __packed *rthdr;
        struct sk_buff *skb = rx->skb, *skb2;
        struct net_device *prev_dev = NULL;
        struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
@@ -2189,7 +2244,7 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,
                        skb2 = skb_clone(skb, GFP_ATOMIC);
                        if (skb2) {
                                skb2->dev = prev_dev;
-                               netif_rx(skb2);
+                               netif_receive_skb(skb2);
                        }
                }
 
@@ -2200,7 +2255,7 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,
 
        if (prev_dev) {
                skb->dev = prev_dev;
-               netif_rx(skb);
+               netif_receive_skb(skb);
                skb = NULL;
        } else
                goto out_free_skb;