Merge branch 'upstream-fixes'
[sfrench/cifs-2.6.git] / net / ieee80211 / ieee80211_rx.c
index 5e33803880469eef972ffdee7ebc4c3404fd16b1..6b8469da29b15513e15962faa0be62890924c241 100644 (file)
@@ -35,7 +35,7 @@
 
 #include <net/ieee80211.h>
 
-static inline void ieee80211_monitor_rx(struct ieee80211_device *ieee,
+static void ieee80211_monitor_rx(struct ieee80211_device *ieee,
                                        struct sk_buff *skb,
                                        struct ieee80211_rx_stats *rx_stats)
 {
@@ -165,7 +165,7 @@ static int ieee80211_frag_cache_invalidate(struct ieee80211_device *ieee,
  * Responsible for handling management control frames
  *
  * Called by ieee80211_rx */
-static inline int
+static int
 ieee80211_rx_frame_mgmt(struct ieee80211_device *ieee, struct sk_buff *skb,
                        struct ieee80211_rx_stats *rx_stats, u16 type,
                        u16 stype)
@@ -266,7 +266,7 @@ static int ieee80211_is_eapol_frame(struct ieee80211_device *ieee,
 }
 
 /* Called only as a tasklet (software IRQ), by ieee80211_rx */
-static inline int
+static int
 ieee80211_rx_frame_decrypt(struct ieee80211_device *ieee, struct sk_buff *skb,
                           struct ieee80211_crypt_data *crypt)
 {
@@ -297,7 +297,7 @@ ieee80211_rx_frame_decrypt(struct ieee80211_device *ieee, struct sk_buff *skb,
 }
 
 /* Called only as a tasklet (software IRQ), by ieee80211_rx */
-static inline int
+static int
 ieee80211_rx_frame_decrypt_msdu(struct ieee80211_device *ieee,
                                struct sk_buff *skb, int keyidx,
                                struct ieee80211_crypt_data *crypt)
@@ -350,6 +350,7 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb,
        u8 src[ETH_ALEN];
        struct ieee80211_crypt_data *crypt = NULL;
        int keyidx = 0;
+       int can_be_decrypted = 0;
 
        hdr = (struct ieee80211_hdr_4addr *)skb->data;
        stats = &ieee->stats;
@@ -368,8 +369,8 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb,
 
        /* Put this code here so that we avoid duplicating it in all
         * Rx paths. - Jean II */
+#ifdef CONFIG_WIRELESS_EXT
 #ifdef IW_WIRELESS_SPY         /* defined in iw_handler.h */
-#ifdef CONFIG_NET_RADIO
        /* If spy monitoring on */
        if (ieee->spy_data.spy_number > 0) {
                struct iw_quality wstats;
@@ -396,8 +397,8 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb,
                /* Update spy records */
                wireless_spy_update(ieee->dev, hdr->addr2, &wstats);
        }
-#endif                         /* CONFIG_NET_RADIO */
 #endif                         /* IW_WIRELESS_SPY */
+#endif                         /* CONFIG_WIRELESS_EXT */
 
 #ifdef NOT_YET
        hostap_update_rx_stats(local->ap, hdr, rx_stats);
@@ -410,12 +411,23 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb,
                return 1;
        }
 
-       if (is_multicast_ether_addr(hdr->addr1)
-           ? ieee->host_mc_decrypt : ieee->host_decrypt) {
+       can_be_decrypted = (is_multicast_ether_addr(hdr->addr1) ||
+                           is_broadcast_ether_addr(hdr->addr2)) ?
+           ieee->host_mc_decrypt : ieee->host_decrypt;
+
+       if (can_be_decrypted) {
                int idx = 0;
-               if (skb->len >= hdrlen + 3)
+               if (skb->len >= hdrlen + 3) {
+                       /* Top two-bits of byte 3 are the key index */
                        idx = skb->data[hdrlen + 3] >> 6;
+               }
+
+               /* ieee->crypt[] is WEP_KEY (4) in length.  Given that idx
+                * is only allowed 2-bits of storage, no value of idx can
+                * be provided via above code that would result in idx
+                * being out of range */
                crypt = ieee->crypt[idx];
+
 #ifdef NOT_YET
                sta = NULL;
 
@@ -553,7 +565,7 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb,
 
        /* skb: hdr + (possibly fragmented, possibly encrypted) payload */
 
-       if (ieee->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) &&
+       if ((fc & IEEE80211_FCTL_PROTECTED) && can_be_decrypted &&
            (keyidx = ieee80211_rx_frame_decrypt(ieee, skb, crypt)) < 0)
                goto rx_dropped;
 
@@ -562,7 +574,7 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb,
        /* skb: hdr + (possibly fragmented) plaintext payload */
        // PR: FIXME: hostap has additional conditions in the "if" below:
        // ieee->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) &&
-       if ((frag != 0 || (fc & IEEE80211_FCTL_MOREFRAGS))) {
+       if ((frag != 0) || (fc & IEEE80211_FCTL_MOREFRAGS)) {
                int flen;
                struct sk_buff *frag_skb = ieee80211_frag_cache_get(ieee, hdr);
                IEEE80211_DEBUG_FRAG("Rx Fragment received (%u)\n", frag);
@@ -617,7 +629,7 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb,
 
        /* skb: hdr + (possible reassembled) full MSDU payload; possibly still
         * encrypted/authenticated */
-       if (ieee->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) &&
+       if ((fc & IEEE80211_FCTL_PROTECTED) && can_be_decrypted &&
            ieee80211_rx_frame_decrypt_msdu(ieee, skb, keyidx, crypt))
                goto rx_dropped;
 
@@ -742,7 +754,14 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb,
                memset(skb->cb, 0, sizeof(skb->cb));
                skb->dev = dev;
                skb->ip_summed = CHECKSUM_NONE; /* 802.11 crc not sufficient */
-               netif_rx(skb);
+               if (netif_rx(skb) == NET_RX_DROP) {
+                       /* netif_rx always succeeds, but it might drop
+                        * the packet.  If it drops the packet, we log that
+                        * in our stats. */
+                       IEEE80211_DEBUG_DROP
+                           ("RX: netif_rx dropped the packet\n");
+                       stats->rx_dropped++;
+               }
        }
 
       rx_exit:
@@ -918,6 +937,45 @@ static int ieee80211_parse_qos_info_param_IE(struct ieee80211_info_element
        return rc;
 }
 
+#ifdef CONFIG_IEEE80211_DEBUG
+#define MFIE_STRING(x) case MFIE_TYPE_ ##x: return #x
+
+static const char *get_info_element_string(u16 id)
+{
+       switch (id) {
+               MFIE_STRING(SSID);
+               MFIE_STRING(RATES);
+               MFIE_STRING(FH_SET);
+               MFIE_STRING(DS_SET);
+               MFIE_STRING(CF_SET);
+               MFIE_STRING(TIM);
+               MFIE_STRING(IBSS_SET);
+               MFIE_STRING(COUNTRY);
+               MFIE_STRING(HOP_PARAMS);
+               MFIE_STRING(HOP_TABLE);
+               MFIE_STRING(REQUEST);
+               MFIE_STRING(CHALLENGE);
+               MFIE_STRING(POWER_CONSTRAINT);
+               MFIE_STRING(POWER_CAPABILITY);
+               MFIE_STRING(TPC_REQUEST);
+               MFIE_STRING(TPC_REPORT);
+               MFIE_STRING(SUPP_CHANNELS);
+               MFIE_STRING(CSA);
+               MFIE_STRING(MEASURE_REQUEST);
+               MFIE_STRING(MEASURE_REPORT);
+               MFIE_STRING(QUIET);
+               MFIE_STRING(IBSS_DFS);
+               MFIE_STRING(ERP_INFO);
+               MFIE_STRING(RSN);
+               MFIE_STRING(RATES_EX);
+               MFIE_STRING(GENERIC);
+               MFIE_STRING(QOS_PARAMETER);
+       default:
+               return "UNKNOWN";
+       }
+}
+#endif
+
 static int ieee80211_parse_info_param(struct ieee80211_info_element
                                      *info_element, u16 length,
                                      struct ieee80211_network *network)
@@ -1028,7 +1086,9 @@ static int ieee80211_parse_info_param(struct ieee80211_info_element
                        break;
 
                case MFIE_TYPE_TIM:
-                       IEEE80211_DEBUG_MGMT("MFIE_TYPE_TIM: ignored\n");
+                       network->tim.tim_count = info_element->data[0];
+                       network->tim.tim_period = info_element->data[1];
+                       IEEE80211_DEBUG_MGMT("MFIE_TYPE_TIM: partially ignored\n");
                        break;
 
                case MFIE_TYPE_ERP_INFO:
@@ -1079,10 +1139,49 @@ static int ieee80211_parse_info_param(struct ieee80211_info_element
                        printk(KERN_ERR
                               "QoS Error need to parse QOS_PARAMETER IE\n");
                        break;
+                       /* 802.11h */
+               case MFIE_TYPE_POWER_CONSTRAINT:
+                       network->power_constraint = info_element->data[0];
+                       network->flags |= NETWORK_HAS_POWER_CONSTRAINT;
+                       break;
+
+               case MFIE_TYPE_CSA:
+                       network->power_constraint = info_element->data[0];
+                       network->flags |= NETWORK_HAS_CSA;
+                       break;
+
+               case MFIE_TYPE_QUIET:
+                       network->quiet.count = info_element->data[0];
+                       network->quiet.period = info_element->data[1];
+                       network->quiet.duration = info_element->data[2];
+                       network->quiet.offset = info_element->data[3];
+                       network->flags |= NETWORK_HAS_QUIET;
+                       break;
+
+               case MFIE_TYPE_IBSS_DFS:
+                       if (network->ibss_dfs)
+                               break;
+                       network->ibss_dfs =
+                           kmalloc(info_element->len, GFP_ATOMIC);
+                       if (!network->ibss_dfs)
+                               return 1;
+                       memcpy(network->ibss_dfs, info_element->data,
+                              info_element->len);
+                       network->flags |= NETWORK_HAS_IBSS_DFS;
+                       break;
+
+               case MFIE_TYPE_TPC_REPORT:
+                       network->tpc_report.transmit_power =
+                           info_element->data[0];
+                       network->tpc_report.link_margin = info_element->data[1];
+                       network->flags |= NETWORK_HAS_TPC_REPORT;
+                       break;
 
                default:
-                       IEEE80211_DEBUG_MGMT("unsupported IE %d\n",
-                                            info_element->id);
+                       IEEE80211_DEBUG_MGMT
+                           ("Unsupported info element: %s (%d)\n",
+                            get_info_element_string(info_element->id),
+                            info_element->id);
                        break;
                }
 
@@ -1098,7 +1197,9 @@ static int ieee80211_parse_info_param(struct ieee80211_info_element
 static int ieee80211_handle_assoc_resp(struct ieee80211_device *ieee, struct ieee80211_assoc_response
                                       *frame, struct ieee80211_rx_stats *stats)
 {
-       struct ieee80211_network network_resp;
+       struct ieee80211_network network_resp = {
+               .ibss_dfs = NULL,
+       };
        struct ieee80211_network *network = &network_resp;
        struct net_device *dev = ieee->dev;
 
@@ -1156,7 +1257,7 @@ static int ieee80211_handle_assoc_resp(struct ieee80211_device *ieee, struct iee
 
 /***************************************************/
 
-static inline int ieee80211_network_init(struct ieee80211_device *ieee, struct ieee80211_probe_response
+static int ieee80211_network_init(struct ieee80211_device *ieee, struct ieee80211_probe_response
                                         *beacon,
                                         struct ieee80211_network *network,
                                         struct ieee80211_rx_stats *stats)
@@ -1235,12 +1336,15 @@ static inline int is_same_network(struct ieee80211_network *src,
                !memcmp(src->ssid, dst->ssid, src->ssid_len));
 }
 
-static inline void update_network(struct ieee80211_network *dst,
+static void update_network(struct ieee80211_network *dst,
                                  struct ieee80211_network *src)
 {
        int qos_active;
        u8 old_param;
 
+       ieee80211_network_reset(dst);
+       dst->ibss_dfs = src->ibss_dfs;
+
        memcpy(&dst->stats, &src->stats, sizeof(struct ieee80211_rx_stats));
        dst->capability = src->capability;
        memcpy(dst->rates, src->rates, src->rates_len);
@@ -1257,6 +1361,7 @@ static inline void update_network(struct ieee80211_network *dst,
        dst->listen_interval = src->listen_interval;
        dst->atim_window = src->atim_window;
        dst->erp_value = src->erp_value;
+       dst->tim = src->tim;
 
        memcpy(dst->wpa_ie, src->wpa_ie, src->wpa_ie_len);
        dst->wpa_ie_len = src->wpa_ie_len;
@@ -1289,19 +1394,21 @@ static inline void update_network(struct ieee80211_network *dst,
        /* dst->last_associate is not overwritten */
 }
 
-static inline int is_beacon(int fc)
+static inline int is_beacon(__le16 fc)
 {
        return (WLAN_FC_GET_STYPE(le16_to_cpu(fc)) == IEEE80211_STYPE_BEACON);
 }
 
-static inline void ieee80211_process_probe_response(struct ieee80211_device
+static void ieee80211_process_probe_response(struct ieee80211_device
                                                    *ieee, struct
                                                    ieee80211_probe_response
                                                    *beacon, struct ieee80211_rx_stats
                                                    *stats)
 {
        struct net_device *dev = ieee->dev;
-       struct ieee80211_network network;
+       struct ieee80211_network network = {
+               .ibss_dfs = NULL,
+       };
        struct ieee80211_network *target;
        struct ieee80211_network *oldest = NULL;
 #ifdef CONFIG_IEEE80211_DEBUG
@@ -1336,9 +1443,7 @@ static inline void ieee80211_process_probe_response(struct ieee80211_device
                                     escape_essid(info_element->data,
                                                  info_element->len),
                                     MAC_ARG(beacon->header.addr3),
-                                    is_beacon(le16_to_cpu
-                                              (beacon->header.
-                                               frame_ctl)) ?
+                                    is_beacon(beacon->header.frame_ctl) ?
                                     "BEACON" : "PROBE RESPONSE");
                return;
        }
@@ -1376,6 +1481,7 @@ static inline void ieee80211_process_probe_response(struct ieee80211_device
                                             escape_essid(target->ssid,
                                                          target->ssid_len),
                                             MAC_ARG(target->bssid));
+                       ieee80211_network_reset(target);
                } else {
                        /* Otherwise just pull from the free list */
                        target = list_entry(ieee->network_free_list.next,
@@ -1388,28 +1494,26 @@ static inline void ieee80211_process_probe_response(struct ieee80211_device
                                     escape_essid(network.ssid,
                                                  network.ssid_len),
                                     MAC_ARG(network.bssid),
-                                    is_beacon(le16_to_cpu
-                                              (beacon->header.
-                                               frame_ctl)) ?
+                                    is_beacon(beacon->header.frame_ctl) ?
                                     "BEACON" : "PROBE RESPONSE");
 #endif
                memcpy(target, &network, sizeof(*target));
+               network.ibss_dfs = NULL;
                list_add_tail(&target->list, &ieee->network_list);
        } else {
                IEEE80211_DEBUG_SCAN("Updating '%s' (" MAC_FMT ") via %s.\n",
                                     escape_essid(target->ssid,
                                                  target->ssid_len),
                                     MAC_ARG(target->bssid),
-                                    is_beacon(le16_to_cpu
-                                              (beacon->header.
-                                               frame_ctl)) ?
+                                    is_beacon(beacon->header.frame_ctl) ?
                                     "BEACON" : "PROBE RESPONSE");
                update_network(target, &network);
+               network.ibss_dfs = NULL;
        }
 
        spin_unlock_irqrestore(&ieee->lock, flags);
 
-       if (is_beacon(le16_to_cpu(beacon->header.frame_ctl))) {
+       if (is_beacon(beacon->header.frame_ctl)) {
                if (ieee->handle_beacon != NULL)
                        ieee->handle_beacon(dev, beacon, &network);
        } else {
@@ -1439,7 +1543,7 @@ void ieee80211_rx_mgt(struct ieee80211_device *ieee,
                break;
 
        case IEEE80211_STYPE_PROBE_REQ:
-               IEEE80211_DEBUG_MGMT("recieved auth (%d)\n",
+               IEEE80211_DEBUG_MGMT("received auth (%d)\n",
                                     WLAN_FC_GET_STYPE(le16_to_cpu
                                                       (header->frame_ctl)));
 
@@ -1473,7 +1577,7 @@ void ieee80211_rx_mgt(struct ieee80211_device *ieee,
                break;
        case IEEE80211_STYPE_AUTH:
 
-               IEEE80211_DEBUG_MGMT("recieved auth (%d)\n",
+               IEEE80211_DEBUG_MGMT("received auth (%d)\n",
                                     WLAN_FC_GET_STYPE(le16_to_cpu
                                                       (header->frame_ctl)));
 
@@ -1489,10 +1593,43 @@ void ieee80211_rx_mgt(struct ieee80211_device *ieee,
                                              header);
                break;
 
+       case IEEE80211_STYPE_ACTION:
+               IEEE80211_DEBUG_MGMT("ACTION\n");
+               if (ieee->handle_action)
+                       ieee->handle_action(ieee->dev,
+                                           (struct ieee80211_action *)
+                                           header, stats);
+               break;
+
+       case IEEE80211_STYPE_REASSOC_REQ:
+               IEEE80211_DEBUG_MGMT("received reassoc (%d)\n",
+                                    WLAN_FC_GET_STYPE(le16_to_cpu
+                                                      (header->frame_ctl)));
+
+               IEEE80211_WARNING("%s: IEEE80211_REASSOC_REQ received\n",
+                                 ieee->dev->name);
+               if (ieee->handle_reassoc_request != NULL)
+                       ieee->handle_reassoc_request(ieee->dev,
+                                                   (struct ieee80211_reassoc_request *)
+                                                    header);
+               break;
+
+       case IEEE80211_STYPE_ASSOC_REQ:
+               IEEE80211_DEBUG_MGMT("received assoc (%d)\n",
+                                    WLAN_FC_GET_STYPE(le16_to_cpu
+                                                      (header->frame_ctl)));
+
+               IEEE80211_WARNING("%s: IEEE80211_ASSOC_REQ received\n",
+                                 ieee->dev->name);
+               if (ieee->handle_assoc_request != NULL)
+                       ieee->handle_assoc_request(ieee->dev);
+               break;
+
        case IEEE80211_STYPE_DEAUTH:
-               printk("DEAUTH from AP\n");
+               IEEE80211_DEBUG_MGMT("DEAUTH\n");
                if (ieee->handle_deauth != NULL)
-                       ieee->handle_deauth(ieee->dev, (struct ieee80211_auth *)
+                       ieee->handle_deauth(ieee->dev,
+                                           (struct ieee80211_deauth *)
                                            header);
                break;
        default: