Commit 23edcc41 authored by David Kilroy's avatar David Kilroy Committed by John W. Linville

orinoco: Add MIC on TX and check on RX

Use the MIC algorithm from the crypto subsystem.
Signed-off-by: default avatarDavid Kilroy <kilroyd@gmail.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 31afcef3
...@@ -336,6 +336,8 @@ config HERMES ...@@ -336,6 +336,8 @@ config HERMES
depends on (PPC_PMAC || PCI || PCMCIA) && WLAN_80211 depends on (PPC_PMAC || PCI || PCMCIA) && WLAN_80211
select WIRELESS_EXT select WIRELESS_EXT
select FW_LOADER select FW_LOADER
select CRYPTO
select CRYPTO_MICHAEL_MIC
---help--- ---help---
A driver for 802.11b wireless cards based on the "Hermes" or A driver for 802.11b wireless cards based on the "Hermes" or
Intersil HFA384x (Prism 2) MAC controller. This includes the vast Intersil HFA384x (Prism 2) MAC controller. This includes the vast
......
...@@ -184,13 +184,18 @@ ...@@ -184,13 +184,18 @@
#define HERMES_RXSTAT_ERR (0x0003) #define HERMES_RXSTAT_ERR (0x0003)
#define HERMES_RXSTAT_BADCRC (0x0001) #define HERMES_RXSTAT_BADCRC (0x0001)
#define HERMES_RXSTAT_UNDECRYPTABLE (0x0002) #define HERMES_RXSTAT_UNDECRYPTABLE (0x0002)
#define HERMES_RXSTAT_MIC (0x0010) /* Frame contains MIC */
#define HERMES_RXSTAT_MACPORT (0x0700) #define HERMES_RXSTAT_MACPORT (0x0700)
#define HERMES_RXSTAT_PCF (0x1000) /* Frame was received in CF period */ #define HERMES_RXSTAT_PCF (0x1000) /* Frame was received in CF period */
#define HERMES_RXSTAT_MIC_KEY_ID (0x1800) /* MIC key used */
#define HERMES_RXSTAT_MSGTYPE (0xE000) #define HERMES_RXSTAT_MSGTYPE (0xE000)
#define HERMES_RXSTAT_1042 (0x2000) /* RFC-1042 frame */ #define HERMES_RXSTAT_1042 (0x2000) /* RFC-1042 frame */
#define HERMES_RXSTAT_TUNNEL (0x4000) /* bridge-tunnel encoded frame */ #define HERMES_RXSTAT_TUNNEL (0x4000) /* bridge-tunnel encoded frame */
#define HERMES_RXSTAT_WMP (0x6000) /* Wavelan-II Management Protocol frame */ #define HERMES_RXSTAT_WMP (0x6000) /* Wavelan-II Management Protocol frame */
/* Shift amount for key ID in RXSTAT and TXCTRL */
#define HERMES_MIC_KEY_ID_SHIFT 11
struct hermes_tx_descriptor { struct hermes_tx_descriptor {
__le16 status; __le16 status;
__le16 reserved1; __le16 reserved1;
...@@ -209,6 +214,8 @@ struct hermes_tx_descriptor { ...@@ -209,6 +214,8 @@ struct hermes_tx_descriptor {
#define HERMES_TXCTRL_TX_OK (0x0002) /* ?? interrupt on Tx complete */ #define HERMES_TXCTRL_TX_OK (0x0002) /* ?? interrupt on Tx complete */
#define HERMES_TXCTRL_TX_EX (0x0004) /* ?? interrupt on Tx exception */ #define HERMES_TXCTRL_TX_EX (0x0004) /* ?? interrupt on Tx exception */
#define HERMES_TXCTRL_802_11 (0x0008) /* We supply 802.11 header */ #define HERMES_TXCTRL_802_11 (0x0008) /* We supply 802.11 header */
#define HERMES_TXCTRL_MIC (0x0010) /* 802.3 + TKIP */
#define HERMES_TXCTRL_MIC_KEY_ID (0x1800) /* MIC Key ID mask */
#define HERMES_TXCTRL_ALT_RTRY (0x0020) #define HERMES_TXCTRL_ALT_RTRY (0x0020)
/* Inquiry constants and data types */ /* Inquiry constants and data types */
......
...@@ -89,6 +89,9 @@ ...@@ -89,6 +89,9 @@
#include <net/iw_handler.h> #include <net/iw_handler.h>
#include <net/ieee80211.h> #include <net/ieee80211.h>
#include <linux/scatterlist.h>
#include <linux/crypto.h>
#include "hermes_rid.h" #include "hermes_rid.h"
#include "hermes_dld.h" #include "hermes_dld.h"
#include "orinoco.h" #include "orinoco.h"
...@@ -243,6 +246,74 @@ struct hermes_rx_descriptor { ...@@ -243,6 +246,74 @@ struct hermes_rx_descriptor {
static int __orinoco_program_rids(struct net_device *dev); static int __orinoco_program_rids(struct net_device *dev);
static void __orinoco_set_multicast_list(struct net_device *dev); static void __orinoco_set_multicast_list(struct net_device *dev);
/********************************************************************/
/* Michael MIC crypto setup */
/********************************************************************/
#define MICHAEL_MIC_LEN 8
static int orinoco_mic_init(struct orinoco_private *priv)
{
priv->tx_tfm_mic = crypto_alloc_hash("michael_mic", 0, 0);
if (IS_ERR(priv->tx_tfm_mic)) {
printk(KERN_DEBUG "orinoco_mic_init: could not allocate "
"crypto API michael_mic\n");
priv->tx_tfm_mic = NULL;
return -ENOMEM;
}
priv->rx_tfm_mic = crypto_alloc_hash("michael_mic", 0, 0);
if (IS_ERR(priv->rx_tfm_mic)) {
printk(KERN_DEBUG "orinoco_mic_init: could not allocate "
"crypto API michael_mic\n");
priv->rx_tfm_mic = NULL;
return -ENOMEM;
}
return 0;
}
static void orinoco_mic_free(struct orinoco_private *priv)
{
if (priv->tx_tfm_mic)
crypto_free_hash(priv->tx_tfm_mic);
if (priv->rx_tfm_mic)
crypto_free_hash(priv->rx_tfm_mic);
}
static int michael_mic(struct crypto_hash *tfm_michael, u8 *key,
u8 *da, u8 *sa, u8 priority,
u8 *data, size_t data_len, u8 *mic)
{
struct hash_desc desc;
struct scatterlist sg[2];
u8 hdr[ETH_HLEN + 2]; /* size of header + padding */
if (tfm_michael == NULL) {
printk(KERN_WARNING "michael_mic: tfm_michael == NULL\n");
return -1;
}
/* Copy header into buffer. We need the padding on the end zeroed */
memcpy(&hdr[0], da, ETH_ALEN);
memcpy(&hdr[ETH_ALEN], sa, ETH_ALEN);
hdr[ETH_ALEN*2] = priority;
hdr[ETH_ALEN*2+1] = 0;
hdr[ETH_ALEN*2+2] = 0;
hdr[ETH_ALEN*2+3] = 0;
/* Use scatter gather to MIC header and data in one go */
sg_init_table(sg, 2);
sg_set_buf(&sg[0], hdr, sizeof(hdr));
sg_set_buf(&sg[1], data, data_len);
if (crypto_hash_setkey(tfm_michael, key, MIC_KEYLEN))
return -1;
desc.tfm = tfm_michael;
desc.flags = 0;
return crypto_hash_digest(&desc, sg, data_len + sizeof(hdr),
mic);
}
/********************************************************************/ /********************************************************************/
/* Internal helper functions */ /* Internal helper functions */
/********************************************************************/ /********************************************************************/
...@@ -764,7 +835,6 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -764,7 +835,6 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
int err = 0; int err = 0;
u16 txfid = priv->txfid; u16 txfid = priv->txfid;
struct ethhdr *eh; struct ethhdr *eh;
int data_off;
int tx_control; int tx_control;
unsigned long flags; unsigned long flags;
...@@ -797,10 +867,12 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -797,10 +867,12 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
if (skb->len < ETH_HLEN) if (skb->len < ETH_HLEN)
goto drop; goto drop;
eh = (struct ethhdr *)skb->data;
tx_control = HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX; tx_control = HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX;
if (priv->encode_alg == IW_ENCODE_ALG_TKIP)
tx_control |= (priv->tx_key << HERMES_MIC_KEY_ID_SHIFT) |
HERMES_TXCTRL_MIC;
if (priv->has_alt_txcntl) { if (priv->has_alt_txcntl) {
/* WPA enabled firmwares have tx_cntl at the end of /* WPA enabled firmwares have tx_cntl at the end of
* the 802.11 header. So write zeroed descriptor and * the 802.11 header. So write zeroed descriptor and
...@@ -842,6 +914,8 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -842,6 +914,8 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
HERMES_802_3_OFFSET - HERMES_802_11_OFFSET); HERMES_802_3_OFFSET - HERMES_802_11_OFFSET);
} }
eh = (struct ethhdr *)skb->data;
/* Encapsulate Ethernet-II frames */ /* Encapsulate Ethernet-II frames */
if (ntohs(eh->h_proto) > ETH_DATA_LEN) { /* Ethernet-II frame */ if (ntohs(eh->h_proto) > ETH_DATA_LEN) { /* Ethernet-II frame */
struct header_struct { struct header_struct {
...@@ -851,33 +925,65 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -851,33 +925,65 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
/* Strip destination and source from the data */ /* Strip destination and source from the data */
skb_pull(skb, 2 * ETH_ALEN); skb_pull(skb, 2 * ETH_ALEN);
data_off = HERMES_802_2_OFFSET + sizeof(encaps_hdr);
/* And move them to a separate header */ /* And move them to a separate header */
memcpy(&hdr.eth, eh, 2 * ETH_ALEN); memcpy(&hdr.eth, eh, 2 * ETH_ALEN);
hdr.eth.h_proto = htons(sizeof(encaps_hdr) + skb->len); hdr.eth.h_proto = htons(sizeof(encaps_hdr) + skb->len);
memcpy(hdr.encap, encaps_hdr, sizeof(encaps_hdr)); memcpy(hdr.encap, encaps_hdr, sizeof(encaps_hdr));
err = hermes_bap_pwrite(hw, USER_BAP, &hdr, sizeof(hdr), /* Insert the SNAP header */
txfid, HERMES_802_3_OFFSET); if (skb_headroom(skb) < sizeof(hdr)) {
if (err) { printk(KERN_ERR
if (net_ratelimit()) "%s: Not enough headroom for 802.2 headers %d\n",
printk(KERN_ERR "%s: Error %d writing packet " dev->name, skb_headroom(skb));
"header to BAP\n", dev->name, err); goto drop;
goto busy;
} }
} else { /* IEEE 802.3 frame */ eh = (struct ethhdr *) skb_push(skb, sizeof(hdr));
data_off = HERMES_802_3_OFFSET; memcpy(eh, &hdr, sizeof(hdr));
} }
err = hermes_bap_pwrite(hw, USER_BAP, skb->data, skb->len, err = hermes_bap_pwrite(hw, USER_BAP, skb->data, skb->len,
txfid, data_off); txfid, HERMES_802_3_OFFSET);
if (err) { if (err) {
printk(KERN_ERR "%s: Error %d writing packet to BAP\n", printk(KERN_ERR "%s: Error %d writing packet to BAP\n",
dev->name, err); dev->name, err);
goto busy; goto busy;
} }
/* Calculate Michael MIC */
if (priv->encode_alg == IW_ENCODE_ALG_TKIP) {
u8 mic_buf[MICHAEL_MIC_LEN + 1];
u8 *mic;
size_t offset;
size_t len;
if (skb->len % 2) {
/* MIC start is on an odd boundary */
mic_buf[0] = skb->data[skb->len - 1];
mic = &mic_buf[1];
offset = skb->len - 1;
len = MICHAEL_MIC_LEN + 1;
} else {
mic = &mic_buf[0];
offset = skb->len;
len = MICHAEL_MIC_LEN;
}
michael_mic(priv->tx_tfm_mic,
priv->tkip_key[priv->tx_key].tx_mic,
eh->h_dest, eh->h_source, 0 /* priority */,
skb->data + ETH_HLEN, skb->len - ETH_HLEN, mic);
/* Write the MIC */
err = hermes_bap_pwrite(hw, USER_BAP, &mic_buf[0], len,
txfid, HERMES_802_3_OFFSET + offset);
if (err) {
printk(KERN_ERR "%s: Error %d writing MIC to BAP\n",
dev->name, err);
goto busy;
}
}
/* Finally, we actually initiate the send */ /* Finally, we actually initiate the send */
netif_stop_queue(dev); netif_stop_queue(dev);
...@@ -892,7 +998,7 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -892,7 +998,7 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
} }
dev->trans_start = jiffies; dev->trans_start = jiffies;
stats->tx_bytes += data_off + skb->len; stats->tx_bytes += HERMES_802_3_OFFSET + skb->len;
goto ok; goto ok;
drop: drop:
...@@ -1172,6 +1278,25 @@ static void orinoco_rx_monitor(struct net_device *dev, u16 rxfid, ...@@ -1172,6 +1278,25 @@ static void orinoco_rx_monitor(struct net_device *dev, u16 rxfid,
stats->rx_dropped++; stats->rx_dropped++;
} }
/* Get tsc from the firmware */
static int orinoco_hw_get_tkip_iv(struct orinoco_private *priv, int key,
u8 *tsc)
{
hermes_t *hw = &priv->hw;
int err = 0;
u8 tsc_arr[4][IW_ENCODE_SEQ_MAX_SIZE];
if ((key < 0) || (key > 4))
return -EINVAL;
err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_TKIP_IV,
sizeof(tsc_arr), NULL, &tsc_arr);
if (!err)
memcpy(tsc, &tsc_arr[key][0], sizeof(tsc_arr[0]));
return err;
}
static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
...@@ -1240,6 +1365,11 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) ...@@ -1240,6 +1365,11 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
goto update_stats; goto update_stats;
} }
/* Payload size does not include Michael MIC. Increase payload
* size to read it together with the data. */
if (status & HERMES_RXSTAT_MIC)
length += MICHAEL_MIC_LEN;
/* We need space for the packet data itself, plus an ethernet /* We need space for the packet data itself, plus an ethernet
header, plus 2 bytes so we can align the IP header on a header, plus 2 bytes so we can align the IP header on a
32bit boundary, plus 1 byte so we can read in odd length 32bit boundary, plus 1 byte so we can read in odd length
...@@ -1303,6 +1433,63 @@ static void orinoco_rx(struct net_device *dev, ...@@ -1303,6 +1433,63 @@ static void orinoco_rx(struct net_device *dev,
length = le16_to_cpu(desc->data_len); length = le16_to_cpu(desc->data_len);
fc = le16_to_cpu(desc->frame_ctl); fc = le16_to_cpu(desc->frame_ctl);
/* Calculate and check MIC */
if (status & HERMES_RXSTAT_MIC) {
int key_id = ((status & HERMES_RXSTAT_MIC_KEY_ID) >>
HERMES_MIC_KEY_ID_SHIFT);
u8 mic[MICHAEL_MIC_LEN];
u8 *rxmic;
u8 *src = (fc & IEEE80211_FCTL_FROMDS) ?
desc->addr3 : desc->addr2;
/* Extract Michael MIC from payload */
rxmic = skb->data + skb->len - MICHAEL_MIC_LEN;
skb_trim(skb, skb->len - MICHAEL_MIC_LEN);
length -= MICHAEL_MIC_LEN;
michael_mic(priv->rx_tfm_mic,
priv->tkip_key[key_id].rx_mic,
desc->addr1,
src,
0, /* priority or QoS? */
skb->data,
skb->len,
&mic[0]);
if (memcmp(mic, rxmic,
MICHAEL_MIC_LEN)) {
union iwreq_data wrqu;
struct iw_michaelmicfailure wxmic;
DECLARE_MAC_BUF(mac);
printk(KERN_WARNING "%s: "
"Invalid Michael MIC in data frame from %s, "
"using key %i\n",
dev->name, print_mac(mac, src), key_id);
/* TODO: update stats */
/* Notify userspace */
memset(&wxmic, 0, sizeof(wxmic));
wxmic.flags = key_id & IW_MICFAILURE_KEY_ID;
wxmic.flags |= (desc->addr1[0] & 1) ?
IW_MICFAILURE_GROUP : IW_MICFAILURE_PAIRWISE;
wxmic.src_addr.sa_family = ARPHRD_ETHER;
memcpy(wxmic.src_addr.sa_data, src, ETH_ALEN);
(void) orinoco_hw_get_tkip_iv(priv, key_id,
&wxmic.tsc[0]);
memset(&wrqu, 0, sizeof(wrqu));
wrqu.data.length = sizeof(wxmic);
wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu,
(char *) &wxmic);
goto drop;
}
}
/* Handle decapsulation /* Handle decapsulation
* In most cases, the firmware tell us about SNAP frames. * In most cases, the firmware tell us about SNAP frames.
* For some reason, the SNAP frames sent by LinkSys APs * For some reason, the SNAP frames sent by LinkSys APs
...@@ -1342,6 +1529,11 @@ static void orinoco_rx(struct net_device *dev, ...@@ -1342,6 +1529,11 @@ static void orinoco_rx(struct net_device *dev,
stats->rx_bytes += length; stats->rx_bytes += length;
return; return;
drop:
dev_kfree_skb(skb);
stats->rx_errors++;
stats->rx_dropped++;
} }
static void orinoco_rx_isr_tasklet(unsigned long data) static void orinoco_rx_isr_tasklet(unsigned long data)
...@@ -3112,8 +3304,14 @@ static int orinoco_init(struct net_device *dev) ...@@ -3112,8 +3304,14 @@ static int orinoco_init(struct net_device *dev)
else else
printk("40-bit key\n"); printk("40-bit key\n");
} }
if (priv->has_wpa) if (priv->has_wpa) {
printk(KERN_DEBUG "%s: WPA-PSK supported\n", dev->name); printk(KERN_DEBUG "%s: WPA-PSK supported\n", dev->name);
if (orinoco_mic_init(priv)) {
printk(KERN_ERR "%s: Failed to setup MIC crypto "
"algorithm. Disabling WPA support\n", dev->name);
priv->has_wpa = 0;
}
}
/* Now we have the firmware capabilities, allocate appropiate /* Now we have the firmware capabilities, allocate appropiate
* sized scan buffers */ * sized scan buffers */
...@@ -3292,6 +3490,9 @@ struct net_device ...@@ -3292,6 +3490,9 @@ struct net_device
dev->set_multicast_list = orinoco_set_multicast_list; dev->set_multicast_list = orinoco_set_multicast_list;
/* we use the default eth_mac_addr for setting the MAC addr */ /* we use the default eth_mac_addr for setting the MAC addr */
/* Reserve space in skb for the SNAP header */
dev->hard_header_len += ENCAPS_OVERHEAD;
/* Set up default callbacks */ /* Set up default callbacks */
dev->open = orinoco_open; dev->open = orinoco_open;
dev->stop = orinoco_stop; dev->stop = orinoco_stop;
...@@ -3327,6 +3528,7 @@ void free_orinocodev(struct net_device *dev) ...@@ -3327,6 +3528,7 @@ void free_orinocodev(struct net_device *dev)
tasklet_kill(&priv->rx_tasklet); tasklet_kill(&priv->rx_tasklet);
priv->wpa_ie_len = 0; priv->wpa_ie_len = 0;
kfree(priv->wpa_ie); kfree(priv->wpa_ie);
orinoco_mic_free(priv);
orinoco_bss_data_free(priv); orinoco_bss_data_free(priv);
free_netdev(dev); free_netdev(dev);
} }
......
...@@ -158,6 +158,8 @@ struct orinoco_private { ...@@ -158,6 +158,8 @@ struct orinoco_private {
int wpa_ie_len; int wpa_ie_len;
struct orinoco_tkip_key tkip_key[ORINOCO_MAX_KEYS]; struct orinoco_tkip_key tkip_key[ORINOCO_MAX_KEYS];
struct crypto_hash *rx_tfm_mic;
struct crypto_hash *tx_tfm_mic;
unsigned int wpa_enabled:1; unsigned int wpa_enabled:1;
unsigned int tkip_cm_active:1; unsigned int tkip_cm_active:1;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment