aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/net/mac80211.h37
-rw-r--r--net/mac80211/rx.c113
-rw-r--r--net/mac80211/wpa.c7
3 files changed, 103 insertions, 54 deletions
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 000b8e3133b..6a2a0c3e725 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1,7 +1,9 @@
/*
- * Low-level hardware driver -- IEEE 802.11 driver (80211.o) interface
+ * mac80211 <-> driver interface
+ *
* Copyright 2002-2005, Devicescape Software, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
+ * Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -472,10 +474,16 @@ struct ieee80211_hw {
*/
#define IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE (1<<1)
- /* Some devices handle decryption internally and do not
+ /*
+ * Some devices handle decryption internally and do not
* indicate whether the frame was encrypted (unencrypted frames
* will be dropped by the hardware, unless specifically allowed
- * through) */
+ * through.)
+ * It is permissible to not handle all encrypted frames and fall
+ * back to software encryption; however, if this flag is set
+ * unencrypted frames must be dropped unless the driver is told
+ * otherwise via the set_ieee8021x() callback.
+ */
#define IEEE80211_HW_DEVICE_HIDES_WEP (1<<2)
/* Whether RX frames passed to ieee80211_rx() include FCS in the end */
@@ -489,6 +497,18 @@ struct ieee80211_hw {
* can fetch them with ieee80211_get_buffered_bc(). */
#define IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING (1<<4)
+ /*
+ * This flag is only relevant if hardware encryption is used.
+ * If set, it has two meanings:
+ * 1) the IV and ICV are present in received frames that have
+ * been decrypted (unless IEEE80211_HW_DEVICE_HIDES_WEP is
+ * also set)
+ * 2) on transmission, the IV should be generated in software.
+ *
+ * Please let us know if you *don't* use this flag, the stack would
+ * really like to be able to get the IV to keep key statistics
+ * accurate.
+ */
#define IEEE80211_HW_WEP_INCLUDE_IV (1<<5)
/* hole at 6 */
@@ -496,11 +516,12 @@ struct ieee80211_hw {
/* Force software encryption for TKIP packets if WMM is enabled. */
#define IEEE80211_HW_NO_TKIP_WMM_HWACCEL (1<<7)
- /* Some devices handle Michael MIC internally and do not include MIC in
- * the received packets passed up. device_strips_mic must be set
- * for such devices. The 'encryption' frame control bit is expected to
- * be still set in the IEEE 802.11 header with this option unlike with
- * the device_hides_wep configuration option.
+ /*
+ * Some devices handle Michael MIC internally and do not include MIC in
+ * the received packets passed up. This flag must be set for such
+ * devices. The 'encryption' frame control bit is expected to be still
+ * set in the IEEE 802.11 header with this option unlike with the
+ * IEEE80211_HW_DEVICE_HIDES_WEP flag.
*/
#define IEEE80211_HW_DEVICE_STRIPS_MIC (1<<8)
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index b0959c24986..08ca066246b 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -310,52 +310,77 @@ static ieee80211_txrx_result
ieee80211_rx_h_load_key(struct ieee80211_txrx_data *rx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
- int always_sta_key;
+ int keyidx;
+ int hdrlen;
- if (rx->sdata->type == IEEE80211_IF_TYPE_STA)
- always_sta_key = 0;
- else
- always_sta_key = 1;
+ /*
+ * Key selection 101
+ *
+ * There are three types of keys:
+ * - GTK (group keys)
+ * - PTK (pairwise keys)
+ * - STK (station-to-station pairwise keys)
+ *
+ * When selecting a key, we have to distinguish between multicast
+ * (including broadcast) and unicast frames, the latter can only
+ * use PTKs and STKs while the former always use GTKs. Unless, of
+ * course, actual WEP keys ("pre-RSNA") are used, then unicast
+ * frames can also use key indizes like GTKs. Hence, if we don't
+ * have a PTK/STK we check the key index for a WEP key.
+ *
+ * There is also a slight problem in IBSS mode: GTKs are negotiated
+ * with each station, that is something we don't currently handle.
+ */
+
+ if (!(rx->fc & IEEE80211_FCTL_PROTECTED))
+ return TXRX_CONTINUE;
- if (rx->sta && rx->sta->key && always_sta_key) {
+ /*
+ * No point in finding a key if the frame is neither
+ * addressed to us nor a multicast frame.
+ */
+ if (!rx->u.rx.ra_match)
+ return TXRX_CONTINUE;
+
+ if (!is_multicast_ether_addr(hdr->addr1) && rx->sta && rx->sta->key) {
rx->key = rx->sta->key;
} else {
- if (rx->sta && rx->sta->key)
- rx->key = rx->sta->key;
- else
- rx->key = rx->sdata->default_key;
+ /*
+ * The device doesn't give us the IV so we won't be
+ * able to look up the key. That's ok though, we
+ * don't need to decrypt the frame, we just won't
+ * be able to keep statistics accurate.
+ * Except for key threshold notifications, should
+ * we somehow allow the driver to tell us which key
+ * the hardware used if this flag is set?
+ */
+ if (!(rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV))
+ return TXRX_CONTINUE;
- if ((rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) &&
- rx->fc & IEEE80211_FCTL_PROTECTED) {
- int keyidx = ieee80211_wep_get_keyidx(rx->skb);
+ hdrlen = ieee80211_get_hdrlen(rx->fc);
- if (keyidx >= 0 && keyidx < NUM_DEFAULT_KEYS &&
- (!rx->sta || !rx->sta->key || keyidx > 0))
- rx->key = rx->sdata->keys[keyidx];
+ if (rx->skb->len < 8 + hdrlen)
+ return TXRX_DROP; /* TODO: count this? */
- if (!rx->key) {
- if (!rx->u.rx.ra_match)
- return TXRX_DROP;
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: RX WEP frame "
- "with unknown keyidx %d "
- "(A1=" MAC_FMT
- " A2=" MAC_FMT
- " A3=" MAC_FMT ")\n",
- rx->dev->name, keyidx,
- MAC_ARG(hdr->addr1),
- MAC_ARG(hdr->addr2),
- MAC_ARG(hdr->addr3));
- /*
- * TODO: notify userspace about this
- * via cfg/nl80211
- */
- return TXRX_DROP;
- }
- }
+ /*
+ * no need to call ieee80211_wep_get_keyidx,
+ * it verifies a bunch of things we've done already
+ */
+ keyidx = rx->skb->data[hdrlen + 3] >> 6;
+
+ rx->key = rx->sdata->keys[keyidx];
+
+ /*
+ * RSNA-protected unicast frames should always be sent with
+ * pairwise or station-to-station keys, but for WEP we allow
+ * using a key index as well.
+ */
+ if (rx->key && rx->key->alg != ALG_WEP &&
+ !is_multicast_ether_addr(hdr->addr1))
+ rx->key = NULL;
}
- if (rx->fc & IEEE80211_FCTL_PROTECTED && rx->key && rx->u.rx.ra_match) {
+ if (rx->key) {
rx->key->tx_rx_count++;
if (unlikely(rx->local->key_tx_rx_threshold &&
rx->key->tx_rx_count >
@@ -516,10 +541,6 @@ ieee80211_rx_h_wep_weak_iv_detection(struct ieee80211_txrx_data *rx)
static ieee80211_txrx_result
ieee80211_rx_h_wep_decrypt(struct ieee80211_txrx_data *rx)
{
- /* If the device handles decryption totally, skip this test */
- if (rx->local->hw.flags & IEEE80211_HW_DEVICE_HIDES_WEP)
- return TXRX_CONTINUE;
-
if ((rx->key && rx->key->alg != ALG_WEP) ||
!(rx->fc & IEEE80211_FCTL_PROTECTED) ||
((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA &&
@@ -871,8 +892,14 @@ ieee80211_rx_h_802_1x_pae(struct ieee80211_txrx_data *rx)
static ieee80211_txrx_result
ieee80211_rx_h_drop_unencrypted(struct ieee80211_txrx_data *rx)
{
- /* If the device handles decryption totally, skip this test */
- if (rx->local->hw.flags & IEEE80211_HW_DEVICE_HIDES_WEP)
+ /*
+ * Pass through unencrypted frames if the hardware might have
+ * decrypted them already without telling us, but that can only
+ * be true if we either didn't find a key or the found key is
+ * uploaded to the hardware.
+ */
+ if ((rx->local->hw.flags & IEEE80211_HW_DEVICE_HIDES_WEP) &&
+ (!rx->key || !rx->key->force_sw_encrypt))
return TXRX_CONTINUE;
/* Drop unencrypted frames if key is set. */
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index 783af32c691..f5723ea15aa 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -137,9 +137,10 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_txrx_data *rx)
fc = rx->fc;
- /* If device handles decryption totally, skip this check */
- if ((rx->local->hw.flags & IEEE80211_HW_DEVICE_HIDES_WEP) ||
- (rx->local->hw.flags & IEEE80211_HW_DEVICE_STRIPS_MIC))
+ /*
+ * No way to verify the MIC if the hardware stripped it
+ */
+ if (rx->local->hw.flags & IEEE80211_HW_DEVICE_STRIPS_MIC)
return TXRX_CONTINUE;
if (!rx->key || rx->key->alg != ALG_TKIP ||