Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless
[pandora-kernel.git] / net / mac80211 / rx.c
index 097b42d..daf5cde 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/rcupdate.h>
+#include <linux/export.h>
 #include <net/mac80211.h>
 #include <net/ieee80211_radiotap.h>
 
@@ -747,10 +748,11 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx)
        struct ieee80211_local *local = rx->local;
        struct ieee80211_hw *hw = &local->hw;
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+       struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
        struct sta_info *sta = rx->sta;
        struct tid_ampdu_rx *tid_agg_rx;
        u16 sc;
-       int tid;
+       u8 tid, ack_policy;
 
        if (!ieee80211_is_data_qos(hdr->frame_control))
                goto dont_reorder;
@@ -763,6 +765,8 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx)
        if (!sta)
                goto dont_reorder;
 
+       ack_policy = *ieee80211_get_qos_ctl(hdr) &
+                    IEEE80211_QOS_CTL_ACK_POLICY_MASK;
        tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
 
        tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]);
@@ -773,6 +777,15 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx)
        if (unlikely(hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_NULLFUNC)))
                goto dont_reorder;
 
+       /* not part of a BA session */
+       if (ack_policy != IEEE80211_QOS_CTL_ACK_POLICY_BLOCKACK &&
+           ack_policy != IEEE80211_QOS_CTL_ACK_POLICY_NORMAL)
+               goto dont_reorder;
+
+       /* not actually part of this BA session */
+       if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))
+               goto dont_reorder;
+
        /* new, potentially un-ordered, ampdu frame - process it */
 
        /* reset session timer */
@@ -857,6 +870,13 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
                            rx->sdata->control_port_protocol)
                                return RX_CONTINUE;
                }
+
+               if (rx->sdata->vif.type == NL80211_IFTYPE_AP &&
+                   cfg80211_rx_spurious_frame(rx->sdata->dev,
+                                              hdr->addr2,
+                                              GFP_ATOMIC))
+                       return RX_DROP_UNUSABLE;
+
                return RX_DROP_MONITOR;
        }
 
@@ -1326,15 +1346,20 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
 
                /*
                 * If we receive a 4-addr nullfunc frame from a STA
-                * that was not moved to a 4-addr STA vlan yet, drop
-                * the frame to the monitor interface, to make sure
-                * that hostapd sees it
+                * that was not moved to a 4-addr STA vlan yet send
+                * the event to userspace and for older hostapd drop
+                * the frame to the monitor interface.
                 */
                if (ieee80211_has_a4(hdr->frame_control) &&
                    (rx->sdata->vif.type == NL80211_IFTYPE_AP ||
                     (rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
-                     !rx->sdata->u.vlan.sta)))
+                     !rx->sdata->u.vlan.sta))) {
+                       if (!test_and_set_sta_flag(sta, WLAN_STA_4ADDR_EVENT))
+                               cfg80211_rx_unexpected_4addr_frame(
+                                       rx->sdata->dev, sta->sta.addr,
+                                       GFP_ATOMIC);
                        return RX_DROP_MONITOR;
+               }
                /*
                 * Update counter and free packet here to avoid
                 * counting this as a dropped packed.
@@ -1870,13 +1895,16 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
 static ieee80211_rx_result
 ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
 {
-       struct ieee80211_hdr *hdr;
+       struct ieee80211_hdr *fwd_hdr, *hdr;
+       struct ieee80211_tx_info *info;
        struct ieee80211s_hdr *mesh_hdr;
-       unsigned int hdrlen;
        struct sk_buff *skb = rx->skb, *fwd_skb;
        struct ieee80211_local *local = rx->local;
        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;
        hdrlen = ieee80211_hdrlen(hdr->frame_control);
@@ -1892,15 +1920,8 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
                return RX_CONTINUE;
 
        if (!mesh_hdr->ttl)
-               /* illegal frame */
                return RX_DROP_MONITOR;
 
-       if (ieee80211_queue_stopped(&local->hw, skb_get_queue_mapping(skb))) {
-               IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh,
-                                               dropped_frames_congestion);
-               return RX_DROP_MONITOR;
-       }
-
        if (mesh_hdr->flags & MESH_FLAGS_AE) {
                struct mesh_path *mppath;
                char *proxied_addr;
@@ -1932,60 +1953,50 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
            compare_ether_addr(sdata->vif.addr, hdr->addr3) == 0)
                return RX_CONTINUE;
 
-       mesh_hdr->ttl--;
+       q = ieee80211_select_queue_80211(local, skb, hdr);
+       if (ieee80211_queue_stopped(&local->hw, q)) {
+               IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_congestion);
+               return RX_DROP_MONITOR;
+       }
+       skb_set_queue_mapping(skb, q);
 
