diff options
-rw-r--r-- | include/net/mac80211.h | 37 | ||||
-rw-r--r-- | net/mac80211/rx.c | 113 | ||||
-rw-r--r-- | net/mac80211/wpa.c | 7 |
3 files changed, 103 insertions, 54 deletions
diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 000b8e3..6a2a0c3 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 b0959c2..08ca066 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 783af32..f5723ea 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 || |