[Prism54-devel] WPA Supplicant support for Prism54 driver

Jouni Malinen jkmaline@cc.hut.fi
Fri, 9 Jan 2004 21:27:13 -0800


--jho1yZJdad60DJr+
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

I have been looking into adding WPA Supplicant support for Prism54
driver using wpa_supplicant that I originally implemented for Host AP
driver (Prism2/2.5/3). I've attached a patch to add support for
reporting WPA IEs in the scan results using a format that wpa_supplicant
understands. This shows up as a custom text string in 'iwlist eth# scan'
output.

WPA client mode requires that mlmeautolevel is set to
DOT11_MLME_EXTENDED in order to receive Beacon and Probe Response frames
with payload and to add WPA IE into (Re)Association Request. The
attached patch enables this mode when WPA mode is enabled (iwpriv eth#
set_wpa 1). However, this does not seem to work in the way I expected.

Has anyone used DOT11_MLME_EXTENDED or DOT11_MLME_INTERMEDIATE in client
mode? It looks like the card is able to authenticate with an AP
(DOT11_OID_AUTHENTICATEEX is received with a success code), but for some
reason, association intent is not reported with DOT11_OID_ASSOCIATEEX.
The firmware just continues trying to authenticate again after some
time. This happens both when trying to use an AP in WPA mode and when
using just plaintext open AP. I don't believe anything special would
need to be done after authentication to trigger association, but maybe
something is not set properly and the firwmare decides not to associate.

After this issue with association gets resolved, wpa_supplicant should
be able to complete WPA key handshakes and set TKIP keys (some support
for this was apparently added couple of days ago into the driver). This
should be enough to get minimal WPA-PSK setup running with Prism54
driver.

-- 
Jouni Malinen                                            PGP id EFC895FA

--jho1yZJdad60DJr+
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="prism54-wpa-scan.patch"

diff -upr prism54-cvs20040110.orig/ksrc/isl_ioctl.c prism54-cvs20040110/ksrc/isl_ioctl.c
--- prism54-cvs20040110.orig/ksrc/isl_ioctl.c	2004-01-09 07:35:27.000000000 -0800
+++ prism54-cvs20040110/ksrc/isl_ioctl.c	2004-01-09 21:06:13.521168496 -0800
@@ -802,6 +802,8 @@ prism54_set_mode(struct net_device *ndev
 	/* the ACL code needs an intermediate mlmeautolevel */
 	if ((*uwrq == IW_MODE_MASTER) && (priv->acl.policy != MAC_POLICY_OPEN))
 		priv->mib.mlmeautolevel = DOT11_MLME_INTERMEDIATE;
+	if (priv->wpa)
+		priv->mib.mlmeautolevel = DOT11_MLME_EXTENDED;
 
 	/* update MIB copy now that requests succeeded
 	 * Note : there is a small window  for race here : 
@@ -1034,6 +1036,7 @@ prism54_translate_bss(struct net_device 
 {
 	struct iw_event iwe;	/* Temporary buffer */
 	short cap;
+	islpci_private *priv = ndev->priv;
 
 	/* The first entry must be the MAC address */
 	memcpy(iwe.u.ap_addr.sa_data, bss->address, 6);
@@ -1094,6 +1097,31 @@ prism54_translate_bss(struct net_device 
 	current_ev =
 	    iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_QUAL_LEN);
 
+#if WIRELESS_EXT > 14
+	if (priv->wpa) {
+		u8 wpa_ie[MAX_WPA_IE_LEN];
+		char *buf, *p;
+		size_t wpa_ie_len;
+		int i;
+
+		wpa_ie_len = prism54_wpa_ie_get(priv, bss->address, wpa_ie);
+		if (wpa_ie_len > 0 &&
+		    (buf = kmalloc(wpa_ie_len * 2 + 10, GFP_ATOMIC))) {
+			p = buf;
+			p += sprintf(p, "wpa_ie=");
+			for (i = 0; i < wpa_ie_len; i++) {
+				p += sprintf(p, "%02x", wpa_ie[i]);
+			}
+			memset(&iwe, 0, sizeof(iwe));
+			iwe.cmd = IWEVCUSTOM;
+			iwe.u.data.length = strlen(buf);
+			current_ev = iwe_stream_add_point(current_ev, end_buf,
+							  &iwe, buf);
+			kfree(buf);
+		}
+	}
+#endif /* WIRELESS_EXT > 14 */
+
 	return current_ev;
 }
 
@@ -1829,6 +1857,8 @@ prism54_set_policy(struct net_device *nd
 		priv->mib.mlmeautolevel = DOT11_MLME_INTERMEDIATE;
 	else
 		priv->mib.mlmeautolevel = CARD_DEFAULT_MLME_MODE;
+	if (priv->wpa)
+		priv->mib.mlmeautolevel = DOT11_MLME_EXTENDED;
 	/* restart the card with our new policy */
 	prism54_mib_init(priv);
 
@@ -1989,10 +2019,168 @@ link_changed(struct net_device *ndev, u3
 		send_simple_event(ndev->priv, "Link lost");
 }
 
+
+/* Beacon/ProbeResp payload header */
+struct ieee80211_beacon_phdr {
+	u8 timestamp[8];
+	u16 beacon_int;
+	u16 capab_info;
+} __attribute__ ((packed));
+
+#define WLAN_EID_GENERIC 0xdd
+static u8 wpa_oid[4] = { 0x00, 0x50, 0xf2, 1 };
+
+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
+#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
+
+
+
+void prism54_wpa_ie_add(islpci_private *priv, u8 *bssid,
+			u8 *wpa_ie, size_t wpa_ie_len)
+{
+	unsigned long flags;
+	struct list_head *ptr;
+	struct islpci_bss_wpa_ie *bss = NULL;
+
+	if (wpa_ie_len > MAX_WPA_IE_LEN)
+		wpa_ie_len = MAX_WPA_IE_LEN;
+
+	spin_lock_irqsave(&priv->slock, flags);
+
+	/* try to use existing entry */
+	list_for_each(ptr, &priv->bss_wpa_list) {
+		bss = list_entry(ptr, struct islpci_bss_wpa_ie, list);
+		if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0) {
+			list_move(&bss->list, &priv->bss_wpa_list);
+			break;
+		}
+		bss = NULL;
+	}
+
+	if (bss == NULL) {
+		/* add a new BSS entry; if max number of entries is already
+		 * reached, replace the least recently updated */
+		if (priv->num_bss_wpa >= MAX_BSS_WPA_IE_COUNT) {
+			bss = list_entry(priv->bss_wpa_list.prev,
+					 struct islpci_bss_wpa_ie, list);
+			list_del(&bss->list);
+		} else {
+			bss = kmalloc(sizeof(*bss), GFP_ATOMIC);
+			if (bss != NULL) {
+				priv->num_bss_wpa++;
+				memset(bss, 0, sizeof(*bss));
+			}
+		}
+		if (bss != NULL) {
+			memcpy(bss->bssid, bssid, ETH_ALEN);
+			list_add(&bss->list, &priv->bss_wpa_list);
+		}
+	}
+
+	if (bss != NULL) {
+		memcpy(bss->wpa_ie, wpa_ie, wpa_ie_len);
+		bss->wpa_ie_len = wpa_ie_len;
+		bss->last_update = jiffies;
+	} else {
+		printk(KERN_DEBUG "Failed to add BSS WPA entry for " MACSTR
+		       "\n", MAC2STR(bssid));
+	}
+
+	/* expire old entries from WPA list */
+	while (priv->num_bss_wpa > 0) {
+		bss = list_entry(priv->bss_wpa_list.prev,
+				 struct islpci_bss_wpa_ie, list);
+		if (!time_after(jiffies, bss->last_update + 60 * HZ))
+			break;
+
+		list_del(&bss->list);
+		priv->num_bss_wpa--;
+		kfree(bss);
+	}
+
+	spin_unlock_irqrestore(&priv->slock, flags);
+}
+
+
+size_t prism54_wpa_ie_get(islpci_private *priv, u8 *bssid, u8 *wpa_ie)
+{
+	unsigned long flags;
+	struct list_head *ptr;
+	struct islpci_bss_wpa_ie *bss = NULL;
+	size_t len = 0;
+
+	spin_lock_irqsave(&priv->slock, flags);
+	list_for_each(ptr, &priv->bss_wpa_list) {
+		bss = list_entry(ptr, struct islpci_bss_wpa_ie, list);
+		if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
+			break;
+		bss = NULL;
+	}
+	if (bss) {
+		len = bss->wpa_ie_len;
+		memcpy(wpa_ie, bss->wpa_ie, len);
+	}
+	spin_unlock_irqrestore(&priv->slock, flags);
+
+	return len;
+}
+
+
+void prism54_wpa_ie_init(islpci_private *priv)
+{
+	INIT_LIST_HEAD(&priv->bss_wpa_list);
+}
+
+
+void prism54_wpa_ie_clean(islpci_private *priv)
+{
+	struct list_head *ptr, *n;
+
+	list_for_each_safe(ptr, n, &priv->bss_wpa_list) {
+		struct islpci_bss_wpa_ie *bss;
+		bss = list_entry(ptr, struct islpci_bss_wpa_ie, list);
+		kfree(bss);
+	}
+}
+
+
+static void prism54_process_bss_data(islpci_private *priv, u32 oid, u8 *addr,
+				     u8 *payload, size_t len)
+{
+	struct ieee80211_beacon_phdr *hdr;
+	u8 *pos, *end;
+
+	if (!priv->wpa)
+		return;
+
+	hdr = (struct ieee80211_beacon_phdr *) payload;
+	pos = (u8 *) (hdr + 1);
+	end = payload + len;
+	while (pos < end) {
+		if (pos + 2 + pos[1] > end) {
+			printk(KERN_DEBUG "Parsing Beacon/ProbeResp failed "
+			       "for " MACSTR "\n", MAC2STR(addr));
+			return;
+		}
+		if (pos[0] == WLAN_EID_GENERIC && pos[1] >= 4 &&
+		    memcmp(pos + 2, wpa_oid, 4) == 0) {
+			prism54_wpa_ie_add(priv, addr, pos, pos[1] + 2);
+			return;
+		}
+		pos += 2 + pos[1];
+	}
+}
+
+
 int
 prism54_process_trap_helper(islpci_private *priv, u32 oid, char *data)
 {
 	struct obj_mlme *mlme = (struct obj_mlme *) data;
+	size_t len;
+	u8 *payload, *pos = (u8 *) (mlme + 1);
+
+	len = pos[0] | (pos[1] << 8); /* little endian data length */
+	payload = pos + 2;
 
 	/* I think all trapable objects are listed here.
 	 * Some oids have a EX version. The difference is that they are emitted
@@ -2066,6 +2254,8 @@ prism54_process_trap_helper(islpci_priva
 		break;
 
 	case DOT11_OID_BEACON:
+		prism54_process_bss_data(priv, oid, mlme->address,
+					 payload, len);
 		send_formatted_event(priv,
 				     "Received a beacon from an unkown AP",
 				     mlme, 0);
@@ -2073,6 +2263,8 @@ prism54_process_trap_helper(islpci_priva
 
 	case DOT11_OID_PROBE:
 		/* we received a probe from a client. */
+		prism54_process_bss_data(priv, oid, mlme->address,
+					 payload, len);
 		send_formatted_event(priv, "Received a probe from client", mlme,
 				     0);
 		break;
@@ -2149,6 +2341,36 @@ prism54_ioctl(struct net_device *ndev, s
 	return -EOPNOTSUPP;
 }
 
+
+int
+prism54_set_wpa(struct net_device *ndev, struct iw_request_info *info,
+		__u32 *uwrq, char *extra)
+{
+	islpci_private *priv = ndev->priv;
+
+	if (down_interruptible(&priv->mib.sem))
+		return -ERESTARTSYS;
+
+	priv->wpa = *uwrq;
+	if (priv->wpa)
+		priv->mib.mlmeautolevel = DOT11_MLME_EXTENDED;
+	prism54_mib_init(priv);
+
+	up(&priv->mib.sem);
+
+	return 0;
+}
+
+
+int
+prism54_get_wpa(struct net_device *ndev, struct iw_request_info *info,
+		__u32 *uwrq, char *extra)
+{
+	islpci_private *priv = ndev->priv;
+	*uwrq = priv->wpa;
+	return 0;
+}
+
 #if WIRELESS_EXT > 12
 
 static const iw_handler prism54_handler[] = {
@@ -2228,6 +2450,9 @@ static const iw_handler prism54_handler[
 
 #define PRISM54_KICK_ALL   SIOCIWFIRSTPRIV+12
 
+#define PRISM54_GET_WPA	   SIOCIWFIRSTPRIV+13
+#define PRISM54_SET_WPA	   SIOCIWFIRSTPRIV+14
+
 static const struct iw_priv_args prism54_private_args[] = {
 /*{ cmd, set_args, get_args, name } */
 	{PRISM54_RESET, 0, 0, "reset"},
@@ -2246,7 +2471,11 @@ static const struct iw_priv_args prism54
 	 "delMac"},
 	{PRISM54_KICK_MAC, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,
 	 "kickMac"},
-	{PRISM54_KICK_ALL, 0, 0, "kickAll"}
+	{PRISM54_KICK_ALL, 0, 0, "kickAll"},
+	{ PRISM54_GET_WPA, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	  "get_wpa" },
+	{ PRISM54_SET_WPA, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0,
+	  "set_wpa" },
 };
 
 static const iw_handler prism54_private_handler[] = {
@@ -2263,6 +2492,8 @@ static const iw_handler prism54_private_
 	(iw_handler) prism54_kick_mac,
 	(iw_handler) NULL,
 	(iw_handler) prism54_kick_all,
+	(iw_handler) prism54_get_wpa,
+	(iw_handler) prism54_set_wpa,
 };
 
 const struct iw_handler_def prism54_handler_def = {
diff -upr prism54-cvs20040110.orig/ksrc/isl_ioctl.h prism54-cvs20040110/ksrc/isl_ioctl.h
--- prism54-cvs20040110.orig/ksrc/isl_ioctl.h	2004-01-07 13:50:41.000000000 -0800
+++ prism54-cvs20040110/ksrc/isl_ioctl.h	2004-01-09 21:06:13.522168344 -0800
@@ -46,6 +46,13 @@ void prism54_acl_clean(struct islpci_acl
 
 void prism54_process_trap(islpci_private *);
 
+void prism54_wpa_ie_init(islpci_private *priv);
+void prism54_wpa_ie_clean(islpci_private *priv);
+void prism54_wpa_ie_add(islpci_private *priv, u8 *bssid,
+			u8 *wpa_ie, size_t wpa_ie_len);
+size_t prism54_wpa_ie_get(islpci_private *priv, u8 *bssid, u8 *wpa_ie);
+
+
 int prism54_set_mac_address(struct net_device *, void *);
 
 int prism54_ioctl(struct net_device *, struct ifreq *, int);
diff -upr prism54-cvs20040110.orig/ksrc/islpci_dev.c prism54-cvs20040110/ksrc/islpci_dev.c
--- prism54-cvs20040110.orig/ksrc/islpci_dev.c	2004-01-08 16:28:59.000000000 -0800
+++ prism54-cvs20040110/ksrc/islpci_dev.c	2004-01-09 21:06:13.523168192 -0800
@@ -618,6 +618,7 @@ islpci_free_memory(islpci_private *priv)
 
 	/* Free the acces control list */
 	prism54_acl_clean(&priv->acl);
+	prism54_wpa_ie_clean(priv);
 
 	return 0;
 }
@@ -723,6 +724,7 @@ islpci_setup(struct pci_dev *pdev)
 	priv->stats_timestamp = 0;
 
 	prism54_acl_init(&priv->acl);
+	prism54_wpa_ie_init(priv);
 
 	/* allocate various memory areas */
 	if (islpci_alloc_memory(priv))
diff -upr prism54-cvs20040110.orig/ksrc/islpci_dev.h prism54-cvs20040110/ksrc/islpci_dev.h
--- prism54-cvs20040110.orig/ksrc/islpci_dev.h	2004-01-09 07:35:27.000000000 -0800
+++ prism54-cvs20040110/ksrc/islpci_dev.h	2004-01-09 21:06:13.523168192 -0800
@@ -115,6 +115,16 @@ struct islpci_acl {
    struct semaphore sem;   /* accessed in ioctls and trap_work */
 };
 
+#define MAX_BSS_WPA_IE_COUNT 64
+#define MAX_WPA_IE_LEN 64
+struct islpci_bss_wpa_ie {
+	struct list_head list;
+	unsigned long last_update;
+	u8 bssid[ETH_ALEN];
+	u8 wpa_ie[MAX_WPA_IE_LEN];
+	size_t wpa_ie_len;
+};
+
 typedef struct {
 	spinlock_t slock;	/* generic spinlock; */
 
@@ -196,6 +206,10 @@ typedef struct {
 	volatile int state_off;		/* enumeration of off-state, if 0 then
 				 * we're not in any off-state */
 
+	int wpa; /* WPA mode enabled */
+	struct list_head bss_wpa_list;
+	int num_bss_wpa;
+
 #ifdef CONFIG_PRISM54_WDS
 	struct wds_priv wdsp;
 #endif

--jho1yZJdad60DJr+--