-       if (status->rx_flags & IEEE80211_RX_RA_MATCH) {
-               if (!mesh_hdr->ttl)
-                       IEEE80211_IFSTA_MESH_CTR_INC(&rx->sdata->u.mesh,
-                                                    dropped_frames_ttl);
-               else {
-                       struct ieee80211_hdr *fwd_hdr;
-                       struct ieee80211_tx_info *info;
-
-                       fwd_skb = skb_copy(skb, GFP_ATOMIC);
-
-                       if (!fwd_skb && net_ratelimit())
-                               printk(KERN_DEBUG "%s: failed to clone mesh frame\n",
-                                                  sdata->name);
-                       if (!fwd_skb)
-                               goto out;
-
-                       fwd_hdr =  (struct ieee80211_hdr *) fwd_skb->data;
-                       memcpy(fwd_hdr->addr2, sdata->vif.addr, ETH_ALEN);
-                       info = IEEE80211_SKB_CB(fwd_skb);
-                       memset(info, 0, sizeof(*info));
-                       info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
-                       info->control.vif = &rx->sdata->vif;
-                       if (is_multicast_ether_addr(fwd_hdr->addr1)) {
-                               IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh,
-                                                               fwded_mcast);
-                               skb_set_queue_mapping(fwd_skb,
-                                       ieee80211_select_queue(sdata, fwd_skb));
-                               ieee80211_set_qos_hdr(sdata, fwd_skb);
-                       } else {
-                               int err;
-                               /*
-                                * Save TA to addr1 to send TA a path error if a
-                                * suitable next hop is not found
-                                */
-                               memcpy(fwd_hdr->addr1, fwd_hdr->addr2,
-                                               ETH_ALEN);
-                               err = mesh_nexthop_lookup(fwd_skb, sdata);
-                               /* Failed to immediately resolve next hop:
-                                * fwded frame was dropped or will be added
-                                * later to the pending skb queue.  */
-                               if (err)
-                                       return RX_DROP_MONITOR;
-
-                               IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh,
-                                                               fwded_unicast);
-                       }
-                       IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh,
-                                                    fwded_frames);
-                       ieee80211_add_pending_skb(local, fwd_skb);
-               }
+       if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))
+               goto out;
+
+       if (!--mesh_hdr->ttl) {
+               IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_ttl);
+               return RX_DROP_MONITOR;
        }
 
+       fwd_skb = skb_copy(skb, GFP_ATOMIC);
+       if (!fwd_skb) {
+               if (net_ratelimit())
+                       printk(KERN_DEBUG "%s: failed to clone mesh frame\n",
+                                       sdata->name);
+               goto out;
+       }
+
+       fwd_hdr =  (struct ieee80211_hdr *) fwd_skb->data;
+       info = IEEE80211_SKB_CB(fwd_skb);
+       memset(info, 0, sizeof(*info));
+       info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
+       info->control.vif = &rx->sdata->vif;
+       info->control.jiffies = jiffies;
+       if (is_multicast_ether_addr(fwd_hdr->addr1)) {
+               IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_mcast);
+               memcpy(fwd_hdr->addr2, sdata->vif.addr, ETH_ALEN);
+       } else if (!mesh_nexthop_lookup(fwd_skb, sdata)) {
+               IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_unicast);
+       } else {
+               /* unable to resolve next hop */
+               mesh_path_error_tx(ifmsh->mshcfg.element_ttl, fwd_hdr->addr3,
+                                   0, reason, fwd_hdr->addr2, sdata);
+               IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_no_route);
+               return RX_DROP_MONITOR;
+       }
+
+       IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_frames);
+       ieee80211_add_pending_skb(local, fwd_skb);
  out:
        if (is_multicast_ether_addr(hdr->addr1) ||
            sdata->dev->flags & IFF_PROMISC)
@@ -2013,12 +2024,17 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
                return RX_DROP_MONITOR;
 
        /*
-        * Allow the cooked monitor interface of an AP to see 4-addr frames so
-        * that a 4-addr station can be detected and moved into a separate VLAN
+        * Send unexpected-4addr-frame event to hostapd. For older versions,
+        * also drop the frame to cooked monitor interfaces.
         */
        if (ieee80211_has_a4(hdr->frame_control) &&
-           sdata->vif.type == NL80211_IFTYPE_AP)
+           sdata->vif.type == NL80211_IFTYPE_AP) {
+               if (rx->sta &&
+                   !test_and_set_sta_flag(rx->sta, WLAN_STA_4ADDR_EVENT))
+                       cfg80211_rx_unexpected_4addr_frame(
+                               rx->sdata->dev, rx->sta->sta.addr, GFP_ATOMIC);
                return RX_DROP_MONITOR;
+       }
 
        err = __ieee80211_data_to_8023(rx, &port_control);
        if (unlikely(err))
@@ -2173,6 +2189,18 @@ ieee80211_rx_h_mgmt_check(struct ieee80211_rx_data *rx)
        if (!ieee80211_is_mgmt(mgmt->frame_control))
                return RX_DROP_MONITOR;
 
+       if (rx->sdata->vif.type == NL80211_IFTYPE_AP &&
+           ieee80211_is_beacon(mgmt->frame_control) &&
+           !(rx->flags & IEEE80211_RX_BEACON_REPORTED)) {
+               struct ieee80211_rx_status *status;
+
+               status = IEEE80211_SKB_RXCB(rx->skb);
+               cfg80211_report_obss_beacon(rx->local->hw.wiphy,
+                                           rx->skb->data, rx->skb->len,
+                                           status->freq, GFP_ATOMIC);
+               rx->flags |= IEEE80211_RX_BEACON_REPORTED;
+       }
+
        if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))
                return RX_DROP_MONITOR;
 
@@ -2206,13 +2234,8 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
 
        switch (mgmt->u.action.category) {
        case WLAN_CATEGORY_BACK:
-               /*
-                * The aggregation code is not prepared to handle
-                * anything but STA/AP due to the BSSID handling;
-                * IBSS could work in the code but isn't supported
-                * by drivers or the standard.
-                */
                if (sdata->vif.type != NL80211_IFTYPE_STATION &&
+                   sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
                    sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
                    sdata->vif.type != NL80211_IFTYPE_AP)
                        break;
@@ -2492,6 +2515,10 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,
                goto out_free_skb;
        rx->flags |= IEEE80211_RX_CMNTR;
 
+       /* If there are no cooked monitor interfaces, just free the SKB */
+       if (!local->cooked_mntrs)
+               goto out_free_skb;
+
        if (skb_headroom(skb) < sizeof(*rthdr) &&
            pskb_expand_head(skb, sizeof(*rthdr), 0, GFP_ATOMIC))
                goto out_free_skb;