|
@@ -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 {
|
|
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)
|
|
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)
|
|
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)
|
|
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)
|
|
|
|
|
|
/* 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),
|
|
|
|
- txfid, HERMES_802_3_OFFSET);
|
|
|
|
- if (err) {
|
|
|
|
- if (net_ratelimit())
|
|
|
|
- printk(KERN_ERR "%s: Error %d writing packet "
|
|
|
|
- "header to BAP\n", dev->name, err);
|
|
|
|
- goto busy;
|
|
|
|
|
|
+ /* Insert the SNAP header */
|
|
|
|
+ if (skb_headroom(skb) < sizeof(hdr)) {
|
|
|
|
+ printk(KERN_ERR
|
|
|
|
+ "%s: Not enough headroom for 802.2 headers %d\n",
|
|
|
|
+ dev->name, skb_headroom(skb));
|
|
|
|
+ goto drop;
|
|
}
|
|
}
|
|
- } else { /* IEEE 802.3 frame */
|
|
|
|
- data_off = HERMES_802_3_OFFSET;
|
|
|
|
|
|
+ eh = (struct ethhdr *) skb_push(skb, sizeof(hdr));
|
|
|
|
+ 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)
|
|
}
|
|
}
|
|
|
|
|
|
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,
|
|
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)
|
|
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,
|
|
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,
|
|
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)
|
|
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
|
|
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)
|
|
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);
|
|
}
|
|
}
|