changeset 4126:31652d91f33e

PSARC 2006/046 Wireless WPA Supplicant PSARC 2007/223 Wireless WPA Supplicant Addendum 6363273 Need access to WPA/TKIP functionality
author zf162725
date Fri, 27 Apr 2007 09:21:03 -0700
parents a506428a3590
children 64886a16cf93
files usr/src/Makefile.lint usr/src/cmd/Makefile usr/src/cmd/cmd-inet/lib/nwamd/structures.h usr/src/cmd/cmd-inet/lib/nwamd/wireless.c usr/src/cmd/cmd-inet/usr.lib/Makefile usr/src/cmd/cmd-inet/usr.lib/wpad/Makefile usr/src/cmd/cmd-inet/usr.lib/wpad/README usr/src/cmd/cmd-inet/usr.lib/wpad/driver.h usr/src/cmd/cmd-inet/usr.lib/wpad/driver_wifi.c usr/src/cmd/cmd-inet/usr.lib/wpad/eloop.c usr/src/cmd/cmd-inet/usr.lib/wpad/eloop.h usr/src/cmd/cmd-inet/usr.lib/wpad/l2_packet.c usr/src/cmd/cmd-inet/usr.lib/wpad/l2_packet.h usr/src/cmd/cmd-inet/usr.lib/wpad/wpa.c usr/src/cmd/cmd-inet/usr.lib/wpad/wpa.xml usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_enc.c usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_enc.h usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_impl.h usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_supplicant.c usr/src/cmd/dladm/dladm.c usr/src/lib/libdladm/Makefile.com usr/src/lib/libdladm/common/libdllink.h usr/src/lib/libdladm/common/libdlwlan.c usr/src/lib/libdladm/common/libdlwlan.h usr/src/lib/libdladm/common/mapfile-vers usr/src/lib/libdladm/common/secobj.c usr/src/lib/libsecdb/auth_attr.txt usr/src/lib/libsecdb/help/auths/Makefile usr/src/lib/libsecdb/help/auths/SmfWpaStates.html usr/src/lib/libsecdb/prof_attr.txt usr/src/lib/libsecdb/user_attr.txt usr/src/pkgdefs/Makefile usr/src/pkgdefs/SUNW0on/prototype_com usr/src/pkgdefs/SUNWcsu/prototype_com usr/src/pkgdefs/SUNWwpar/Makefile usr/src/pkgdefs/SUNWwpar/depend usr/src/pkgdefs/SUNWwpar/pkginfo.tmpl usr/src/pkgdefs/SUNWwpar/prototype_com usr/src/pkgdefs/SUNWwpar/prototype_i386 usr/src/pkgdefs/SUNWwpar/prototype_sparc usr/src/pkgdefs/SUNWwpau/Makefile usr/src/pkgdefs/SUNWwpau/depend usr/src/pkgdefs/SUNWwpau/pkginfo.tmpl usr/src/pkgdefs/SUNWwpau/prototype_com usr/src/pkgdefs/SUNWwpau/prototype_i386 usr/src/pkgdefs/SUNWwpau/prototype_sparc usr/src/pkgdefs/etc/exception_list_i386 usr/src/pkgdefs/etc/exception_list_sparc usr/src/uts/common/Makefile.files usr/src/uts/common/inet/wifi_ioctl.h usr/src/uts/common/io/ath/ath_aux.c usr/src/uts/common/io/ath/ath_impl.h usr/src/uts/common/io/ath/ath_main.c usr/src/uts/common/io/dld/dld_drv.c usr/src/uts/common/io/ipw/ipw2100.c usr/src/uts/common/io/mac/plugins/mac_wifi.c usr/src/uts/common/io/net80211/net80211.c usr/src/uts/common/io/net80211/net80211_crypto.c usr/src/uts/common/io/net80211/net80211_crypto_ccmp.c usr/src/uts/common/io/net80211/net80211_crypto_tkip.c usr/src/uts/common/io/net80211/net80211_impl.h usr/src/uts/common/io/net80211/net80211_input.c usr/src/uts/common/io/net80211/net80211_ioctl.c usr/src/uts/common/io/net80211/net80211_node.c usr/src/uts/common/io/net80211/net80211_output.c usr/src/uts/common/net/Makefile usr/src/uts/common/net/wpa.h usr/src/uts/common/sys/dld.h usr/src/uts/common/sys/ethernet.h usr/src/uts/common/sys/mac_wifi.h usr/src/uts/common/sys/net80211.h usr/src/uts/common/sys/net80211_crypto.h usr/src/uts/common/sys/net80211_proto.h
diffstat 73 files changed, 8709 insertions(+), 250 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/Makefile.lint	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/Makefile.lint	Fri Apr 27 09:21:03 2007 -0700
@@ -78,6 +78,7 @@
 	cmd/cmd-inet/usr.lib/mipagent \
 	cmd/cmd-inet/usr.lib/pppoe \
 	cmd/cmd-inet/usr.lib/slpd \
+	cmd/cmd-inet/usr.lib/wpad \
 	cmd/cmd-inet/usr.lib/wanboot \
 	cmd/cmd-inet/usr.sadm \
 	cmd/cmd-inet/usr.sbin \
--- a/usr/src/cmd/Makefile	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/cmd/Makefile	Fri Apr 27 09:21:03 2007 -0700
@@ -808,6 +808,7 @@
 	cmd-inet/usr.lib/in.timed	\
 	cmd-inet/usr.lib/inetd		\
 	cmd-inet/usr.lib/slpd		\
+	cmd-inet/usr.lib/wpad		\
 	cmd-inet/usr.sbin		\
 	cmd-inet/usr.sbin/in.ftpd	\
 	cmd-inet/usr.sbin/in.rdisc	\
--- a/usr/src/cmd/cmd-inet/lib/nwamd/structures.h	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/structures.h	Fri Apr 27 09:21:03 2007 -0700
@@ -160,9 +160,9 @@
 	char *essid;
 	char *bssid;
 	char *signal_strength;
-	char *raw_wepkey;
-	dladm_wlan_wepkey_t *cooked_wepkey;
-	boolean_t need_wepkey;
+	char *raw_key;
+	dladm_wlan_key_t *cooked_key;
+	dladm_wlan_secmode_t sec_mode;
 	char *wl_if_name;
 };
 
--- a/usr/src/cmd/cmd-inet/lib/nwamd/wireless.c	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/wireless.c	Fri Apr 27 09:21:03 2007 -0700
@@ -36,7 +36,7 @@
  * finish and if one of the WiFi interfaces is chosen to be active, the
  * code will pop up a window showing the scan results and wait for the
  * user's input on which AP to connect to and then complete the AP
- * connection and IP interface set up.  WEP is supported to connect to
+ * connection and IP interface set up.  WEP/WPA is supported to connect to
  * those APs which require it.  The code also maintains a list of known
  * WiFi APs in the file KNOWN_WIFI_NETS.  Whenever the code successfully
  * connects to an AP, the AP's ESSID/BSSID will be added to that file.
@@ -104,6 +104,13 @@
 #include "functions.h"
 #include "variables.h"
 
+#define	WLAN_ENC(sec)						\
+	((sec == DLADM_WLAN_SECMODE_WPA ? "WPA" : 		\
+	(sec == DLADM_WLAN_SECMODE_WEP ? "WEP" : "none")))
+
+#define	NEED_ENC(sec)						\
+	(sec == DLADM_WLAN_SECMODE_WPA || sec == DLADM_WLAN_SECMODE_WEP)
+
 static pthread_mutex_t wifi_mutex;
 static pthread_mutexattr_t wifi_mutex_attr;
 static pthread_mutex_t wifi_init_mutex = PTHREAD_MUTEX_INITIALIZER;
@@ -129,12 +136,14 @@
 static uint_t wireless_lan_count = 0; /* allocated */
 static uint_t wireless_lan_used = 0; /* used entries */
 
-static int wepkey_string_to_secobj_value(char *, uint8_t *, uint_t *);
-static int store_wepkey(struct wireless_lan *);
-static dladm_wlan_wepkey_t *retrieve_wepkey(const char *, const char *);
+static int key_string_to_secobj_value(char *, uint8_t *, uint_t *,
+    dladm_secobj_class_t);
+static int store_key(struct wireless_lan *);
+static dladm_wlan_key_t *retrieve_key(const char *, const char *,
+    dladm_secobj_class_t);
 
 static boolean_t add_wlan_entry(struct interface *, char *, char *, char *,
-    boolean_t);
+    dladm_wlan_secmode_t);
 static boolean_t already_in_visited_wlan_list(const struct wireless_lan *);
 static boolean_t check_wlan(const char *, const char *);
 static boolean_t connect_or_autoconf(struct wireless_lan *, const char *);
@@ -143,7 +152,7 @@
 static boolean_t find_wlan_entry(struct interface *, char *, char *);
 static void free_wireless_lan(struct wireless_lan *);
 static struct wireless_lan *get_specific_lan(void);
-static void get_user_wepkey(struct wireless_lan *);
+static void get_user_key(struct wireless_lan *);
 static char *get_zenity_response(const char *);
 static boolean_t wlan_autoconf(const char *ifname);
 static int zenity_height(int);
@@ -178,27 +187,30 @@
  * wlan is expected to be non-NULL.
  */
 static void
-get_user_wepkey(struct wireless_lan *wlan)
+get_user_key(struct wireless_lan *wlan)
 {
 	char zenity_cmd[1024];
 	char buf[1024];
 	FILE *zcptr;
+	dladm_secobj_class_t class;
 
 	/*
-	 * First, test if we have wepkey stored as secobj. If so,
+	 * First, test if we have key stored as secobj. If so,
 	 * no need to prompt for it.
 	 */
-	wlan->cooked_wepkey = retrieve_wepkey(wlan->essid, wlan->bssid);
-	if (wlan->cooked_wepkey != NULL) {
-		dprintf("get_user_wepkey: retrieve_wepkey() returns non NULL");
+	class = (wlan->sec_mode == DLADM_WLAN_SECMODE_WEP ?
+	    DLADM_SECOBJ_CLASS_WEP : DLADM_SECOBJ_CLASS_WPA);
+	wlan->cooked_key = retrieve_key(wlan->essid, wlan->bssid, class);
+	if (wlan->cooked_key != NULL) {
+		dprintf("get_user_key: retrieve_key() returns non NULL");
 		return;
 	}
 
 	(void) snprintf(zenity_cmd, sizeof (zenity_cmd),
 	    "%s --entry --text=\"%s %s\""
 	    " --title=\"%s\" --hide-text", ZENITY,
-	    gettext("Enter WEP key for WiFi network"), wlan->essid,
-	    gettext("Enter WEP key"));
+	    gettext("Enter key for WiFi network"), wlan->essid,
+	    gettext("Enter key"));
 
 	if (!valid_graphical_user(B_TRUE))
 		return;
@@ -206,17 +218,17 @@
 	zcptr = popen(zenity_cmd, "r");
 	if (zcptr != NULL) {
 		if (fgets(buf, sizeof (buf), zcptr) != NULL) {
-			wlan->raw_wepkey = strdup(buf);
-			if (wlan->raw_wepkey != NULL) {
-				/* Store WEP key persistently */
-				if (store_wepkey(wlan) != 0) {
+			wlan->raw_key = strdup(buf);
+			if (wlan->raw_key != NULL) {
+				/* Store key persistently */
+				if (store_key(wlan) != 0) {
 					syslog(LOG_ERR,
-					    "get_user_wepkey: failed to store"
-					    " user specified WEP key");
+					    "get_user_key: failed to store"
+					    " user specified key");
 				}
 			} else {
 				syslog(LOG_ERR,
-				    "get_user_wepkey: strdup failed");
+				    "get_user_key: strdup failed");
 			}
 		}
 		(void) pclose(zcptr);
@@ -261,17 +273,17 @@
 	wlp->bssid = NULL;
 	free(wlp->signal_strength);
 	wlp->signal_strength = NULL;
-	free(wlp->raw_wepkey);
-	wlp->raw_wepkey = NULL;
-	free(wlp->cooked_wepkey);
-	wlp->cooked_wepkey = NULL;
+	free(wlp->raw_key);
+	wlp->raw_key = NULL;
+	free(wlp->cooked_key);
+	wlp->cooked_key = NULL;
 	free(wlp->wl_if_name);
 	wlp->wl_if_name = NULL;
 }
 
 static boolean_t
 add_wlan_entry(struct interface *intf, char *essid, char *bssid,
-    char *signal_strength, boolean_t wep)
+    char *signal_strength, dladm_wlan_secmode_t sec)
 {
 	int n;
 
@@ -300,9 +312,9 @@
 	wlans[n].bssid = strdup(bssid);
 	wlans[n].signal_strength = strdup(signal_strength);
 	wlans[n].wl_if_name = strdup(intf->if_name);
-	wlans[n].need_wepkey = wep;
-	wlans[n].raw_wepkey = NULL;
-	wlans[n].cooked_wepkey = NULL;
+	wlans[n].sec_mode = sec;
+	wlans[n].raw_key = NULL;
+	wlans[n].cooked_key = NULL;
 	if (wlans[n].essid == NULL || wlans[n].bssid == NULL ||
 	    wlans[n].signal_strength == NULL || wlans[n].wl_if_name == NULL) {
 		syslog(LOG_ERR, "add_wlan_entry: strdup failed");
@@ -435,8 +447,7 @@
 static boolean_t
 get_scan_results(void *arg, dladm_wlan_attr_t *attrp)
 {
-
-	boolean_t 	wep;
+	dladm_wlan_secmode_t	sec;
 	char		essid_name[DLADM_STRSIZE];
 	char		bssid_name[DLADM_STRSIZE];
 	char		strength[DLADM_STRSIZE];
@@ -445,10 +456,10 @@
 	(void) dladm_wlan_bssid2str(&attrp->wa_bssid, bssid_name);
 	(void) dladm_wlan_strength2str(&attrp->wa_strength, strength);
 
-	wep = (attrp->wa_secmode == DLADM_WLAN_SECMODE_WEP);
+	sec = attrp->wa_secmode;
 
 	if (!find_wlan_entry(arg, essid_name, bssid_name) &&
-	    add_wlan_entry(arg, essid_name, bssid_name, strength, wep)) {
+	    add_wlan_entry(arg, essid_name, bssid_name, strength, sec)) {
 		return (B_TRUE);
 	}
 	return (B_FALSE);
@@ -536,32 +547,50 @@
 }
 
 /*
- * Below are functions used to handle storage/retrieval of WEP keys
+ * Below are functions used to handle storage/retrieval of keys
  * for a given WLAN. The keys are stored/retrieved using dladm_set_secobj()
  * and dladm_get_secobj().
  */
 
 /*
- * Convert wepkey hexascii string to raw secobj value. This
+ * Convert key hexascii string to raw secobj value. This
  * code is very similar to convert_secobj() in dladm.c, it would
  * be good to have a libdladm function to convert values.
  */
 static int
-wepkey_string_to_secobj_value(char *buf, uint8_t *obj_val, uint_t *obj_lenp)
+key_string_to_secobj_value(char *buf, uint8_t *obj_val, uint_t *obj_lenp,
+    dladm_secobj_class_t class)
 {
 	size_t buf_len = strlen(buf);
 
-	dprintf("before: wepkey_string_to_secobj_value: buf_len = %d", buf_len);
+	dprintf("before: key_string_to_secobj_value: buf_len = %d", buf_len);
 	if (buf_len == 0) {
 		syslog(LOG_ERR,
-		    "wepkey_string_to_secobj_value: empty WEP key");
+		    "key_string_to_secobj_value: empty key");
 		return (-1);
 	}
 
 	if (buf[buf_len - 1] == '\n')
 		buf[--buf_len] = '\0';
 
-	dprintf("after: wepkey_string_to_secobj_value: buf_len = %d", buf_len);
+	dprintf("after: key_string_to_secobj_value: buf_len = %d", buf_len);
+
+	if (class == DLADM_SECOBJ_CLASS_WPA) {
+		/*
+		 * Per IEEE802.11i spec, the Pre-shared key (PSK) length should
+		 * be between 8 and 63.
+		 */
+		if (buf_len < 8 || buf_len > 63) {
+			syslog(LOG_ERR,
+			    "key_string_to_secobj_value:"
+			    " invalid WPA key length: buf_len = %d", buf_len);
+			return (-1);
+		}
+		(void) memcpy(obj_val, buf, (uint_t)buf_len);
+		*obj_lenp = buf_len;
+		return (0);
+	}
+
 	switch (buf_len) {
 	case 5:		/* ASCII key sizes */
 	case 13:
@@ -573,7 +602,7 @@
 		if (hexascii_to_octet(buf, (uint_t)buf_len, obj_val, obj_lenp)
 		    != 0) {
 			syslog(LOG_ERR,
-			    "wepkey_string_to_secobj_value: invalid WEP key");
+			    "key_string_to_secobj_value: invalid WEP key");
 			return (-1);
 		}
 		break;
@@ -583,13 +612,13 @@
 		    hexascii_to_octet(buf + 2, (uint_t)buf_len - 2, obj_val,
 		    obj_lenp) != 0) {
 			syslog(LOG_ERR,
-			    "wepkey_string_to_secobj_value: invalid WEP key");
+			    "key_string_to_secobj_value: invalid WEP key");
 			return (-1);
 		}
 		break;
 	default:
 		syslog(LOG_ERR,
-		    "wepkey_string_to_secobj_value: invalid WEP key length");
+		    "key_string_to_secobj_value: invalid WEP key length");
 		return (-1);
 	}
 	return (0);
@@ -617,33 +646,36 @@
 }
 
 static int
-store_wepkey(struct wireless_lan *wlan)
+store_key(struct wireless_lan *wlan)
 {
 	uint8_t obj_val[DLADM_SECOBJ_VAL_MAX];
 	uint_t obj_len = sizeof (obj_val);
 	char obj_name[DLADM_SECOBJ_NAME_MAX];
 	dladm_status_t status;
 	char errmsg[DLADM_STRSIZE];
+	dladm_secobj_class_t class;
 
 	/*
-	 * Name wepkey object for this WLAN so it can be later retrieved
+	 * Name key object for this WLAN so it can be later retrieved
 	 * (name is unique for each ESSID/BSSID combination).
 	 */
 	set_key_name(wlan->essid, wlan->bssid, obj_name, sizeof (obj_name));
-	dprintf("store_wepkey: obj_name is %s", obj_name);
+	dprintf("store_key: obj_name is %s", obj_name);
 
-	if (wepkey_string_to_secobj_value(wlan->raw_wepkey, obj_val, &obj_len)
-	    != 0) {
+	class = (wlan->sec_mode == DLADM_WLAN_SECMODE_WEP ?
+	    DLADM_SECOBJ_CLASS_WEP : DLADM_SECOBJ_CLASS_WPA);
+	if (key_string_to_secobj_value(wlan->raw_key, obj_val, &obj_len,
+	    class) != 0) {
 		/* above function logs internally on failure */
 		return (-1);
 	}
 
-	status = dladm_set_secobj(obj_name, DLADM_SECOBJ_CLASS_WEP,
+	status = dladm_set_secobj(obj_name, class,
 	    obj_val, obj_len,
 	    DLADM_OPT_CREATE | DLADM_OPT_PERSIST | DLADM_OPT_TEMP);
 	if (status != DLADM_STATUS_OK) {
-		syslog(LOG_ERR, "store_wepkey: could not create secure object "
-		    "'%s' for wepkey: %s", obj_name,
+		syslog(LOG_ERR, "store_key: could not create secure object "
+		    "'%s' for key: %s", obj_name,
 		    dladm_status2str(status, errmsg));
 		return (-1);
 	}
@@ -654,77 +686,84 @@
 	 * besides just copying the value, so it is simpler just to call
 	 * the retrieve function instead of doing it all here.
 	 *
-	 * Since we just stored the key, retrieve_wepkey() "shouldn't"
+	 * Since we just stored the key, retrieve_key() "shouldn't"
 	 * fail.  If it does fail, it's not the end of the world; a NULL
-	 * value for wlan->cooked_wepkey simply means this particular
+	 * value for wlan->cooked_key simply means this particular
 	 * attempt to connect will fail, and alternative connection
 	 * options will be used.
 	 */
-	wlan->cooked_wepkey = retrieve_wepkey(wlan->essid, wlan->bssid);
+	wlan->cooked_key = retrieve_key(wlan->essid, wlan->bssid, class);
 	return (0);
 }
 
 /*
- * retrieve_wepkey returns NULL if no wepkey was recovered from dladm
+ * retrieve_key returns NULL if no key was recovered from libdladm
  */
-static dladm_wlan_wepkey_t *
-retrieve_wepkey(const char *essid, const char *bssid)
+static dladm_wlan_key_t *
+retrieve_key(const char *essid, const char *bssid, dladm_secobj_class_t req)
 {
 	dladm_status_t status;
 	char errmsg[DLADM_STRSIZE];
-	dladm_wlan_wepkey_t *cooked_wepkey;
+	dladm_wlan_key_t *cooked_key;
 	dladm_secobj_class_t class;
 
 	/*
-	 * Newly-allocated wepkey must be freed by caller, or by
-	 * subsequent call to retrieve_wepkey().
+	 * Newly-allocated key must be freed by caller, or by
+	 * subsequent call to retrieve_key().
 	 */
-	if ((cooked_wepkey = malloc(sizeof (dladm_wlan_wepkey_t))) == NULL) {
-		syslog(LOG_ERR, "retrieve_wepkey: malloc failed");
+	if ((cooked_key = malloc(sizeof (dladm_wlan_key_t))) == NULL) {
+		syslog(LOG_ERR, "retrieve_key: malloc failed");
 		return (NULL);
 	}
 
-	/* Set name appropriately to retrieve wepkey for this WLAN */
-	set_key_name(essid, bssid, cooked_wepkey->wk_name,
+	/* Set name appropriately to retrieve key for this WLAN */
+	set_key_name(essid, bssid, cooked_key->wk_name,
 	    DLADM_SECOBJ_NAME_MAX);
-	dprintf("retrieve_wepkey: len = %d, object = %s\n",
-	    strlen(cooked_wepkey->wk_name), cooked_wepkey->wk_name);
-	cooked_wepkey->wk_len = DLADM_SECOBJ_NAME_MAX;
-	cooked_wepkey->wk_idx = 1;
+	dprintf("retrieve_key: len = %d, object = %s\n",
+	    strlen(cooked_key->wk_name), cooked_key->wk_name);
+	cooked_key->wk_len = DLADM_SECOBJ_NAME_MAX;
+	cooked_key->wk_idx = 1;
 
 	/* Try the kernel first, then fall back to persistent storage. */
-	status = dladm_get_secobj(cooked_wepkey->wk_name, &class,
-	    cooked_wepkey->wk_val, &cooked_wepkey->wk_len,
+	status = dladm_get_secobj(cooked_key->wk_name, &class,
+	    cooked_key->wk_val, &cooked_key->wk_len,
 	    DLADM_OPT_TEMP);
 	if (status != DLADM_STATUS_OK) {
-		dprintf("retrieve_wepkey: dladm_get_secobj(TEMP) failed: %s",
+		dprintf("retrieve_key: dladm_get_secobj(TEMP) failed: %s",
 		    dladm_status2str(status, errmsg));
-		status = dladm_get_secobj(cooked_wepkey->wk_name, &class,
-		    cooked_wepkey->wk_val, &cooked_wepkey->wk_len,
+		status = dladm_get_secobj(cooked_key->wk_name, &class,
+		    cooked_key->wk_val, &cooked_key->wk_len,
 		    DLADM_OPT_PERSIST);
 	}
 
 	switch (status) {
 	case DLADM_STATUS_OK:
-		dprintf("retrieve_wepkey: dladm_get_secobj succeeded: len %d",
-		    cooked_wepkey->wk_len);
+		dprintf("retrieve_key: dladm_get_secobj succeeded: len %d",
+		    cooked_key->wk_len);
 		break;
 	case DLADM_STATUS_NOTFOUND:
 		/*
 		 * We do not want an error in the case that the secobj
 		 * is not found, since we then prompt for it.
 		 */
-		free(cooked_wepkey);
+		free(cooked_key);
 		return (NULL);
 	default:
-		syslog(LOG_ERR, "retrieve_wepkey: could not get wepkey "
-		    "from secure object '%s': %s", cooked_wepkey->wk_name,
+		syslog(LOG_ERR, "retrieve_key: could not get key "
+		    "from secure object '%s': %s", cooked_key->wk_name,
 		    dladm_status2str(status, errmsg));
-		free(cooked_wepkey);
+		free(cooked_key);
 		return (NULL);
 	}
 
-	return (cooked_wepkey);
+	if (class != req) {	/* the key mismatch */
+		syslog(LOG_ERR, "retrieve_key: key type mismatch"
+		    " from secure object '%s'", cooked_key->wk_name);
+		free(cooked_key);
+		return (NULL);
+	}
+
+	return (cooked_key);
 }
 
 /* Create the KNOWN_WIFI_NETS using info from the interface list.  */
@@ -860,7 +899,7 @@
 connect_chosen_lan(struct wireless_lan *reqlan, const char *ifname)
 {
 	uint_t	keycount;
-	dladm_wlan_wepkey_t *key;
+	dladm_wlan_key_t *key;
 	dladm_wlan_attr_t attr;
 	dladm_status_t status;
 	uint_t flags = DLADM_WLAN_CONNECT_NOSCAN;
@@ -897,14 +936,14 @@
 		attr.wa_valid |= DLADM_WLAN_ATTR_BSSID;
 	}
 
-	/* First check for the wepkey */
-	if (reqlan->need_wepkey) {
-		get_user_wepkey(reqlan);
-		if (reqlan->cooked_wepkey == NULL)
+	/* First check for the key */
+	if (NEED_ENC(reqlan->sec_mode)) {
+		get_user_key(reqlan);
+		if (reqlan->cooked_key == NULL)
 			return (B_FALSE);
 		attr.wa_valid |= DLADM_WLAN_ATTR_SECMODE;
-		attr.wa_secmode = DLADM_WLAN_SECMODE_WEP;
-		key = reqlan->cooked_wepkey;
+		attr.wa_secmode = reqlan->sec_mode;
+		key = reqlan->cooked_key;
 		keycount = 1;
 		dprintf("connect_chosen_lan: retrieved key");
 	} else {
@@ -1023,7 +1062,7 @@
 			buflen += snprintf(endbuf, sizeof (buf) - buflen,
 			    "%d '%s' %s %s '%s' ", j,
 			    lanlist[i].essid, lanlist[i].bssid,
-			    lanlist[i].need_wepkey ? "WEP" : "none",
+			    WLAN_ENC(lanlist[i].sec_mode),
 			    lanlist[i].signal_strength);
 			endbuf = buf + buflen;
 		}
@@ -1072,12 +1111,12 @@
 	}
 	dprintf("get_user_preference() returned essid %s, bssid %s, encr %s",
 	    reqlan->essid, STRING(reqlan->bssid),
-	    reqlan->need_wepkey ? "WEP" : "none");
+	    WLAN_ENC(reqlan->sec_mode));
 
-	/* set wepkey before first time connection */
-	if (reqlan->need_wepkey && reqlan->raw_wepkey == NULL &&
-	    reqlan->cooked_wepkey == NULL)
-		get_user_wepkey(reqlan);
+	/* set key before first time connection */
+	if (NEED_ENC(reqlan->sec_mode) && reqlan->raw_key == NULL &&
+	    reqlan->cooked_key == NULL)
+		get_user_key(reqlan);
 
 	/*
 	 * now attempt to connect to selection, backing
@@ -1137,15 +1176,15 @@
 				    sizeof (buf) - buflen,
 				    "%d '%s' %s %s '%s' ",
 				    i, wlp->essid, wlp->bssid,
-				    wlp->need_wepkey ? "WEP" : gettext("none"),
+				    WLAN_ENC(wlp->sec_mode),
 				    wlp->signal_strength);
 				endbuf = buf + buflen;
 			}
 			list[i-1].essid = wlp->essid;
 			list[i-1].bssid = wlp->bssid;
-			list[i-1].need_wepkey = wlp->need_wepkey;
-			list[i-1].raw_wepkey = wlp->raw_wepkey;
-			list[i-1].cooked_wepkey = wlp->cooked_wepkey;
+			list[i-1].sec_mode = wlp->sec_mode;
+			list[i-1].raw_key = wlp->raw_key;
+			list[i-1].cooked_key = wlp->cooked_key;
 			list[i-1].signal_strength = wlp->signal_strength;
 			list[i-1].wl_if_name = wlp->wl_if_name;
 		}
@@ -1343,17 +1382,17 @@
 	if ((sel->bssid != NULL) && ((wlp->bssid = strdup(sel->bssid)) == NULL))
 		goto dup_error;
 
-	wlp->need_wepkey = sel->need_wepkey;
+	wlp->sec_mode = sel->sec_mode;
 
-	if ((sel->raw_wepkey != NULL) &&
-	    ((wlp->raw_wepkey = strdup(sel->raw_wepkey)) == NULL))
+	if ((sel->raw_key != NULL) &&
+	    ((wlp->raw_key = strdup(sel->raw_key)) == NULL))
 		goto dup_error;
 
-	if (sel->cooked_wepkey != NULL) {
-		wlp->cooked_wepkey = malloc(sizeof (dladm_wlan_wepkey_t));
-		if (wlp->cooked_wepkey == NULL)
+	if (sel->cooked_key != NULL) {
+		wlp->cooked_key = malloc(sizeof (dladm_wlan_key_t));
+		if (wlp->cooked_key == NULL)
 			goto dup_error;
-		*(wlp->cooked_wepkey) = *(sel->cooked_wepkey);
+		*(wlp->cooked_key) = *(sel->cooked_key);
 	}
 
 	if ((sel->signal_strength != NULL) &&
@@ -1365,7 +1404,7 @@
 		goto dup_error;
 
 	dprintf("selected: %s, %s, %s, '%s', %s", wlp->essid,
-	    STRING(wlp->bssid), wlp->need_wepkey ? "WEP" : "none",
+	    STRING(wlp->bssid), WLAN_ENC(wlp->sec_mode),
 	    STRING(wlp->signal_strength), STRING(wlp->wl_if_name));
 
 	free(response);
@@ -1414,13 +1453,18 @@
 	wlp->essid = response;
 
 	(void) snprintf(specify_str, sizeof (specify_str), ZENITY
-	    " --list --title=\"%s\" --text=\"%s\" --column=\"%s\" none wep",
+	    " --list --title=\"%s\" --text=\"%s\" --column=\"%s\" none wep wpa",
 	    gettext("Security"), gettext("Enter security"),
 	    gettext("Type"));
 
 	response = get_zenity_response(specify_str);
-	if (response != NULL && strcmp(response, "wep") == 0)
-		wlp->need_wepkey = B_TRUE;
+	wlp->sec_mode = DLADM_WLAN_SECMODE_NONE;
+	if (response != NULL) {
+		if (strcmp(response, "wep") == 0)
+			wlp->sec_mode = DLADM_WLAN_SECMODE_WEP;
+		else if (strcmp(response, "wpa") == 0)
+			wlp->sec_mode = DLADM_WLAN_SECMODE_WPA;
+	}
 
 	free(response);
 	return (wlp);
@@ -1513,9 +1557,9 @@
 		}
 		new_wlan->wifi_net->essid = strdup(cur_wlans[i].essid);
 		new_wlan->wifi_net->bssid = strdup(cur_wlans[i].bssid);
-		new_wlan->wifi_net->raw_wepkey = NULL;
-		new_wlan->wifi_net->cooked_wepkey = NULL;
-		new_wlan->wifi_net->need_wepkey = cur_wlans[i].need_wepkey;
+		new_wlan->wifi_net->raw_key = NULL;
+		new_wlan->wifi_net->cooked_key = NULL;
+		new_wlan->wifi_net->sec_mode = cur_wlans[i].sec_mode;
 		new_wlan->wifi_net->signal_strength =
 		    strdup(cur_wlans[i].signal_strength);
 		new_wlan->wifi_net->wl_if_name =
--- a/usr/src/cmd/cmd-inet/usr.lib/Makefile	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/cmd/cmd-inet/usr.lib/Makefile	Fri Apr 27 09:21:03 2007 -0700
@@ -2,9 +2,8 @@
 # CDDL HEADER START
 #
 # The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
 #
 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 # or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # ident	"%Z%%M%	%I%	%E% SMI"
@@ -28,7 +27,8 @@
 
 SUBDIRS=	dhcp dsvclockd in.chargend in.daytimed \
 		in.discardd in.echod in.dhcpd in.mpathd in.ndpd \
-		in.ripngd in.timed inetd mipagent ncaconfd pppoe slpd wanboot
+		in.ripngd in.timed inetd mipagent ncaconfd pppoe slpd wanboot \
+		wpad
 
 MSGSUBDIRS=	dsvclockd in.dhcpd inetd ncaconfd wanboot
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/Makefile	Fri Apr 27 09:21:03 2007 -0700
@@ -0,0 +1,64 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+PROG =		wpad
+MANIFEST =	wpa.xml
+OBJS =		wpa_supplicant.o wpa.o wpa_enc.o eloop.o \
+		driver_wifi.o l2_packet.o
+SRCS = 		$(OBJS:%.o=%.c)
+
+include	../../../Makefile.cmd
+
+ROOTMANIFESTDIR = $(ROOTSVCNETWORK)
+
+LDFLAGS   += 	-L/usr/sfw/lib -R/usr/sfw/lib
+LDLIBS    += 	-ldladm -ldlpi
+all install := LDLIBS += -lcrypto
+
+CPPFLAGS  += 	-I/usr/sfw/include
+LINTFLAGS += 	-u -erroff=E_BAD_PTR_CAST_ALIGN
+
+.KEEP_STATE:
+
+all:		$(PROG)
+
+$(PROG):	$(OBJS)
+		$(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+		$(POST_PROCESS)
+
+include ../Makefile.lib
+
+install:	all $(ROOTLIBINETPROG) $(ROOTMANIFEST)
+
+check:		$(CHKMANIFEST)
+
+clean:
+		$(RM) $(OBJS)
+
+lint:		lint_SRCS
+
+include ../../../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/README	Fri Apr 27 09:21:03 2007 -0700
@@ -0,0 +1,772 @@
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+WPA Supplicant
+==============
+
+Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+All Rights Reserved.
+
+Sun elects to license this software under the BSD license.
+
+
+License
+-------
+
+BSD license:
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+3. Neither the name(s) of the above-listed copyright holder(s) nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+Features
+--------
+
+Supported WPA/IEEE 802.11i features:
+- WPA-PSK ("WPA-Personal")
+- WPA with EAP (e.g., with RADIUS authentication server) ("WPA-Enterprise")
+  Following authentication methods are supported with an integrate IEEE 802.1X
+  Supplicant:
+  * EAP-TLS
+  * EAP-PEAP/MSCHAPv2 (both PEAPv0 and PEAPv1)
+  * EAP-PEAP/TLS (both PEAPv0 and PEAPv1)
+  * EAP-PEAP/GTC (both PEAPv0 and PEAPv1)
+  * EAP-PEAP/OTP (both PEAPv0 and PEAPv1)
+  * EAP-PEAP/MD5-Challenge (both PEAPv0 and PEAPv1)
+  * EAP-TTLS/EAP-MD5-Challenge
+  * EAP-TTLS/EAP-GTC
+  * EAP-TTLS/EAP-OTP
+  * EAP-TTLS/EAP-MSCHAPv2
+  * EAP-TTLS/EAP-TLS
+  * EAP-TTLS/MSCHAPv2
+  * EAP-TTLS/MSCHAP
+  * EAP-TTLS/PAP
+  * EAP-TTLS/CHAP
+  * EAP-SIM
+  * LEAP (note: only with WEP keys, i.e., not for WPA; in addition, LEAP
+	requires special support from the driver for IEEE 802.11
+	authentication)
+  (following methods are supported, but since they do not generate keying
+   material, they cannot be used with WPA or IEEE 802.1X WEP keying)
+  * EAP-MD5-Challenge 
+  * EAP-MSCHAPv2
+  * EAP-GTC
+  * EAP-OTP
+  Alternatively, an external program, e.g., Xsupplicant, can be used for EAP
+  authentication.
+- key management for CCMP, TKIP, WEP104, WEP40
+- RSN/WPA2 (IEEE 802.11i)
+  * pre-authentication
+  * PMKSA caching
+
+
+
+Requirements
+------------
+
+Current hardware/software requirements:
+- Linux kernel 2.4.x or 2.6.x
+- Linux Wireless Extensions v15 or newer
+- drivers:
+	Host AP driver for Prism2/2.5/3 (development snapshot/v0.2.x)
+	in Managed mode ('iwconfig wlan0 mode managed'). Please note that
+	station firmware version needs to be 1.7.0 or newer to work in
+	WPA mode.
+
+	Linuxant DriverLoader (http://www.linuxant.com/driverloader/)
+	with Windows NDIS driver for your wlan card supporting WPA.
+
+	Agere Systems Inc. Linux Driver
+	(http://www.agere.com/support/drivers/)
+	Please note that the driver interface file (driver_hermes.c) and
+	hardware specific include files are not included in the
+	wpa_supplicant distribution. You will need to copy these from the
+	source package of the Agere driver.
+
+	madwifi driver for cards based on Atheros chip set (ar521x)
+	(http://sourceforge.net/projects/madwifi/)
+	Please note that you will need to modify the wpa_supplicant Makefile
+	to use correct path for madwifi driver root directory
+	(CFLAGS += -I../madwifi/wpa line in Makefile).
+
+	ATMEL AT76C5XXx driver for USB and PCMCIA cards
+	(http://atmelwlandriver.sourceforge.net/).
+
+	Linux ndiswrapper (http://ndiswrapper.sourceforge.net/) with
+	Windows NDIS driver.
+
+	In theory, any driver that supports Linux wireless extensions can be
+	used with IEEE 802.1X (i.e., not WPA) when using ap_scan=0 option in
+	configuration file.
+
+wpa_supplicant was designed to be portable for different drivers and
+operating systems. Hopefully, support for more wlan cards will be
+added in the future. See developer.txt for more information about the
+design of wpa_supplicant and porting to other drivers. One main goal
+is to add full WPA/WPA2 support to Linux wireless extensions to allow
+new drivers to be supported without having to implement new
+driver-specific interface code in wpa_supplicant.
+
+Optional libraries for layer2 packet processing:
+- libpcap (tested with 0.7.2, most relatively recent versions assumed to work,
+	this is likely to be available with most distributions,
+	http://tcpdump.org/)
+- libdnet (tested with v1.4, most versions assumed to work,
+	http://libdnet.sourceforge.net/)
+
+These libraries are _not_ used in the default build. Instead, internal
+Linux specific implementation is used. libpcap/libdnet are more
+portable and they can be used by modifying Makefile (define
+USE_DNET_PCAP and link with these libraries).
+
+
+Optional libraries for EAP-TLS, EAP-PEAP, and EAP-TTLS:
+- openssl (tested with 0.9.7c and 0.9.7d, assumed to work with most
+  relatively recent versions; this is likely to be available with most
+  distributions, http://www.openssl.org/)
+
+This library is only needed when EAP-TLS, EAP-PEAP, or EAP-TTLS
+support is enabled. WPA-PSK mode does not require this or EAPOL/EAP
+implementation. A configuration file, .config, for compilation is
+needed to enable IEEE 802.1X/EAPOL and EAP methods. Note that EAP-MD5,
+EAP-GTC, EAP-OTP, and EAP-MSCHAPV2 cannot be used alone with WPA, so
+they should only be enabled if testing the EAPOL/EAP state
+machines. However, there can be used as inner authentication
+algorithms with EAP-PEAP and EAP-TTLS.
+
+See Building and installing section below for more detailed
+information about the wpa_supplicant build time configuration.
+
+
+
+WPA
+---
+
+The original security mechanism of IEEE 802.11 standard was not
+designed to be strong and has proved to be insufficient for most
+networks that require some kind of security. Task group I (Security)
+of IEEE 802.11 working group (http://www.ieee802.org/11/) has worked
+to address the flaws of the base standard and has in practice
+completed its work in May 2004. The IEEE 802.11i amendment to the IEEE
+802.11 standard was approved in June 2004 and this amendment is likely
+to be published in July 2004.
+
+Wi-Fi Alliance (http://www.wi-fi.org/) used a draft version of the
+IEEE 802.11i work (draft 3.0) to define a subset of the security
+enhancements that can be implemented with existing wlan hardware. This
+is called Wi-Fi Protected Access<TM> (WPA). This has now become a
+mandatory component of interoperability testing and certification done
+by Wi-Fi Alliance. Wi-Fi provides information about WPA at its web
+site (http://www.wi-fi.org/OpenSection/protected_access.asp).
+
+IEEE 802.11 standard defined wired equivalent privacy (WEP) algorithm
+for protecting wireless networks. WEP uses RC4 with 40-bit keys,
+24-bit initialization vector (IV), and CRC32 to protect against packet
+forgery. All these choice have proved to be insufficient: key space is
+too small against current attacks, RC4 key scheduling is insufficient
+(beginning of the pseudorandom stream should be skipped), IV space is
+too small and IV reuse makes attacks easier, there is no replay
+protection, and non-keyed authentication does not protect against bit
+flipping packet data.
+
+WPA is an intermediate solution for the security issues. It uses
+temporal key integrity protocol (TKIP) to replace WEP. TKIP is a
+compromise on strong security and possibility to use existing
+hardware. It still uses RC4 for the encryption like WEP, but with
+per-packet RC4 keys. In addition, it implements replay protection,
+keyed packet authentication mechanism (Michael MIC).
+
+Keys can be managed using two different mechanisms. WPA can either use
+an external authentication server (e.g., RADIUS) and EAP just like
+IEEE 802.1X is using or pre-shared keys without need for additional
+servers. Wi-Fi calls these "WPA-Enterprise" and "WPA-Personal",
+respectively. Both mechanisms will generate a master session key for
+the Authenticator (AP) and Supplicant (client station).
+
+WPA implements a new key handshake (4-Way Handshake and Group Key
+Handshake) for generating and exchanging data encryption keys between
+the Authenticator and Supplicant. This handshake is also used to
+verify that both Authenticator and Supplicant know the master session
+key. These handshakes are identical regardless of the selected key
+management mechanism (only the method for generating master session
+key changes).
+
+
+
+IEEE 802.11i / WPA2
+-------------------
+
+The design for parts of IEEE 802.11i that were not included in WPA has
+finished (May 2004) and this amendment to IEEE 802.11 was approved in
+June 2004. Wi-Fi Alliance is using the final IEEE 802.11i as a new
+version of WPA called WPA2. This includes, e.g., support for more
+robust encryption algorithm (CCMP: AES in Counter mode with CBC-MAC)
+to replace TKIP and optimizations for handoff (reduced number of
+messages in initial key handshake, pre-authentication, key caching).
+
+Some wireless LAN vendors are already providing support for CCMP in
+their WPA products. There is no "official" interoperability
+certification for CCMP and/or mixed modes using both TKIP and CCMP, so
+some interoperability issues can be expected even though many
+combinations seem to be working with equipment from different vendors.
+Certification for WPA2 is likely to start during the second half of
+2004.
+
+
+
+wpa_supplicant
+--------------
+
+wpa_supplicant is an implementation of the WPA Supplicant component,
+i.e., the part that runs in the client stations. It implements WPA key
+negotiation with a WPA Authenticator and EAP authentication with
+Authentication Server. In addition, it controls the roaming and IEEE
+802.11 authentication/association of the wlan driver.
+
+wpa_supplicant is designed to be a "daemon" program that runs in the
+background and acts as the backend component controlling the wireless
+connection. wpa_supplicant supports separate frontend programs and an
+example text-based frontend, wpa_cli, is included with wpa_supplicant.
+
+Following steps are used when associating with an AP using WPA:
+
+- wpa_supplicant requests the kernel driver to scan neighboring BSSes
+- wpa_supplicant selects a BSS based on its configuration
+- wpa_supplicant requests the kernel driver to associate with the chosen
+  BSS
+- If WPA-EAP: integrated IEEE 802.1X Supplicant or external Xsupplicant
+  completes EAP authentication with the authentication server (proxied
+  by the Authenticator in the AP)
+- If WPA-EAP: master key is received from the IEEE 802.1X Supplicant
+- If WPA-PSK: wpa_supplicant uses PSK as the master session key
+- wpa_supplicant completes WPA 4-Way Handshake and Group Key Handshake
+  with the Authenticator (AP)
+- wpa_supplicant configures encryption keys for unicast and broadcast
+- normal data packets can be transmitted and received
+
+
+
+Building and installing
+-----------------------
+
+In order to be able to build wpa_supplicant, you will first need to
+select which parts of it will be included. This is done by creating a
+build time configuration file, .config, in the wpa_supplicant root
+directory. Configuration options are text lines using following
+format: CONFIG_<option>=y. Lines starting with # are considered
+comments and are ignored.
+
+The build time configuration can be used to select only the needed
+features and limit the binary size and requirements for external
+libraries. The main configuration parts are the selection of which
+driver interfaces (e.g., hostap, madwifi, ..) and which authentication
+methods (e.g., EAP-TLS, EAP-PEAP, ..) are included.
+
+Following build time configuration options are used to control IEEE
+802.1X/EAPOL and EAP state machines and all EAP methods. Including
+TLS, PEAP, or TTLS will require linking wpa_supplicant with openssl
+library for TLS implementation.
+
+CONFIG_IEEE8021X_EAPOL=y
+CONFIG_EAP_MD5=y
+CONFIG_MSCHAPV2=y
+CONFIG_EAP_TLS=y
+CONFIG_EAP_PEAP=y
+CONFIG_EAP_TTLS=y
+CONFIG_EAP_GTC=y
+CONFIG_EAP_OTP=y
+CONFIG_EAP_SIM=y
+CONFIG_EAP_LEAP=y
+
+Following option can be used to include GSM SIM/USIM interface for GSM
+authentication algorithm (for EAP-SIM). This requires pcsc-lite
+(http://www.linuxnet.com/) for smart card access.
+
+CONFIG_PCSC=y
+
+Following options can be added to .config to select which driver
+interfaces are included. Prism54.org driver is not yet complete and
+Hermes driver interface needs to be downloaded from Agere (see above).
+Most Linux driver need to include CONFIG_WIRELESS_EXTENSION.
+
+CONFIG_WIRELESS_EXTENSION=y
+CONFIG_DRIVER_HOSTAP=y
+CONFIG_DRIVER_PRISM54=y
+CONFIG_DRIVER_HERMES=y
+CONFIG_DRIVER_MADWIFI=y
+CONFIG_DRIVER_ATMEL=y
+CONFIG_DRIVER_WEXT=y
+CONFIG_DRIVER_NDISWRAPPER=y
+
+Following example includes all features and driver interfaces that are
+included in the wpa_supplicant package:
+
+CONFIG_DRIVER_HOSTAP=y
+CONFIG_DRIVER_PRISM54=y
+CONFIG_DRIVER_HERMES=y
+CONFIG_DRIVER_MADWIFI=y
+CONFIG_DRIVER_ATMEL=y
+CONFIG_DRIVER_WEXT=y
+CONFIG_DRIVER_NDISWRAPPER=y
+CONFIG_WIRELESS_EXTENSION=y
+CONFIG_IEEE8021X_EAPOL=y
+CONFIG_EAP_MD5=y
+CONFIG_MSCHAPV2=y
+CONFIG_EAP_TLS=y
+CONFIG_EAP_PEAP=y
+CONFIG_EAP_TTLS=y
+CONFIG_EAP_GTC=y
+CONFIG_EAP_OTP=y
+CONFIG_EAP_SIM=y
+CONFIG_EAP_LEAP=y
+CONFIG_PCSC=y
+
+EAP-PEAP and EAP-TTLS will automatically include configured EAP
+methods (MD5, OTP, GTC, MSCHAPV2) for inner authentication selection.
+
+
+After you have created a configuration file, you can build
+wpa_supplicant and wpa_cli with 'make' command. You may then install
+the binaries to a suitable system directory, e.g., /usr/local/bin.
+
+Example commands:
+
+# build wpa_supplicant and wpa_cli
+make
+# install binaries (this may need root privileges)
+cp wpa_cli wpa_supplicant /usr/local/bin
+
+
+You will need to make a configuration file, e.g.,
+/etc/wpa_supplicant.conf, with network configuration for the networks
+you are going to use. Configuration file section below includes
+explanation fo the configuration file format and includes various
+examples. Once the configuration is ready, you can test whether the
+configuration work by first running wpa_supplicant with following
+command to start it on foreground with debugging enabled:
+
+wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -d
+
+Assuming everything goes fine, you can start using following command
+to start wpa_supplicant on background without debugging:
+
+wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -B
+
+Please note that if you included more than one driver interface in the
+build time configuration (.config), you may need to specify which
+interface to use by including -D<driver name> option on the command
+line. See following section for more details on command line options
+for wpa_supplicant.
+
+
+
+Command line options
+--------------------
+
+usage:
+  wpa_supplicant [-BddehLqqvw] -i<ifname> -c<config file> [-D<driver>]
+
+options:
+  -B = run daemon in the background
+  -d = increase debugging verbosity (-dd even more)
+  -e = use external IEEE 802.1X Supplicant (e.g., xsupplicant)
+       (this disables the internal Supplicant)
+  -h = show this help text
+  -L = show license (GPL and BSD)
+  -q = decrease debugging verbosity (-qq even less)
+  -v = show version
+  -w = wait for interface to be added, if needed
+
+drivers:
+  hostap = Host AP driver (Intersil Prism2/2.5/3) [default]
+	(this can also be used with Linuxant DriverLoader)
+  prism54 = Prism54.org driver (Intersil Prism GT/Duette/Indigo)
+	not yet fully implemented
+  hermes = Agere Systems Inc. driver (Hermes-I/Hermes-II)
+  madwifi = MADWIFI 802.11 support (Atheros, etc.)
+  atmel = ATMEL AT76C5XXx (USB, PCMCIA)
+  wext = Linux wireless extensions (generic)
+  ndiswrapper = Linux ndiswrapper
+
+In most common cases, wpa_supplicant is started with
+
+wpa_supplicant -Bw -c/etc/wpa_supplicant.conf -iwlan0
+
+This makes the process fork into background and wait for the wlan0
+interface if it is not available at startup time.
+
+
+
+Configuration file
+------------------
+
+wpa_supplicant is configured using a text file that lists all accepted
+networks and security policies, including pre-shared keys. See
+example configuration file, wpa_supplicant.conf, for detailed
+information about the configuration format and supported fields.
+
+Changes to configuration file can be reloaded be sending SIGHUP signal
+to wpa_supplicant ('killall -HUP wpa_supplicant'). Similarily,
+reloading can be triggered with 'wpa_cli reconfigure' command.
+
+Configuration file can include one or more network blocks, e.g., one
+for each used SSID. wpa_supplicant will automatically select the best
+betwork based on the order of network blocks in the configuration
+file, network security level (WPA/WPA2 is prefered), and signal
+strength.
+
+Example configuration files for some common configurations:
+
+1) WPA-Personal (PSK) as home network and WPA-Enterprise with EAP-TLS as work
+   network
+
+# allow frontend (e.g., wpa_cli) to be used by all users in 'wheel' group
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+#
+# home network; allow all valid ciphers
+network={
+	ssid="home"
+	scan_ssid=1
+	key_mgmt=WPA-PSK
+	psk="very secret passphrase"
+}
+#
+# work network; use EAP-TLS with WPA; allow only CCMP and TKIP ciphers
+network={
+	ssid="work"
+	scan_ssid=1
+	key_mgmt=WPA-EAP
+	pairwise=CCMP TKIP
+	group=CCMP TKIP
+	eap=TLS
+	identity="user@example.com"
+	ca_cert="/etc/cert/ca.pem"
+	client_cert="/etc/cert/user.pem"
+	private_key="/etc/cert/user.prv"
+	private_key_passwd="password"
+}
+
+
+2) WPA-RADIUS/EAP-PEAP/MSCHAPv2 with RADIUS servers that use old peaplabel
+   (e.g., Funk Odyssey and SBR, Meetinghouse Aegis, Interlink RAD-Series)
+
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+network={
+	ssid="example"
+	scan_ssid=1
+	key_mgmt=WPA-EAP
+	eap=PEAP
+	identity="user@example.com"
+	password="foobar"
+	ca_cert="/etc/cert/ca.pem"
+	phase1="peaplabel=0"
+	phase2="auth=MSCHAPV2"
+}
+
+
+3) EAP-TTLS/EAP-MD5-Challenge configuration with anonymous identity for the
+   unencrypted use. Real identity is sent only within an encrypted TLS tunnel.
+
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+network={
+	ssid="example"
+	scan_ssid=1
+	key_mgmt=WPA-EAP
+	eap=TTLS
+	identity="user@example.com"
+	anonymous_identity="anonymous@example.com"
+	password="foobar"
+	ca_cert="/etc/cert/ca.pem"
+	phase2="auth=MD5"
+}
+
+
+4) IEEE 802.1X (i.e., no WPA) with dynamic WEP keys (require both unicast and
+   broadcast); use EAP-TLS for authentication
+
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+network={
+	ssid="1x-test"
+	scan_ssid=1
+	key_mgmt=IEEE8021X
+	eap=TLS
+	identity="user@example.com"
+	ca_cert="/etc/cert/ca.pem"
+	client_cert="/etc/cert/user.pem"
+	private_key="/etc/cert/user.prv"
+	private_key_passwd="password"
+	eapol_flags=3
+}
+
+
+5) Catch all example that allows more or less all configuration modes. The
+   configuration options are used based on what security policy is used in the
+   selected SSID. This is mostly for testing and is not recommended for normal
+   use.
+
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+network={
+	ssid="example"
+	scan_ssid=1
+	key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE
+	pairwise=CCMP TKIP
+	group=CCMP TKIP WEP104 WEP40
+	psk="very secret passphrase"
+	eap=TTLS PEAP TLS
+	identity="user@example.com"
+	password="foobar"
+	ca_cert="/etc/cert/ca.pem"
+	client_cert="/etc/cert/user.pem"
+	private_key="/etc/cert/user.prv"
+	private_key_passwd="password"
+	phase1="peaplabel=0"
+	ca_cert2="/etc/cert/ca2.pem"
+	client_cert2="/etc/cer/user.pem"
+	private_key2="/etc/cer/user.prv"
+	private_key2_passwd="password"
+}
+
+
+
+Certificates
+------------
+
+Some EAP authentication methods require use of certificates. EAP-TLS
+uses both server side and client certificates whereas EAP-PEAP and
+EAP-TTLS only require the server side certificate. When client
+certificate is used, a matching private key file has to also be
+included in configuration. If the private key uses a passphrase, this
+has to be configured in wpa_supplicant.conf ("private_key_passwd").
+
+wpa_supplicant supports X.509 certificates in PEM and DER
+formats. User certificate and private key can be included in the same
+file.
+
+If the user certificate and private key is received in PKCS#12/PFX
+format, they need to be converted to suitable PEM/DER format for
+wpa_supplicant. This can be done, e.g., with following commands:
+
+# convert client certificate and private key to PEM format
+openssl pkcs12 -in example.pfx -out user.pem -clcerts
+# convert CA certificate (if included in PFX file) to PEM format
+openssl pkcs12 -in example.pfx -out ca.pem -cacerts -nokeys
+
+
+
+wpa_cli
+-------
+
+wpa_cli is a text-based frontend program for interacting with
+wpa_supplicant. It is used to query current status, change
+configuration, trigger events, and request interactive user input.
+
+wpa_cli can show the current authentication status, selected security
+mode, dot11 and dot1x MIBs, etc. In addition, it can configuring some
+variables like EAPOL state machine parameters and trigger events like
+reassociation and IEEE 802.1X logoff/logon. wpa_cli provides a user
+interface to request authentication information, like username and
+password, if these are not included in the configuration. This can be
+used to implement, e.g., one-time-passwords or generic token card
+authentication where the authentication is based on a
+challenge-response that uses an external device for generating the
+response.
+
+The control interface of wpa_supplicant can be configured to allow
+non-root user access (ctrl_interface_group in the configuration
+file). This makes it possible to run wpa_cli with a normal user
+account.
+
+wpa_cli supports two modes: interactive and command line. Both modes
+share the same command set and the main difference is in interactive
+mode providing access to unsolicited messages (event messages,
+username/password requests).
+
+Interactive mode is started when wpa_cli is executed without including
+the command as a command line parameter. Commands are then entered on
+the wpa_cli prompt. In command line mode, the same commands are
+entered as command line arguments for wpa_cli.
+
+
+Interactive authentication parameters request
+
+When wpa_supplicant need authentication parameters, like username and
+password, which are not present in the configuration file, it sends a
+request message to all attached frontend programs, e.g., wpa_cli in
+interactive mode. wpa_cli shows these requests with
+"CTRL-REQ-<type>-<id>:<text>" prefix. <type> is IDENTITY, PASSWORD, or
+OTP (one-time-password). <id> is a unique identifier for the current
+network. <text> is description of the request. In case of OTP request,
+it includes the challenge from the authentication server.
+
+The reply to these requests can be given with 'identity', 'password',
+and 'otp' commands. <id> needs to be copied from the the matching
+request. 'password' and 'otp' commands can be used regardless of
+whether the request was for PASSWORD or OTP. The main difference
+between these two commands is that values given with 'password' are
+remembered as long as wpa_supplicant is running whereas values given
+with 'otp' are used only once and then forgotten, i.e., wpa_supplicant
+will ask frontend for a new value for every use. This can be used to
+implement one-time-password lists and generic token card -based
+authentication.
+
+Example request for password and a matching reply:
+
+CTRL-REQ-PASSWORD-1:Password needed for SSID foobar
+> password 1 mysecretpassword
+
+Example request for generic token card challenge-response:
+
+CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar
+> otp 2 9876
+
+
+wpa_cli commands
+
+  status = get current WPA/EAPOL/EAP status
+  mib = get MIB variables (dot1x, dot11)
+  help = show this usage help
+  interface [ifname] = show interfaces/select interface
+  level <debug level> = change debug level
+  license = show full wpa_cli license
+  logoff = IEEE 802.1X EAPOL state machine logoff
+  logon = IEEE 802.1X EAPOL state machine logon
+  set = set variables (shows list of variables when run without arguments)
+  pmksa = show PMKSA cache
+  reassociate = force reassociation
+  reconfigure = force wpa_supplicant to re-read its configuration file
+  preauthenticate <BSSID> = force preauthentication
+  identity <network id> <identity> = configure identity for an SSID
+  password <network id> <password> = configure password for an SSID
+  otp <network id> <password> = configure one-time-password for an SSID
+  quit = exit wpa_cli
+
+
+
+Integrating with pcmcia-cs/cardmgr scripts
+------------------------------------------
+
+wpa_supplicant needs to be running when using a wireless network with
+WPA. It can be started either from system startup scripts or from
+pcmcia-cs/cardmgr scripts (when using PC Cards). WPA handshake must be
+completed before data frames can be exchanged, so wpa_supplicant
+should be started before DHCP client.
+
+Command line option '-w' can be used if wpa_supplicant is started
+before the wireless LAN interface is present (e.g., before inserting
+the PC Card) or is not yet up.
+
+For example, following small changes to pcmcia-cs scripts can be used
+to enable WPA support:
+
+Add MODE="Managed" and WPA="y" to the network scheme in
+/etc/pcmcia/wireless.opts.
+
+Add the following block to the end of 'start' action handler in
+/etc/pcmcia/wireless:
+
+    if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then
+	/usr/local/bin/wpa_supplicant -Bw -c/etc/wpa_supplicant.conf \
+		-i$DEVICE
+    fi
+
+Add the following block to the end of 'stop' action handler (may need
+to be separated from other actions) in /etc/pcmcia/wireless:
+
+    if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then
+	killall wpa_supplicant
+    fi
+
+This will make cardmgr start wpa_supplicant when the card is plugged
+in. wpa_supplicant will wait until the interface is set up--either
+when a static IP address is configured or when DHCP client is
+started--and will then negotiate keys with the AP.
+
+
+
+Optional integration with Xsupplicant
+-------------------------------------
+
+wpa_supplicant has an integrated IEEE 802.1X Supplicant that supports
+most commonly used EAP methods. In addition, wpa_supplicant has an
+experimental interface for integrating it with Xsupplicant
+(http://www.open1x.org/) for the WPA with EAP authentication.
+
+Xsupplicant needs to be modified to send master session key to
+wpa_supplicant after successful EAP authentication. The included patch
+(xsupplicant.patch) shows the changes needed. This was merged into
+xsupplicant CVS on February 6, 2004, so any snapshot after that should
+have the needed functionality already included.
+
+When using WPA-EAP, both wpa_supplicant and Xsupplicant must be
+configured with the network security policy. See Xsupplicant documents
+for information about its configuration. Please also note, that a new
+command line option -W (enable WPA; added by xsupplicant.patch) must
+be used when starting xsupplicant.
+
+Example configuration for xsupplicant:
+
+network_list = all
+default_netname = jkm
+
+jkm
+{
+	type = wireless
+	allow_types = eap_peap
+	identity = <BEGIN_ID>jkm<END_ID>
+	eap-peap {
+		random_file = /dev/urandom
+		root_cert = /home/jkm/CA.pem
+		chunk_size = 1398
+		allow_types = eap_mschapv2
+		eap-mschapv2 {
+			username = <BEGIN_UNAME>jkm<END_UNAME>
+			password = <BEGIN_PASS>jkm<END_PASS>
+		}
+	}
+}
+
+
+Example configuration for wpa_supplicant:
+
+network={
+	ssid="jkm"
+	key_mgmt=WPA-EAP
+}
+
+
+Both wpa_supplicant and xsupplicant need to be started. Please remember
+to add '-W' option for xsupplicant in order to provide keying material
+for wpa_supplicant and '-e' option for wpa_supplicant to disable internal
+IEEE 802.1X implementation.
+
+wpa_supplicant -iwlan0 -cwpa_supplicant.conf -e
+xsupplicant -iwlan0 -cxsupplicant.conf -W
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/driver.h	Fri Apr 27 09:21:03 2007 -0700
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Sun elects to license this software under the BSD license.
+ * See README for more details.
+ */
+#ifndef __DRIVER_H
+#define	__DRIVER_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <libdlwlan.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+typedef enum { WPA_ALG_NONE, WPA_ALG_WEP, WPA_ALG_TKIP, WPA_ALG_CCMP } wpa_alg;
+typedef enum { CIPHER_NONE, CIPHER_WEP40, CIPHER_TKIP, CIPHER_CCMP,
+	CIPHER_WEP104 } wpa_cipher;
+typedef enum { KEY_MGMT_802_1X, KEY_MGMT_PSK, KEY_MGMT_NONE } wpa_key_mgmt;
+
+struct wpa_driver_ops {
+	int (*get_bssid)(const char *, char *);
+	int (*get_ssid)(const char *ifname, char *);
+	int (*set_wpa)(const char *, boolean_t);
+	int (*set_key)(const char *, wpa_alg, uint8_t *,
+	    int, boolean_t, uint8_t *, uint32_t, uint8_t *, uint32_t);
+	int (*scan)(const char *);
+	int (*get_scan_results)(const char *, dladm_wlan_ess_t *, uint32_t);
+	int (*disassociate)(const char *, int);
+	int (*associate)(const char *, const char *, uint8_t *, uint32_t);
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __DRIVER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/driver_wifi.c	Fri Apr 27 09:21:03 2007 -0700
@@ -0,0 +1,372 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2004, Sam Leffler <sam@errno.com>
+ * Sun elects to license this software under the BSD license.
+ * See README for more details.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stropts.h>
+#include <string.h>
+#include <stddef.h>
+
+#include "wpa_impl.h"
+#include "driver.h"
+
+#define	WPA_STATUS(status)	(status == DLADM_STATUS_OK? 0 : -1)
+
+/*
+ * get_bssid - get the current BSSID
+ * @ifname: interface name, e.g., wlan0
+ * @bssid: buffer for BSSID (IEEE80211_ADDR_LEN = 6 bytes)
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Query kernel driver for the current BSSID and copy it to @bssid.
+ * Setting @bssid to 00:00:00:00:00:00 is recommended if the STA is not
+ * associated.
+ */
+int
+wpa_driver_wifi_get_bssid(const char *ifname, char *bssid)
+{
+	int ret;
+	dladm_wlan_linkattr_t attr;
+	dladm_wlan_attr_t *wl_attrp;
+
+	ret = dladm_wlan_get_linkattr(ifname, &attr);
+	if (ret != DLADM_STATUS_OK)
+		return (-1);
+
+	wl_attrp = &attr.la_wlan_attr;
+	if ((attr.la_valid & DLADM_WLAN_LINKATTR_WLAN) == 0 ||
+	    (wl_attrp->wa_valid & DLADM_WLAN_ATTR_BSSID) == 0)
+		return (-1);
+
+	(void) memcpy(bssid, wl_attrp->wa_bssid.wb_bytes, DLADM_WLAN_BSSID_LEN);
+
+	wpa_printf(MSG_DEBUG, "wpa_driver_wifi_get_bssid: " MACSTR,
+	    MAC2STR((unsigned char *)bssid));
+
+	return (WPA_STATUS(ret));
+}
+
+/*
+ * get_ssid - get the current SSID
+ * @ifname: interface name, e.g., wlan0
+ * @ssid: buffer for SSID (at least 32 bytes)
+ *
+ * Returns: length of the SSID on success, -1 on failure
+ *
+ * Query kernel driver for the current SSID and copy it to @ssid.
+ * Returning zero is recommended if the STA is not associated.
+ */
+int
+wpa_driver_wifi_get_ssid(const char *ifname, char *ssid)
+{
+	int ret;
+	dladm_wlan_linkattr_t attr;
+	dladm_wlan_attr_t *wl_attrp;
+
+	ret = dladm_wlan_get_linkattr(ifname, &attr);
+	if (ret != DLADM_STATUS_OK)
+		return (-1);
+
+	wl_attrp = &attr.la_wlan_attr;
+	if ((attr.la_valid & DLADM_WLAN_LINKATTR_WLAN) == 0 ||
+	    (wl_attrp->wa_valid & DLADM_WLAN_ATTR_ESSID) == 0)
+		return (-1);
+
+	(void) memcpy(ssid, wl_attrp->wa_essid.we_bytes, MAX_ESSID_LENGTH);
+	ret = strlen(ssid);
+
+	wpa_printf(MSG_DEBUG, "wpa_driver_wifi_get_ssid: ssid=%s len=%d",
+	    ssid, ret);
+
+	return (ret);
+}
+
+static int
+wpa_driver_wifi_set_wpa_ie(const char *ifname,
+    uint8_t *wpa_ie, uint32_t wpa_ie_len)
+{
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "%s", "wpa_driver_wifi_set_wpa_ie");
+	ret = dladm_wlan_wpa_set_ie(ifname, wpa_ie, wpa_ie_len);
+
+	return (WPA_STATUS(ret));
+}
+
+/*
+ * set_wpa - enable/disable WPA support
+ * @ifname: interface name, e.g., wlan0
+ * @enabled: 1 = enable, 0 = disable
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Configure the kernel driver to enable/disable WPA support. This may
+ * be empty function, if WPA support is always enabled. Common
+ * configuration items are WPA IE (clearing it when WPA support is
+ * disabled), Privacy flag for capability field, roaming mode (need to
+ * allow wpa_supplicant to control roaming).
+ */
+static int
+wpa_driver_wifi_set_wpa(const char *ifname, boolean_t enabled)
+{
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "wpa_driver_wifi_set_wpa: enable=%d", enabled);
+
+	if (!enabled && wpa_driver_wifi_set_wpa_ie(ifname, NULL, 0) < 0)
+		return (-1);
+
+	ret = dladm_wlan_wpa_set_wpa(ifname, enabled);
+
+	return (WPA_STATUS(ret));
+}
+
+static int
+wpa_driver_wifi_del_key(const char *ifname, int key_idx, unsigned char *addr)
+{
+	int ret;
+	dladm_wlan_bssid_t bss;
+
+	wpa_printf(MSG_DEBUG, "%s: id=%d", "wpa_driver_wifi_del_key",
+	    key_idx);
+
+	(void) memcpy(bss.wb_bytes, addr, DLADM_WLAN_BSSID_LEN);
+	ret = dladm_wlan_wpa_del_key(ifname, key_idx, &bss);
+
+	return (WPA_STATUS(ret));
+}
+
+/*
+ * set_key - configure encryption key
+ * @ifname: interface name, e.g., wlan0
+ * @alg: encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP,
+ *	%WPA_ALG_TKIP, %WPA_ALG_CCMP); %WPA_ALG_NONE clears the key.
+ * @addr: address of the peer STA or ff:ff:ff:ff:ff:ff for
+ *	broadcast/default keys
+ * @key_idx: key index (0..3), always 0 for unicast keys
+ * @set_tx: configure this key as the default Tx key (only used when
+ *	driver does not support separate unicast/individual key
+ * @seq: sequence number/packet number, @seq_len octets, the next
+ *	packet number to be used for in replay protection; configured
+ *	for Rx keys (in most cases, this is only used with broadcast
+ *	keys and set to zero for unicast keys)
+ * @seq_len: length of the @seq, depends on the algorithm:
+ *	TKIP: 6 octets, CCMP: 6 octets
+ * @key: key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key,
+ *	8-byte Rx Mic Key
+ * @key_len: length of the key buffer in octets (WEP: 5 or 13,
+ *	TKIP: 32, CCMP: 16)
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Configure the given key for the kernel driver. If the driver
+ * supports separate individual keys (4 default keys + 1 individual),
+ * @addr can be used to determine whether the key is default or
+ * individual. If only 4 keys are supported, the default key with key
+ * index 0 is used as the individual key. STA must be configured to use
+ * it as the default Tx key (@set_tx is set) and accept Rx for all the
+ * key indexes. In most cases, WPA uses only key indexes 1 and 2 for
+ * broadcast keys, so key index 0 is available for this kind of
+ * configuration.
+ */
+static int
+wpa_driver_wifi_set_key(const char *ifname, wpa_alg alg,
+    unsigned char *addr, int key_idx,
+    boolean_t set_tx, uint8_t *seq, uint32_t seq_len,
+    uint8_t *key, uint32_t key_len)
+{
+	char *alg_name;
+	dladm_wlan_cipher_t cipher;
+	dladm_wlan_bssid_t bss;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "%s", "wpa_driver_wifi_set_key");
+	if (alg == WPA_ALG_NONE)
+		return (wpa_driver_wifi_del_key(ifname, key_idx, addr));
+
+	switch (alg) {
+	case WPA_ALG_WEP:
+		alg_name = "WEP";
+		cipher = DLADM_WLAN_CIPHER_WEP;
+		break;
+	case WPA_ALG_TKIP:
+		alg_name = "TKIP";
+		cipher = DLADM_WLAN_CIPHER_TKIP;
+		break;
+	case WPA_ALG_CCMP:
+		alg_name = "CCMP";
+		cipher = DLADM_WLAN_CIPHER_AES_CCM;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "wpa_driver_wifi_set_key:"
+		    " unknown/unsupported algorithm %d", alg);
+		return (-1);
+	}
+
+	wpa_printf(MSG_DEBUG, "wpa_driver_wifi_set_key: alg=%s key_idx=%d"
+	    " set_tx=%d seq_len=%d seq=%d key_len=%d",
+	    alg_name, key_idx, set_tx,
+	    seq_len, *(uint64_t *)seq, key_len);
+
+	if (seq_len > sizeof (uint64_t)) {
+		wpa_printf(MSG_DEBUG, "wpa_driver_wifi_set_key:"
+		    " seq_len %d too big", seq_len);
+		return (-1);
+	}
+	(void) memcpy(bss.wb_bytes, addr, DLADM_WLAN_BSSID_LEN);
+
+	ret = dladm_wlan_wpa_set_key(ifname, cipher, &bss, set_tx,
+	    *(uint64_t *)seq, key_idx, key, key_len);
+
+	return (WPA_STATUS(ret));
+}
+
+/*
+ * disassociate - request driver to disassociate
+ * @ifname: interface name, e.g., wlan0
+ * @reason_code: 16-bit reason code to be sent in the disassociation
+ * frame
+ *
+ * Return: 0 on success, -1 on failure
+ */
+static int
+wpa_driver_wifi_disassociate(const char *ifname, int reason_code)
+{
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "wpa_driver_wifi_disassociate");
+
+	ret = dladm_wlan_wpa_set_mlme(ifname, DLADM_WLAN_MLME_DISASSOC,
+	    reason_code, NULL);
+
+	return (WPA_STATUS(ret));
+}
+
+/*
+ * associate - request driver to associate
+ * @ifname: interface name, e.g., wlan0
+ * @bssid: BSSID of the selected AP
+ * @wpa_ie: WPA information element to be included in (Re)Association
+ *	Request (including information element id and length). Use of
+ *	this WPA IE is optional. If the driver generates the WPA IE, it
+ *	can use @pairwise_suite, @group_suite, and @key_mgmt_suite
+ *	to select proper algorithms. In this case, the driver has to
+ *	notify wpa_supplicant about the used WPA IE by generating an
+ *	event that the interface code will convert into EVENT_ASSOCINFO
+ *	data (see wpa_supplicant.h). When using WPA2/IEEE 802.11i,
+ *	@wpa_ie is used for RSN IE instead. The driver can determine
+ *	which version is used by looking at the first byte of the IE
+ *	(0xdd for WPA, 0x30 for WPA2/RSN).
+ * @wpa_ie_len: length of the @wpa_ie
+ *
+ * Return: 0 on success, -1 on failure
+ */
+static int
+wpa_driver_wifi_associate(const char *ifname, const char *bssid,
+    uint8_t *wpa_ie, uint32_t wpa_ie_len)
+{
+	int ret;
+	dladm_wlan_bssid_t bss;
+
+	wpa_printf(MSG_DEBUG, "wpa_driver_wifi_associate : "
+	    MACSTR, MAC2STR(bssid));
+
+	/*
+	 * NB: Don't need to set the freq or cipher-related state as
+	 * this is implied by the bssid which is used to locate
+	 * the scanned node state which holds it.
+	 */
+	if (wpa_driver_wifi_set_wpa_ie(ifname, wpa_ie, wpa_ie_len) < 0)
+		return (-1);
+
+	(void) memcpy(bss.wb_bytes, bssid, DLADM_WLAN_BSSID_LEN);
+	ret = dladm_wlan_wpa_set_mlme(ifname, DLADM_WLAN_MLME_ASSOC,
+	    0, &bss);
+
+	return (WPA_STATUS(ret));
+}
+
+/*
+ * scan - request the driver to initiate scan
+ * @ifname: interface name, e.g., wlan0
+ *
+ * Return: 0 on success, -1 on failure
+ *
+ * Once the scan results are ready, the driver should report scan
+ * results event for wpa_supplicant which will eventually request the
+ * results with wpa_driver_get_scan_results().
+ */
+static int
+wpa_driver_wifi_scan(const char *ifname)
+{
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "%s", "wpa_driver_wifi_scan");
+	/*
+	 * We force the state to INIT before calling ieee80211_new_state
+	 * to get ieee80211_begin_scan called.  We really want to scan w/o
+	 * altering the current state but that's not possible right now.
+	 */
+	(void) wpa_driver_wifi_disassociate(ifname,
+	    DLADM_WLAN_REASON_DISASSOC_LEAVING);
+
+	ret = dladm_wlan_scan(ifname, NULL, NULL);
+
+	wpa_printf(MSG_DEBUG, "%s: return", "wpa_driver_wifi_scan");
+	return (WPA_STATUS(ret));
+}
+
+/*
+ * get_scan_results - fetch the latest scan results
+ * @ifname: interface name, e.g., wlan0
+ * @results: pointer to buffer for scan results
+ * @max_size: maximum number of entries (buffer size)
+ *
+ * Return: number of scan result entries used on success, -1 on failure
+ *
+ * If scan results include more than @max_size BSSes, @max_size will be
+ * returned and the remaining entries will not be included in the
+ * buffer.
+ */
+int
+wpa_driver_wifi_get_scan_results(const char *ifname,
+    dladm_wlan_ess_t *results, uint32_t max_size)
+{
+	uint_t ret;
+
+	wpa_printf(MSG_DEBUG, "%s: interface name =%s max size=%d\n",
+		"wpa_driver_wifi_get_scan_results", ifname, max_size);
+
+	if (dladm_wlan_wpa_get_sr(ifname, results, max_size, &ret)
+	    != DLADM_STATUS_OK) {
+		return (-1);
+	}
+
+	return (ret);
+}
+
+struct wpa_driver_ops wpa_driver_wifi_ops = {
+	wpa_driver_wifi_get_bssid,
+	wpa_driver_wifi_get_ssid,
+	wpa_driver_wifi_set_wpa,
+	wpa_driver_wifi_set_key,
+	wpa_driver_wifi_scan,
+	wpa_driver_wifi_get_scan_results,
+	wpa_driver_wifi_disassociate,
+	wpa_driver_wifi_associate
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/eloop.c	Fri Apr 27 09:21:03 2007 -0700
@@ -0,0 +1,322 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Sun elects to license this software under the BSD license.
+ * See README for more details.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <poll.h>
+
+#include "eloop.h"
+
+static struct eloop_data eloop;
+/*
+ * Initialize global event loop data - must be called before any other eloop_*
+ * function. user_data is a pointer to global data structure and will be passed
+ * as eloop_ctx to signal handlers.
+ */
+void
+eloop_init(void *user_data)
+{
+	(void) memset(&eloop, 0, sizeof (eloop));
+	eloop.user_data = user_data;
+}
+
+/*
+ * Register handler for read event
+ */
+int
+eloop_register_read_sock(int sock,
+    void (*handler)(int sock, void *eloop_ctx,
+    void *sock_ctx), void *eloop_data, void *user_data)
+{
+	struct eloop_sock *tmp;
+
+	tmp = (struct eloop_sock *)realloc(eloop.readers,
+	    (eloop.reader_count + 1) * sizeof (struct eloop_sock));
+	if (tmp == NULL)
+		return (-1);
+
+	tmp[eloop.reader_count].sock = sock;
+	tmp[eloop.reader_count].eloop_data = eloop_data;
+	tmp[eloop.reader_count].user_data = user_data;
+	tmp[eloop.reader_count].handler = handler;
+	eloop.reader_count++;
+	eloop.readers = tmp;
+	if (sock > eloop.max_sock)
+		eloop.max_sock = sock;
+
+	return (0);
+}
+
+void
+eloop_unregister_read_sock(int sock)
+{
+	int i;
+
+	if (eloop.readers == NULL || eloop.reader_count == 0)
+		return;
+
+	for (i = 0; i < eloop.reader_count; i++) {
+		if (eloop.readers[i].sock == sock)
+			break;
+	}
+	if (i == eloop.reader_count)
+		return;
+	if (i != eloop.reader_count - 1) {
+		(void) memmove(&eloop.readers[i], &eloop.readers[i + 1],
+		    (eloop.reader_count - i - 1) *
+		    sizeof (struct eloop_sock));
+	}
+	eloop.reader_count--;
+}
+
+/*
+ * Register timeout routines
+ */
+int
+eloop_register_timeout(unsigned int secs, unsigned int usecs,
+    void (*handler)(void *eloop_ctx, void *timeout_ctx),
+    void *eloop_data, void *user_data)
+{
+	struct eloop_timeout *timeout, *tmp, *prev;
+
+	timeout = (struct eloop_timeout *)malloc(sizeof (*timeout));
+	if (timeout == NULL)
+		return (-1);
+	(void) gettimeofday(&timeout->time, NULL);
+	timeout->time.tv_sec += secs;
+	timeout->time.tv_usec += usecs;
+	while (timeout->time.tv_usec >= 1000000) {
+		timeout->time.tv_sec++;
+		timeout->time.tv_usec -= 1000000;
+	}
+	timeout->eloop_data = eloop_data;
+	timeout->user_data = user_data;
+	timeout->handler = handler;
+	timeout->next = NULL;
+
+	if (eloop.timeout == NULL) {
+		eloop.timeout = timeout;
+		return (0);
+	}
+
+	prev = NULL;
+	tmp = eloop.timeout;
+	while (tmp != NULL) {
+		if (timercmp(&timeout->time, &tmp->time, < /* */))
+			break;
+		prev = tmp;
+		tmp = tmp->next;
+	}
+
+	if (prev == NULL) {
+		timeout->next = eloop.timeout;
+		eloop.timeout = timeout;
+	} else {
+		timeout->next = prev->next;
+		prev->next = timeout;
+	}
+
+	return (0);
+}
+
+/*
+ * Cancel timeouts matching <handler,eloop_data,user_data>.
+ * ELOOP_ALL_CTX can be used as a wildcard for cancelling all timeouts
+ * regardless of eloop_data/user_data.
+ */
+void
+eloop_cancel_timeout(void (*handler)(void *eloop_ctx, void *sock_ctx),
+    void *eloop_data, void *user_data)
+{
+	struct eloop_timeout *timeout, *prev, *next;
+
+	prev = NULL;
+	timeout = eloop.timeout;
+	while (timeout != NULL) {
+		next = timeout->next;
+
+		if (timeout->handler == handler &&
+		    (timeout->eloop_data == eloop_data ||
+		    eloop_data == ELOOP_ALL_CTX) &&
+		    (timeout->user_data == user_data ||
+		    user_data == ELOOP_ALL_CTX)) {
+			if (prev == NULL)
+				eloop.timeout = next;
+			else
+				prev->next = next;
+			free(timeout);
+		} else
+			prev = timeout;
+
+		timeout = next;
+	}
+}
+
+static void eloop_handle_signal(int sig)
+{
+	int i;
+
+	eloop.signaled++;
+	for (i = 0; i < eloop.signal_count; i++) {
+		if (eloop.signals[i].sig == sig) {
+			eloop.signals[i].signaled++;
+			break;
+		}
+	}
+}
+
+static void eloop_process_pending_signals(void)
+{
+	int i;
+
+	if (eloop.signaled == 0)
+		return;
+	eloop.signaled = 0;
+
+	for (i = 0; i < eloop.signal_count; i++) {
+		if (eloop.signals[i].signaled) {
+			eloop.signals[i].signaled = 0;
+			eloop.signals[i].handler(eloop.signals[i].sig,
+			    eloop.user_data, eloop.signals[i].user_data);
+		}
+	}
+}
+
+/*
+ * Register handler for signal.
+ * Note: signals are 'global' events and there is no local eloop_data pointer
+ * like with other handlers. The (global) pointer given to eloop_init() will be
+ * used as eloop_ctx for signal handlers.
+ */
+int
+eloop_register_signal(int sig,
+    void (*handler)(int sig, void *eloop_ctx, void *signal_ctx),
+    void *user_data)
+{
+	struct eloop_signal *tmp;
+
+	tmp = (struct eloop_signal *)
+	    realloc(eloop.signals,
+	    (eloop.signal_count + 1) *
+	    sizeof (struct eloop_signal));
+	if (tmp == NULL)
+		return (-1);
+
+	tmp[eloop.signal_count].sig = sig;
+	tmp[eloop.signal_count].user_data = user_data;
+	tmp[eloop.signal_count].handler = handler;
+	tmp[eloop.signal_count].signaled = 0;
+	eloop.signal_count++;
+	eloop.signals = tmp;
+	(void) signal(sig, eloop_handle_signal);
+
+	return (0);
+}
+
+/*
+ * Start event loop and continue running as long as there are any registered
+ * event handlers.
+ */
+void
+eloop_run(void)
+{
+	struct pollfd pfds[MAX_POLLFDS];	/* array of polled fd */
+	int i, res;
+	int default_t, t;
+	struct timeval tv, now;
+
+	default_t = 5 * 1000;	/* 5 seconds */
+	while (!eloop.terminate &&
+		(eloop.timeout || eloop.reader_count > 0)) {
+		if (eloop.timeout) {
+			(void) gettimeofday(&now, NULL);
+			if (timercmp(&now, &eloop.timeout->time, < /* */))
+				/* LINTED E_CONSTANT_CONDITION */
+				timersub(&eloop.timeout->time, &now, &tv);
+			else
+				tv.tv_sec = tv.tv_usec = 0;
+		}
+
+		t = (eloop.timeout == NULL ?
+		    default_t : (tv.tv_sec * 1000 + tv.tv_usec / 1000));
+		for (i = 0; i < eloop.reader_count; i++) {
+			pfds[i].fd = eloop.readers[i].sock;
+			pfds[i].events = POLLIN | POLLPRI;
+		}
+		res = poll(pfds, eloop.reader_count, t);
+		if (res < 0 && errno != EINTR)
+			return;
+
+		eloop_process_pending_signals();
+
+		/* check if some registered timeouts have occurred */
+		if (eloop.timeout) {
+			struct eloop_timeout *tmp;
+
+			(void) gettimeofday(&now, NULL);
+			if (!timercmp(&now, &eloop.timeout->time, < /* */)) {
+				tmp = eloop.timeout;
+				eloop.timeout = eloop.timeout->next;
+				tmp->handler(tmp->eloop_data, tmp->user_data);
+				free(tmp);
+			}
+
+		}
+
+		if (res <= 0)
+			continue;
+
+		for (i = 0; i < eloop.reader_count; i++) {
+			if (pfds[i].revents) {
+				eloop.readers[i].handler(
+				    eloop.readers[i].sock,
+				    eloop.readers[i].eloop_data,
+				    eloop.readers[i].user_data);
+			}
+		}
+	}
+}
+
+/*
+ * Terminate event loop even if there are registered events.
+ */
+void
+eloop_terminate(void)
+{
+	eloop.terminate = 1;
+}
+
+
+/*
+ * Free any reserved resources. After calling eloop_destoy(), other eloop_*
+ * functions must not be called before re-running eloop_init().
+ */
+void
+eloop_destroy(void)
+{
+	struct eloop_timeout *timeout, *prev;
+
+	timeout = eloop.timeout;
+	while (timeout != NULL) {
+		prev = timeout;
+		timeout = timeout->next;
+		free(prev);
+	}
+	free(eloop.readers);
+	free(eloop.signals);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/eloop.h	Fri Apr 27 09:21:03 2007 -0700
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Sun elects to license this software under the BSD license.
+ * See README for more details.
+ */
+#ifndef __ELOOP_H
+#define	__ELOOP_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#ifndef timersub
+#define	timersub(tvp, uvp, vvp)						\
+	do								\
+	{								\
+		(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec;		\
+		(vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec;	\
+		if ((vvp)->tv_usec < 0)					\
+		{							\
+			(vvp)->tv_sec--;				\
+			(vvp)->tv_usec += 1000000;			\
+		}							\
+	} while (0)
+#endif /* !timersub */
+
+#ifndef timeradd
+#define	timeradd(tvp, uvp, vvp)						\
+	do								\
+	{								\
+		(vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec;		\
+		(vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec;	\
+		if ((vvp)->tv_usec >= 1000000)				\
+		{							\
+			(vvp)->tv_sec++;				\
+			(vvp)->tv_usec -= 1000000;			\
+		}							\
+	} while (0)
+#endif /* !timeradd */
+
+/* Magic number for eloop_cancel_timeout() */
+#define	ELOOP_ALL_CTX		(void *) -1
+#define	MAX_POLLFDS		32
+
+struct eloop_sock {
+	int sock;
+	void *eloop_data;
+	void *user_data;
+	void (*handler)(int, void *, void *);
+};
+
+struct eloop_timeout {
+	struct timeval time;
+	void *eloop_data;
+	void *user_data;
+	void (*handler)(void *, void *);
+	struct eloop_timeout *next;
+};
+
+struct eloop_signal {
+	int sig;
+	void *user_data;
+	void (*handler)(int, void *, void *);
+	int signaled;
+};
+
+struct eloop_data {
+	void *user_data;
+
+	int max_sock, reader_count;
+	struct eloop_sock *readers;
+
+	struct eloop_timeout *timeout;
+
+	int signal_count;
+	struct eloop_signal *signals;
+	int signaled;
+
+	int terminate;
+};
+
+void eloop_init(void *);
+
+int eloop_register_read_sock(int,
+	void (*handler)(int, void *, void *), void *, void *);
+
+void eloop_unregister_read_sock(int);
+
+int eloop_register_timeout(unsigned int, unsigned int,
+	void (*handler)(void *, void *), void *, void *);
+
+void eloop_cancel_timeout(void (*handler)(void *, void *), void *, void *);
+int eloop_register_signal(int, void (*handler)(int, void *, void *), void *);
+
+void eloop_run(void);
+void eloop_terminate(void);
+void eloop_destroy(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ELOOP_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/l2_packet.c	Fri Apr 27 09:21:03 2007 -0700
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Sun elects to license this software under the BSD license.
+ * See README for more details.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <libdlpi.h>
+#include <sys/ethernet.h>
+#include <netinet/in.h>
+
+#include "wpa_impl.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+static int
+link_init(struct l2_packet_data *l2)
+{
+	int retval;
+	uint8_t paddr[DLPI_PHYSADDR_MAX];
+	size_t paddrlen = sizeof (paddr);
+
+	retval = dlpi_bind(l2->dh, DLPI_ANY_SAP, NULL);
+	if (retval != DLPI_SUCCESS) {
+		wpa_printf(MSG_ERROR, "cannot bind on %s: %s",
+		    l2->ifname, dlpi_strerror(retval));
+		return (-1);
+	}
+
+	retval = dlpi_promiscon(l2->dh, DL_PROMISC_SAP);
+	if (retval != DLPI_SUCCESS) {
+		wpa_printf(MSG_ERROR, "cannot enable promiscous"
+		    " mode (SAP) on %s: %s",
+		    l2->ifname, dlpi_strerror(retval));
+		return (-1);
+	}
+
+	retval = dlpi_get_physaddr(l2->dh, DL_CURR_PHYS_ADDR, paddr, &paddrlen);
+	if (retval != DLPI_SUCCESS) {
+		wpa_printf(MSG_ERROR, "cannot get physical address for %s: %s",
+		    l2->ifname, dlpi_strerror(retval));
+		return (-1);
+	}
+	if (paddrlen != sizeof (l2->own_addr)) {
+		wpa_printf(MSG_ERROR, "physical address for %s is not %d bytes",
+		    l2->ifname, sizeof (l2->own_addr));
+		return (-1);
+	}
+	(void) memcpy(l2->own_addr, paddr, sizeof (l2->own_addr));
+
+	return (0);
+}
+
+/*
+ * layer2 packet handling.
+ */
+int
+l2_packet_get_own_addr(struct l2_packet_data *l2, uint8_t *addr)
+{
+	(void) memcpy(addr, l2->own_addr, sizeof (l2->own_addr));
+	return (0);
+}
+
+int
+l2_packet_send(struct l2_packet_data *l2, uint8_t *buf, size_t buflen)
+{
+	int retval;
+
+	retval = dlpi_send(l2->dh, NULL, 0, buf, buflen, NULL);
+	if (retval != DLPI_SUCCESS) {
+		wpa_printf(MSG_ERROR, "l2_packet_send: cannot send "
+		    "message on %s: %s", l2->ifname, dlpi_strerror(retval));
+		return (-1);
+	}
+	return (0);
+}
+
+/* ARGSUSED */
+static void
+l2_packet_receive(int fd, void *eloop_ctx, void *sock_ctx)
+{
+	struct l2_packet_data *l2 = eloop_ctx;
+	uint64_t buf[IEEE80211_MTU_MAX / sizeof (uint64_t)];
+	size_t buflen = sizeof (buf);
+	struct l2_ethhdr *ethhdr;
+	int retval;
+
+	retval = dlpi_recv(l2->dh, NULL, NULL, buf, &buflen, 0, NULL);
+	if (retval != DLPI_SUCCESS) {
+		wpa_printf(MSG_ERROR, "l2_packet_receive: cannot receive "
+		    "message on %s: %s", l2->ifname, dlpi_strerror(retval));
+		return;
+	}
+
+	ethhdr = (struct l2_ethhdr *)buf;
+	if (buflen < sizeof (*ethhdr) ||
+	    (ntohs(ethhdr->h_proto) != ETHERTYPE_EAPOL &&
+	    ntohs(ethhdr->h_proto) != ETHERTYPE_RSN_PREAUTH))
+		return;
+
+	l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source,
+	    (unsigned char *)(ethhdr + 1), buflen - sizeof (*ethhdr));
+}
+
+/* ARGSUSED */
+struct l2_packet_data *
+l2_packet_init(const char *ifname, unsigned short protocol,
+	void (*rx_callback)(void *, unsigned char *, unsigned char *, size_t),
+	void *rx_callback_ctx)
+{
+	int retval;
+	struct l2_packet_data *l2;
+
+	l2 = calloc(1, sizeof (struct l2_packet_data));
+	if (l2 == NULL)
+		return (NULL);
+
+	(void) strlcpy(l2->ifname, ifname, sizeof (l2->ifname));
+	l2->rx_callback = rx_callback;
+	l2->rx_callback_ctx = rx_callback_ctx;
+
+	retval = dlpi_open(l2->ifname, &l2->dh, DLPI_RAW);
+	if (retval != DLPI_SUCCESS) {
+		wpa_printf(MSG_ERROR, "unable to open DLPI link %s: %s",
+		    l2->ifname, dlpi_strerror(retval));
+		free(l2);
+		return (NULL);
+	}
+
+	/* NOTE: link_init() sets l2->own_addr */
+	if (link_init(l2) < 0) {
+		dlpi_close(l2->dh);
+		free(l2);
+		return (NULL);
+	}
+
+	(void) eloop_register_read_sock(dlpi_fd(l2->dh), l2_packet_receive, l2,
+	    NULL);
+
+	return (l2);
+}
+
+void
+l2_packet_deinit(struct l2_packet_data *l2)
+{
+	if (l2 == NULL)
+		return;
+
+	eloop_unregister_read_sock(dlpi_fd(l2->dh));
+	dlpi_close(l2->dh);
+	free(l2);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/l2_packet.h	Fri Apr 27 09:21:03 2007 -0700
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Sun elects to license this software under the BSD license.
+ * See README for more details.
+ */
+#ifndef __L2_PACKET_H
+#define	__L2_PACKET_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <net/if.h>
+#include <libdlpi.h>
+
+#define	IEEE80211_MTU_MAX	2304
+
+struct l2_packet_data {
+	dlpi_handle_t	dh;	/* dlpi handle for EAPOL frames */
+	char		ifname[DLPI_LINKNAME_MAX];
+	uint8_t		own_addr[IEEE80211_ADDR_LEN];
+	void		(*rx_callback)(void *, unsigned char *,
+	    unsigned char *, size_t);
+	void		*rx_callback_ctx;
+};
+
+#pragma pack(1)
+struct l2_ethhdr {
+	uint8_t h_dest[IEEE80211_ADDR_LEN];
+	uint8_t h_source[IEEE80211_ADDR_LEN];
+	uint16_t h_proto;
+};
+#pragma pack()
+
+struct l2_packet_data *l2_packet_init(
+	const char *, unsigned short,
+	void (*rx_callback)(void *, unsigned char *,
+			    unsigned char *, size_t),
+	void *);
+void l2_packet_deinit(struct l2_packet_data *);
+
+int l2_packet_get_own_addr(struct l2_packet_data *, uint8_t *);
+int l2_packet_send(struct l2_packet_data *, uint8_t *, size_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __L2_PACKET_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa.c	Fri Apr 27 09:21:03 2007 -0700
@@ -0,0 +1,1796 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Sun elects to license this software under the BSD license.
+ * See README for more details.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <netinet/in.h>
+#include <sys/ethernet.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "wpa_impl.h"
+#include "wpa_enc.h"
+#include "driver.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+static void pmksa_cache_set_expiration(struct wpa_supplicant *);
+
+/*
+ * IEEE 802.11i/D3.0
+ */
+static const int WPA_SELECTOR_LEN = 4;
+static const uint8_t WPA_OUI_TYPE[] = { 0x00, 0x50, 0xf2, 1 };
+static const uint16_t WPA_VERSION = 1;
+static const uint8_t
+WPA_AUTH_KEY_MGMT_UNSPEC_802_1X[] 		= { 0x00, 0x50, 0xf2, 1 };
+static const uint8_t
+WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X[] 		= { 0x00, 0x50, 0xf2, 2 };
+static const uint8_t WPA_CIPHER_SUITE_NONE[]	= { 0x00, 0x50, 0xf2, 0 };
+static const uint8_t WPA_CIPHER_SUITE_WEP40[]	= { 0x00, 0x50, 0xf2, 1 };
+static const uint8_t WPA_CIPHER_SUITE_TKIP[]	= { 0x00, 0x50, 0xf2, 2 };
+static const uint8_t WPA_CIPHER_SUITE_CCMP[]	= { 0x00, 0x50, 0xf2, 4 };
+static const uint8_t WPA_CIPHER_SUITE_WEP104[]	= { 0x00, 0x50, 0xf2, 5 };
+
+/*
+ * WPA IE version 1
+ * 00-50-f2:1 (OUI:OUI type)
+ * 0x01 0x00 (version; little endian)
+ * (all following fields are optional:)
+ * Group Suite Selector (4 octets) (default: TKIP)
+ * Pairwise Suite Count (2 octets, little endian) (default: 1)
+ * Pairwise Suite List (4 * n octets) (default: TKIP)
+ * Authenticated Key Management Suite Count (2 octets, little endian)
+ * (default: 1)
+ * Authenticated Key Management Suite List (4 * n octets)
+ * (default: unspec 802.1x)
+ * WPA Capabilities (2 octets, little endian) (default: 0)
+ */
+#pragma pack(1)
+struct wpa_ie_hdr {
+	uint8_t		elem_id;
+	uint8_t		len;
+	uint8_t		oui[3];
+	uint8_t		oui_type;
+	uint16_t	version;
+};
+#pragma pack()
+
+/*
+ * IEEE 802.11i/D9.0
+ */
+static const int RSN_SELECTOR_LEN = 4;
+static const uint16_t RSN_VERSION = 1;
+static const uint8_t
+RSN_AUTH_KEY_MGMT_UNSPEC_802_1X[]		= { 0x00, 0x0f, 0xac, 1 };
+static const uint8_t
+RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X[]		= { 0x00, 0x0f, 0xac, 2 };
+static const uint8_t RSN_CIPHER_SUITE_NONE[]	= { 0x00, 0x0f, 0xac, 0 };
+static const uint8_t RSN_CIPHER_SUITE_WEP40[]	= { 0x00, 0x0f, 0xac, 1 };
+static const uint8_t RSN_CIPHER_SUITE_TKIP[]	= { 0x00, 0x0f, 0xac, 2 };
+static const uint8_t RSN_CIPHER_SUITE_CCMP[]	= { 0x00, 0x0f, 0xac, 4 };
+static const uint8_t RSN_CIPHER_SUITE_WEP104[]	= { 0x00, 0x0f, 0xac, 5 };
+
+/*
+ * EAPOL-Key Key Data Encapsulation
+ * GroupKey and STAKey require encryption, otherwise, encryption is optional.
+ */
+static const uint8_t RSN_KEY_DATA_GROUPKEY[]	= { 0x00, 0x0f, 0xac, 1 };
+static const uint8_t RSN_KEY_DATA_PMKID[]	= { 0x00, 0x0f, 0xac, 4 };
+
+/*
+ * 1/4: PMKID
+ * 2/4: RSN IE
+ * 3/4: one or two RSN IEs + GTK IE (encrypted)
+ * 4/4: empty
+ * 1/2: GTK IE (encrypted)
+ * 2/2: empty
+ */
+
+/*
+ * RSN IE version 1
+ * 0x01 0x00 (version; little endian)
+ * (all following fields are optional:)
+ * Group Suite Selector (4 octets) (default: CCMP)
+ * Pairwise Suite Count (2 octets, little endian) (default: 1)
+ * Pairwise Suite List (4 * n octets) (default: CCMP)
+ * Authenticated Key Management Suite Count (2 octets, little endian)
+ *    (default: 1)
+ * Authenticated Key Management Suite List (4 * n octets)
+ *    (default: unspec 802.1x)
+ * RSN Capabilities (2 octets, little endian) (default: 0)
+ * PMKID Count (2 octets) (default: 0)
+ * PMKID List (16 * n octets)
+ */
+#pragma pack(1)
+struct rsn_ie_hdr {
+	uint8_t		elem_id; /* WLAN_EID_RSN */
+	uint8_t		len;
+	uint16_t	version;
+};
+#pragma pack()
+
+static int
+random_get_pseudo_bytes(uint8_t *ptr, size_t len)
+{
+	int fd;
+	size_t resid = len;
+	size_t bytes;
+
+	fd = open("/dev/urandom", O_RDONLY);
+	if (fd == -1) {
+		wpa_printf(MSG_ERROR, "Could not open /dev/urandom.\n");
+		return (-1);
+	}
+
+	while (resid != 0) {
+		bytes = read(fd, ptr, resid);
+		ptr += bytes;
+		resid -= bytes;
+	}
+
+	(void) close(fd);
+
+	return (0);
+}
+
+static void
+inc_byte_array(uint8_t *counter, size_t len)
+{
+	int pos = len - 1;
+	while (pos >= 0) {
+		counter[pos]++;
+		if (counter[pos] != 0)
+			break;
+		pos--;
+	}
+}
+
+static int
+wpa_selector_to_bitfield(uint8_t *s)
+{
+	if (memcmp(s, WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN) == 0)
+		return (WPA_CIPHER_NONE);
+	if (memcmp(s, WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN) == 0)
+		return (WPA_CIPHER_WEP40);
+	if (memcmp(s, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN) == 0)
+		return (WPA_CIPHER_TKIP);
+	if (memcmp(s, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN) == 0)
+		return (WPA_CIPHER_CCMP);
+	if (memcmp(s, WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN) == 0)
+		return (WPA_CIPHER_WEP104);
+	return (0);
+}
+
+static int
+wpa_key_mgmt_to_bitfield(uint8_t *s)
+{
+	if (memcmp(s, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X, WPA_SELECTOR_LEN) == 0)
+		return (WPA_KEY_MGMT_IEEE8021X);
+	if (memcmp(s, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X, WPA_SELECTOR_LEN) ==
+	    0)
+		return (WPA_KEY_MGMT_PSK);
+	return (0);
+}
+
+static int
+rsn_selector_to_bitfield(uint8_t *s)
+{
+	if (memcmp(s, RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN) == 0)
+		return (WPA_CIPHER_NONE);
+	if (memcmp(s, RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN) == 0)
+		return (WPA_CIPHER_WEP40);
+	if (memcmp(s, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN) == 0)
+		return (WPA_CIPHER_TKIP);
+	if (memcmp(s, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN) == 0)
+		return (WPA_CIPHER_CCMP);
+	if (memcmp(s, RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN) == 0)
+		return (WPA_CIPHER_WEP104);
+	return (0);
+}
+
+static int
+rsn_key_mgmt_to_bitfield(uint8_t *s)
+{
+	if (memcmp(s, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X, RSN_SELECTOR_LEN) == 0)
+		return (WPA_KEY_MGMT_IEEE8021X);
+	if (memcmp(s, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X, RSN_SELECTOR_LEN) ==
+	    0)
+		return (WPA_KEY_MGMT_PSK);
+	return (0);
+}
+
+static void
+pmksa_cache_free_entry(struct wpa_supplicant *wpa_s,
+	struct rsn_pmksa_cache *entry)
+{
+	wpa_s->pmksa_count--;
+	if (wpa_s->cur_pmksa == entry) {
+		wpa_printf(MSG_DEBUG, "RSN: removed current PMKSA entry");
+		wpa_s->cur_pmksa = NULL;
+	}
+	free(entry);
+}
+
+/* ARGSUSED */
+static void
+pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	time_t now;
+
+	(void) time(&now);
+	while (wpa_s->pmksa && wpa_s->pmksa->expiration <= now) {
+		struct rsn_pmksa_cache *entry = wpa_s->pmksa;
+		wpa_s->pmksa = entry->next;
+		wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
+		    MACSTR, MAC2STR(entry->aa));
+		pmksa_cache_free_entry(wpa_s, entry);
+	}
+
+	pmksa_cache_set_expiration(wpa_s);
+}
+
+static void
+pmksa_cache_set_expiration(struct wpa_supplicant *wpa_s)
+{
+	int sec;
+	eloop_cancel_timeout(pmksa_cache_expire, wpa_s, NULL);
+	if (wpa_s->pmksa == NULL)
+		return;
+	sec = wpa_s->pmksa->expiration - time(NULL);
+	if (sec < 0)
+		sec = 0;
+	(void) eloop_register_timeout(sec + 1, 0, pmksa_cache_expire,
+	    wpa_s, NULL);
+}
+
+void
+pmksa_cache_free(struct wpa_supplicant *wpa_s)
+{
+	struct rsn_pmksa_cache *entry, *prev;
+
+	entry = wpa_s->pmksa;
+	wpa_s->pmksa = NULL;
+	while (entry) {
+		prev = entry;
+		entry = entry->next;
+		free(prev);
+	}
+	pmksa_cache_set_expiration(wpa_s);
+	wpa_s->cur_pmksa = NULL;
+}
+
+struct rsn_pmksa_cache *
+pmksa_cache_get(struct wpa_supplicant *wpa_s,
+		uint8_t *aa, uint8_t *pmkid)
+{
+	struct rsn_pmksa_cache *entry = wpa_s->pmksa;
+	while (entry) {
+		if ((aa == NULL ||
+		    memcmp(entry->aa, aa, IEEE80211_ADDR_LEN) == 0) &&
+		    (pmkid == NULL ||
+		    memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0))
+			return (entry);
+		entry = entry->next;
+	}
+	return (NULL);
+}
+
+int
+pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf, size_t len)
+{
+	int i, j;
+	char *pos = buf;
+	struct rsn_pmksa_cache *entry;
+	time_t now;
+
+	(void) time(&now);
+	pos += snprintf(pos, buf + len - pos,
+	    "Index / AA / PMKID / expiration (in seconds)\n");
+	i = 0;
+	entry = wpa_s->pmksa;
+	while (entry) {
+		i++;
+		pos += snprintf(pos, buf + len - pos, "%d " MACSTR " ",
+		    i, MAC2STR(entry->aa));
+		for (j = 0; j < PMKID_LEN; j++)
+			pos += snprintf(pos, buf + len - pos, "%02x",
+			    entry->pmkid[j]);
+		pos += snprintf(pos, buf + len - pos, " %d\n",
+		    (int)(entry->expiration - now));
+		entry = entry->next;
+	}
+	return (pos - buf);
+}
+
+void
+pmksa_candidate_free(struct wpa_supplicant *wpa_s)
+{
+	struct rsn_pmksa_candidate *entry, *prev;
+
+	entry = wpa_s->pmksa_candidates;
+	wpa_s->pmksa_candidates = NULL;
+	while (entry) {
+		prev = entry;
+		entry = entry->next;
+		free(prev);
+	}
+}
+
+/* ARGSUSED */
+static int
+wpa_parse_wpa_ie_wpa(struct wpa_supplicant *wpa_s, uint8_t *wpa_ie,
+    size_t wpa_ie_len, struct wpa_ie_data *data)
+{
+	struct wpa_ie_hdr *hdr;
+	uint8_t *pos;
+	int left;
+	int i, count;
+
+	data->proto = WPA_PROTO_WPA;
+	data->pairwise_cipher = WPA_CIPHER_TKIP;
+	data->group_cipher = WPA_CIPHER_TKIP;
+	data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+	data->capabilities = 0;
+
+	if (wpa_ie_len == 0) {
+		/* No WPA IE - fail silently */
+		return (-1);
+	}
+
+	if (wpa_ie_len < sizeof (struct wpa_ie_hdr)) {
+		wpa_printf(MSG_DEBUG, "%s: ie len too short %u",
+		    "wpa_parse_wpa_ie_wpa", wpa_ie_len);
+		return (-1);
+	}
+
+	hdr = (struct wpa_ie_hdr *)wpa_ie;
+
+	if (hdr->elem_id != GENERIC_INFO_ELEM ||
+	    hdr->len != wpa_ie_len - 2 ||
+	    memcmp(&hdr->oui, WPA_OUI_TYPE, WPA_SELECTOR_LEN) != 0 ||
+	    LE_16(hdr->version) != WPA_VERSION) {
+		wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
+		    "wpa_parse_wpa_ie_wpa");
+		return (-1);
+	}
+
+	pos = (uint8_t *)(hdr + 1);
+	left = wpa_ie_len - sizeof (*hdr);
+
+	if (left >= WPA_SELECTOR_LEN) {
+		data->group_cipher = wpa_selector_to_bitfield(pos);
+		pos += WPA_SELECTOR_LEN;
+		left -= WPA_SELECTOR_LEN;
+	} else if (left > 0) {
+		wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much",
+		    "wpa_parse_wpa_ie_wpa", left);
+		return (-1);
+	}
+
+	if (left >= 2) {
+		data->pairwise_cipher = 0;
+		count = pos[0] | (pos[1] << 8);
+		pos += 2;
+		left -= 2;
+		if (count == 0 || left < count * WPA_SELECTOR_LEN) {
+			wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), "
+			    "count %u left %u",
+			    "wpa_parse_wpa_ie_wpa", count, left);
+			return (-1);
+		}
+		for (i = 0; i < count; i++) {
+			data->pairwise_cipher |= wpa_selector_to_bitfield(pos);
+			pos += WPA_SELECTOR_LEN;
+			left -= WPA_SELECTOR_LEN;
+		}
+	} else if (left == 1) {
+		wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)",
+		    "wpa_parse_wpa_ie_wpa");
+		return (-1);
+	}
+
+	if (left >= 2) {
+		data->key_mgmt = 0;
+		count = pos[0] | (pos[1] << 8);
+		pos += 2;
+		left -= 2;
+		if (count == 0 || left < count * WPA_SELECTOR_LEN) {
+			wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), "
+			    "count %u left %u",
+			    "wpa_parse_wpa_ie_wpa", count, left);
+			return (-1);
+		}
+		for (i = 0; i < count; i++) {
+			data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos);
+			pos += WPA_SELECTOR_LEN;
+			left -= WPA_SELECTOR_LEN;
+		}
+	} else if (left == 1) {
+		wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)",
+		    "wpa_parse_wpa_ie_wpa");
+		return (-1);
+	}
+
+	if (left >= 2) {
+		data->capabilities = pos[0] | (pos[1] << 8);
+		pos += 2;
+		left -= 2;
+	}
+
+	if (left > 0) {
+		wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes",
+		    "wpa_parse_wpa_ie_wpa", left);
+		return (-1);
+	}
+
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+wpa_parse_wpa_ie_rsn(struct wpa_supplicant *wpa_s, uint8_t *rsn_ie,
+    size_t rsn_ie_len, struct wpa_ie_data *data)
+{
+	struct rsn_ie_hdr *hdr;
+	uint8_t *pos;
+	int left;
+	int i, count;
+
+	data->proto = WPA_PROTO_RSN;
+	data->pairwise_cipher = WPA_CIPHER_CCMP;
+	data->group_cipher = WPA_CIPHER_CCMP;
+	data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+	data->capabilities = 0;
+
+	if (rsn_ie_len == 0) {
+		/* No RSN IE - fail silently */
+		return (-1);
+	}
+
+	if (rsn_ie_len < sizeof (struct rsn_ie_hdr)) {
+		wpa_printf(MSG_DEBUG, "%s: ie len too short %u",
+		    "wpa_parse_wpa_ie_rsn", rsn_ie_len);
+		return (-1);
+	}
+
+	hdr = (struct rsn_ie_hdr *)rsn_ie;
+
+	if (hdr->elem_id != RSN_INFO_ELEM ||
+	    hdr->len != rsn_ie_len - 2 ||
+	    LE_16(hdr->version) != RSN_VERSION) {
+		wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
+		    "wpa_parse_wpa_ie_rsn");
+		return (-1);
+	}
+
+	pos = (uint8_t *)(hdr + 1);
+	left = rsn_ie_len - sizeof (*hdr);
+
+	if (left >= RSN_SELECTOR_LEN) {
+		data->group_cipher = rsn_selector_to_bitfield(pos);
+		pos += RSN_SELECTOR_LEN;
+		left -= RSN_SELECTOR_LEN;
+	} else if (left > 0) {
+		wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much",
+		    "wpa_parse_wpa_ie_rsn", left);
+		return (-1);
+	}
+
+	if (left >= 2) {
+		data->pairwise_cipher = 0;
+		count = pos[0] | (pos[1] << 8);
+		pos += 2;
+		left -= 2;
+		if (count == 0 || left < count * RSN_SELECTOR_LEN) {
+			wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), "
+			    "count %u left %u",
+			    "wpa_parse_wpa_ie_rsn", count, left);
+			return (-1);
+		}
+		for (i = 0; i < count; i++) {
+			data->pairwise_cipher |= rsn_selector_to_bitfield(pos);
+			pos += RSN_SELECTOR_LEN;
+			left -= RSN_SELECTOR_LEN;
+		}
+	} else if (left == 1) {
+		wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)",
+		    "wpa_parse_wpa_ie_rsn");
+		return (-1);
+	}
+
+	if (left >= 2) {
+		data->key_mgmt = 0;
+		count = pos[0] | (pos[1] << 8);
+		pos += 2;
+		left -= 2;
+		if (count == 0 || left < count * RSN_SELECTOR_LEN) {
+			wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), "
+			    "count %u left %u",
+			    "wpa_parse_wpa_ie_rsn", count, left);
+			return (-1);
+		}
+		for (i = 0; i < count; i++) {
+			data->key_mgmt |= rsn_key_mgmt_to_bitfield(pos);
+			pos += RSN_SELECTOR_LEN;
+			left -= RSN_SELECTOR_LEN;
+		}
+	} else if (left == 1) {
+		wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)",
+		    "wpa_parse_wpa_ie_rsn");
+		return (-1);
+	}
+
+	if (left >= 2) {
+		data->capabilities = pos[0] | (pos[1] << 8);
+		pos += 2;
+		left -= 2;
+	}
+
+	if (left > 0) {
+		/*
+		 * RSN IE could include PMKID data, but Authenticator should
+		 * never include it, so no need to parse it in the Supplicant.
+		 */
+		wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored",
+		    "wpa_parse_wpa_ie_rsn", left);
+	}
+
+	return (0);
+}
+
+int
+wpa_parse_wpa_ie(struct wpa_supplicant *wpa_s, uint8_t *wpa_ie,
+    size_t wpa_ie_len, struct wpa_ie_data *data)
+{
+	if (wpa_ie_len >= 1 && wpa_ie[0] == RSN_INFO_ELEM)
+		return (wpa_parse_wpa_ie_rsn(wpa_s, wpa_ie, wpa_ie_len, data));
+	else
+		return (wpa_parse_wpa_ie_wpa(wpa_s, wpa_ie, wpa_ie_len, data));
+}
+
+static int
+wpa_gen_wpa_ie_wpa(struct wpa_supplicant *wpa_s, uint8_t *wpa_ie)
+{
+	uint8_t *pos;
+	struct wpa_ie_hdr *hdr;
+
+	hdr = (struct wpa_ie_hdr *)wpa_ie;
+	hdr->elem_id = GENERIC_INFO_ELEM;
+	(void) memcpy(&hdr->oui, WPA_OUI_TYPE, WPA_SELECTOR_LEN);
+	hdr->version = LE_16(WPA_VERSION);
+	pos = (uint8_t *)(hdr + 1);
+
+	if (wpa_s->group_cipher == WPA_CIPHER_CCMP) {
+		(void) memcpy(pos, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN);
+	} else if (wpa_s->group_cipher == WPA_CIPHER_TKIP) {
+		(void) memcpy(pos, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN);
+	} else if (wpa_s->group_cipher == WPA_CIPHER_WEP104) {
+		(void) memcpy(pos, WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN);
+	} else if (wpa_s->group_cipher == WPA_CIPHER_WEP40) {
+		(void) memcpy(pos, WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN);
+	} else {
+		wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
+		    wpa_s->group_cipher);
+		return (-1);
+	}
+	pos += WPA_SELECTOR_LEN;
+
+	*pos++ = 1;
+	*pos++ = 0;
+	if (wpa_s->pairwise_cipher == WPA_CIPHER_CCMP) {
+		(void) memcpy(pos, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN);
+	} else if (wpa_s->pairwise_cipher == WPA_CIPHER_TKIP) {
+		(void) memcpy(pos, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN);
+	} else if (wpa_s->pairwise_cipher == WPA_CIPHER_NONE) {
+		(void) memcpy(pos, WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN);
+	} else {
+		wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
+		    wpa_s->pairwise_cipher);
+		return (-1);
+	}
+	pos += WPA_SELECTOR_LEN;
+
+	*pos++ = 1;
+	*pos++ = 0;
+	if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
+		(void) memcpy(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X,
+		    WPA_SELECTOR_LEN);
+	} else if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK) {
+		(void) memcpy(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X,
+		    WPA_SELECTOR_LEN);
+	} else {
+		wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
+		    wpa_s->key_mgmt);
+		return (-1);
+	}
+	pos += WPA_SELECTOR_LEN;
+
+	/*
+	 * WPA Capabilities; use defaults, so no need to include it
+	 */
+	hdr->len = (pos - wpa_ie) - 2;
+
+	return (pos - wpa_ie);
+}
+
+static int
+wpa_gen_wpa_ie_rsn(struct wpa_supplicant *wpa_s, uint8_t *rsn_ie)
+{
+	uint8_t *pos;
+	struct rsn_ie_hdr *hdr;
+
+	hdr = (struct rsn_ie_hdr *)rsn_ie;
+	hdr->elem_id = RSN_INFO_ELEM;
+	hdr->version = LE_16(RSN_VERSION);
+	pos = (uint8_t *)(hdr + 1);
+
+	if (wpa_s->group_cipher == WPA_CIPHER_CCMP) {
+		(void) memcpy(pos, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN);
+	} else if (wpa_s->group_cipher == WPA_CIPHER_TKIP) {
+		(void) memcpy(pos, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN);
+	} else if (wpa_s->group_cipher == WPA_CIPHER_WEP104) {
+		(void) memcpy(pos, RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN);
+	} else if (wpa_s->group_cipher == WPA_CIPHER_WEP40) {
+		(void) memcpy(pos, RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN);
+	} else {
+		wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
+		    wpa_s->group_cipher);
+		return (-1);
+	}
+	pos += RSN_SELECTOR_LEN;
+
+	*pos++ = 1;
+	*pos++ = 0;
+	if (wpa_s->pairwise_cipher == WPA_CIPHER_CCMP) {
+		(void) memcpy(pos, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN);
+	} else if (wpa_s->pairwise_cipher == WPA_CIPHER_TKIP) {
+		(void) memcpy(pos, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN);
+	} else if (wpa_s->pairwise_cipher == WPA_CIPHER_NONE) {
+		(void) memcpy(pos, RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN);
+	} else {
+		wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
+		    wpa_s->pairwise_cipher);
+		return (-1);
+	}
+	pos += RSN_SELECTOR_LEN;
+
+	*pos++ = 1;
+	*pos++ = 0;
+	if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
+		(void) memcpy(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X,
+		    RSN_SELECTOR_LEN);
+	} else if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK) {
+		(void) memcpy(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X,
+		    RSN_SELECTOR_LEN);
+	} else {
+		wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
+		    wpa_s->key_mgmt);
+		return (-1);
+	}
+	pos += RSN_SELECTOR_LEN;
+
+	/* RSN Capabilities */
+	*pos++ = 0;
+	*pos++ = 0;
+
+	if (wpa_s->cur_pmksa) {
+		/* PMKID Count (2 octets, little endian) */
+		*pos++ = 1;
+		*pos++ = 0;
+		/* PMKID */
+		(void) memcpy(pos, wpa_s->cur_pmksa->pmkid, PMKID_LEN);
+		pos += PMKID_LEN;
+	}
+
+	hdr->len = (pos - rsn_ie) - 2;
+
+	return (pos - rsn_ie);
+}
+
+int
+wpa_gen_wpa_ie(struct wpa_supplicant *wpa_s, uint8_t *wpa_ie)
+{
+	if (wpa_s->proto == WPA_PROTO_RSN)
+		return (wpa_gen_wpa_ie_rsn(wpa_s, wpa_ie));
+	else
+		return (wpa_gen_wpa_ie_wpa(wpa_s, wpa_ie));
+}
+
+static void
+wpa_pmk_to_ptk(uint8_t *pmk, uint8_t *addr1, uint8_t *addr2,
+    uint8_t *nonce1, uint8_t *nonce2, uint8_t *ptk, size_t ptk_len)
+{
+	uint8_t data[2 * IEEE80211_ADDR_LEN + 2 * WPA_PMK_LEN];
+
+	/*
+	 * PTK = PRF-X(PMK, "Pairwise key expansion",
+	 * 	Min(AA, SA) || Max(AA, SA) ||
+	 * 	Min(ANonce, SNonce) || Max(ANonce, SNonce))
+	 */
+
+	if (memcmp(addr1, addr2, IEEE80211_ADDR_LEN) < 0) {
+		(void) memcpy(data, addr1, IEEE80211_ADDR_LEN);
+		(void) memcpy(data + IEEE80211_ADDR_LEN, addr2,
+		    IEEE80211_ADDR_LEN);
+	} else {
+		(void) memcpy(data, addr2, IEEE80211_ADDR_LEN);
+		(void) memcpy(data + IEEE80211_ADDR_LEN, addr1,
+		    IEEE80211_ADDR_LEN);
+	}
+
+	if (memcmp(nonce1, nonce2, WPA_PMK_LEN) < 0) {
+		(void) memcpy(data + 2 * IEEE80211_ADDR_LEN, nonce1,
+		    WPA_PMK_LEN);
+		(void) memcpy(data + 2 * IEEE80211_ADDR_LEN + WPA_PMK_LEN,
+		    nonce2, WPA_PMK_LEN);
+	} else {
+		(void) memcpy(data + 2 * IEEE80211_ADDR_LEN, nonce2,
+		    WPA_PMK_LEN);
+		(void) memcpy(data + 2 * IEEE80211_ADDR_LEN + WPA_PMK_LEN,
+		    nonce1, WPA_PMK_LEN);
+	}
+
+	sha1_prf(pmk, WPA_PMK_LEN, "Pairwise key expansion", data,
+	    sizeof (data), ptk, ptk_len);
+
+	wpa_hexdump(MSG_DEBUG, "WPA: PMK", pmk, WPA_PMK_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPA: PTK", ptk, ptk_len);
+}
+
+struct wpa_ssid *
+wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_ssid *entry;
+	uint8_t ssid[MAX_ESSID_LENGTH];
+	int ssid_len;
+	uint8_t bssid[IEEE80211_ADDR_LEN];
+
+	(void) memset(ssid, 0, MAX_ESSID_LENGTH);
+	ssid_len = wpa_s->driver->get_ssid(wpa_s->ifname, (char *)ssid);
+	if (ssid_len < 0) {
+		wpa_printf(MSG_WARNING, "Could not read SSID from driver.");
+		return (NULL);
+	}
+
+	if (wpa_s->driver->get_bssid(wpa_s->ifname, (char *)bssid) < 0) {
+		wpa_printf(MSG_WARNING, "Could not read BSSID from driver.");
+		return (NULL);
+	}
+
+	entry = wpa_s->conf->ssid;
+	wpa_printf(MSG_DEBUG, "entry len=%d ssid=%s,"
+	    " driver len=%d ssid=%s",
+	    entry->ssid_len, entry->ssid, ssid_len, ssid);
+
+	if (ssid_len == entry->ssid_len &&
+	    memcmp(ssid, entry->ssid, ssid_len) == 0 &&
+	    (!entry->bssid_set ||
+	    memcmp(bssid, entry->bssid, IEEE80211_ADDR_LEN) == 0))
+		return (entry);
+
+	return (NULL);
+}
+
+static void
+wpa_eapol_key_mic(uint8_t *key, int ver, uint8_t *buf, size_t len, uint8_t *mic)
+{
+	if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) {
+		hmac_md5(key, 16, buf, len, mic);
+	} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+		uint8_t hash[SHA1_MAC_LEN];
+		hmac_sha1(key, 16, buf, len, hash);
+		(void) memcpy(mic, hash, MD5_MAC_LEN);
+	}
+}
+
+void
+wpa_supplicant_key_request(struct wpa_supplicant *wpa_s,
+	int error, int pairwise)
+{
+	int rlen;
+	struct ieee802_1x_hdr *hdr;
+	struct wpa_eapol_key *reply;
+	unsigned char *rbuf;
+	struct l2_ethhdr *ethhdr;
+	int key_info, ver;
+	uint8_t bssid[IEEE80211_ADDR_LEN];
+
+	if (wpa_s->pairwise_cipher == WPA_CIPHER_CCMP)
+		ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
+	else
+		ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
+
+	if (wpa_s->driver->get_bssid(wpa_s->ifname, (char *)bssid) < 0) {
+		wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key "
+		    "request");
+		return;
+	}
+
+	rlen = sizeof (*ethhdr) + sizeof (*hdr) + sizeof (*reply);
+	rbuf = malloc(rlen);
+	if (rbuf == NULL)
+		return;
+
+	(void) memset(rbuf, 0, rlen);
+	ethhdr = (struct l2_ethhdr *)rbuf;
+	(void) memcpy(ethhdr->h_dest, bssid, IEEE80211_ADDR_LEN);
+	(void) memcpy(ethhdr->h_source, wpa_s->own_addr, IEEE80211_ADDR_LEN);
+	ethhdr->h_proto = htons(ETHERTYPE_EAPOL);
+
+	hdr = (struct ieee802_1x_hdr *)(ethhdr + 1);
+	hdr->version = wpa_s->conf->eapol_version;
+	hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
+	hdr->length = htons(sizeof (*reply));
+
+	reply = (struct wpa_eapol_key *)(hdr + 1);
+	reply->type = wpa_s->proto == WPA_PROTO_RSN ?
+		EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
+	key_info = WPA_KEY_INFO_REQUEST | ver;
+	if (wpa_s->ptk_set)
+		key_info |= WPA_KEY_INFO_MIC;
+	if (error)
+		key_info |= WPA_KEY_INFO_ERROR;
+	if (pairwise)
+		key_info |= WPA_KEY_INFO_KEY_TYPE;
+	reply->key_info = BE_16(key_info);
+	reply->key_length = 0;
+	(void) memcpy(reply->replay_counter, wpa_s->request_counter,
+		WPA_REPLAY_COUNTER_LEN);
+	inc_byte_array(wpa_s->request_counter, WPA_REPLAY_COUNTER_LEN);
+
+	reply->key_data_length = BE_16(0);
+
+	if (key_info & WPA_KEY_INFO_MIC) {
+		wpa_eapol_key_mic(wpa_s->ptk.mic_key, ver, (uint8_t *)hdr,
+		    rlen - sizeof (*ethhdr), reply->key_mic);
+	}
+
+	wpa_printf(MSG_INFO, "WPA: Sending EAPOL-Key Request (error=%d "
+	    "pairwise=%d ptk_set=%d len=%d)",
+	    error, pairwise, wpa_s->ptk_set, rlen);
+	wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key Request", rbuf, rlen);
+	(void) l2_packet_send(wpa_s->l2, rbuf, rlen);
+	free(rbuf);
+}
+
+static void
+wpa_supplicant_process_1_of_4(struct wpa_supplicant *wpa_s,
+    unsigned char *src_addr, struct wpa_eapol_key *key, int ver)
+{
+	int rlen;
+	struct ieee802_1x_hdr *hdr;
+	struct wpa_eapol_key *reply;
+	unsigned char *rbuf;
+	struct l2_ethhdr *ethhdr;
+	struct wpa_ssid *ssid;
+	struct wpa_ptk *ptk;
+	uint8_t buf[8], wpa_ie_buf[80], *wpa_ie, *pmkid = NULL;
+	int wpa_ie_len;
+
+	wpa_s->wpa_state = WPA_4WAY_HANDSHAKE;
+	wpa_printf(MSG_DEBUG, "WPA: RX message 1 of 4-Way Handshake from "
+	    MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
+
+	ssid = wpa_supplicant_get_ssid(wpa_s);
+	if (ssid == NULL) {
+		wpa_printf(MSG_WARNING,
+		    "WPA: No SSID info found (msg 1 of 4).");
+		return;
+	}
+
+	if (wpa_s->proto == WPA_PROTO_RSN) {
+		/* RSN: msg 1/4 should contain PMKID for the selected PMK */
+		uint8_t *pos = (uint8_t *)(key + 1);
+		uint8_t *end = pos + BE_16(key->key_data_length);
+
+		wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data",
+		    pos, BE_16(key->key_data_length));
+
+		while (pos + 1 < end) {
+			if (pos + 2 + pos[1] > end) {
+				wpa_printf(MSG_DEBUG, "RSN: key data "
+				    "underflow (ie=%d len=%d)",
+				    pos[0], pos[1]);
+				break;
+			}
+			if (pos[0] == GENERIC_INFO_ELEM &&
+				pos + 1 + RSN_SELECTOR_LEN < end &&
+				pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN &&
+				memcmp(pos + 2, RSN_KEY_DATA_PMKID,
+				RSN_SELECTOR_LEN) == 0) {
+				pmkid = pos + 2 + RSN_SELECTOR_LEN;
+				wpa_hexdump(MSG_DEBUG, "RSN: PMKID from "
+				    "Authenticator", pmkid, PMKID_LEN);
+				break;
+			} else if (pos[0] == GENERIC_INFO_ELEM && pos[1] == 0)
+				break;
+			pos += 2 + pos[1];
+		}
+	}
+
+	wpa_ie = wpa_ie_buf;
+	wpa_ie_len = wpa_gen_wpa_ie(wpa_s, wpa_ie);
+	if (wpa_ie_len < 0) {
+		wpa_printf(MSG_WARNING, "WPA: Failed to generate "
+		    "WPA IE (for msg 2 of 4).");
+		return;
+	}
+	wpa_hexdump(MSG_DEBUG, "WPA: WPA IE for msg 2/4", wpa_ie, wpa_ie_len);
+
+	rlen = sizeof (*ethhdr) + sizeof (*hdr) + sizeof (*reply) + wpa_ie_len;
+	rbuf = malloc(rlen);
+	if (rbuf == NULL)
+		return;
+
+	(void) memset(rbuf, 0, rlen);
+	ethhdr = (struct l2_ethhdr *)rbuf;
+	(void) memcpy(ethhdr->h_dest, src_addr, IEEE80211_ADDR_LEN);
+	(void) memcpy(ethhdr->h_source, wpa_s->own_addr, IEEE80211_ADDR_LEN);
+	ethhdr->h_proto = htons(ETHERTYPE_EAPOL);
+
+	hdr = (struct ieee802_1x_hdr *)(ethhdr + 1);
+	hdr->version = wpa_s->conf->eapol_version;
+	hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
+	hdr->length = htons(sizeof (*reply) + wpa_ie_len);
+
+	reply = (struct wpa_eapol_key *)(hdr + 1);
+	reply->type = wpa_s->proto == WPA_PROTO_RSN ?
+		EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
+	reply->key_info = BE_16(ver | WPA_KEY_INFO_KEY_TYPE |
+					WPA_KEY_INFO_MIC);
+	reply->key_length = key->key_length;
+	(void) memcpy(reply->replay_counter, key->replay_counter,
+		WPA_REPLAY_COUNTER_LEN);
+
+	reply->key_data_length = BE_16(wpa_ie_len);
+	(void) memcpy(reply + 1, wpa_ie, wpa_ie_len);
+
+	if (wpa_s->renew_snonce) {
+		if (random_get_pseudo_bytes(wpa_s->snonce, WPA_NONCE_LEN)) {
+			wpa_printf(MSG_WARNING, "WPA: Failed to get "
+			    "random data for SNonce");
+			free(rbuf);
+			return;
+		}
+
+		wpa_s->renew_snonce = 0;
+		wpa_hexdump(MSG_DEBUG, "WPA: Renewed SNonce",
+		    wpa_s->snonce, WPA_NONCE_LEN);
+	}
+	(void) memcpy(reply->key_nonce, wpa_s->snonce, WPA_NONCE_LEN);
+	ptk = &wpa_s->tptk;
+	(void) memcpy(wpa_s->anonce, key->key_nonce, WPA_NONCE_LEN);
+
+	wpa_pmk_to_ptk(wpa_s->pmk, wpa_s->own_addr, src_addr,
+	    wpa_s->snonce, key->key_nonce, (uint8_t *)ptk, sizeof (*ptk));
+
+	/*
+	 * Supplicant: swap tx/rx Mic keys
+	 */
+	(void) memcpy(buf, ptk->u.auth.tx_mic_key, 8);
+	(void) memcpy(ptk->u.auth.tx_mic_key, ptk->u.auth.rx_mic_key, 8);
+	(void) memcpy(ptk->u.auth.rx_mic_key, buf, 8);
+	wpa_s->tptk_set = 1;
+	wpa_eapol_key_mic(wpa_s->tptk.mic_key, ver, (uint8_t *)hdr,
+			rlen - sizeof (*ethhdr), reply->key_mic);
+	wpa_hexdump(MSG_DEBUG, "WPA: EAPOL-Key MIC", reply->key_mic, 16);
+
+	wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4");
+	wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key 2/4", rbuf, rlen);
+	(void) l2_packet_send(wpa_s->l2, rbuf, rlen);
+
+	free(rbuf);
+}
+
+static void
+wpa_supplicant_process_3_of_4_gtk(struct wpa_supplicant *wpa_s,
+    unsigned char *src_addr, struct wpa_eapol_key *key,
+    uint8_t *gtk, int gtk_len)
+{
+	int keyidx, tx, key_rsc_len = 0, alg;
+
+	wpa_hexdump(MSG_DEBUG,
+	    "WPA: received GTK in pairwise handshake", gtk, gtk_len);
+
+	keyidx = gtk[0] & 0x3;
+	tx = !!(gtk[0] & BIT(2));
+	if (tx && wpa_s->pairwise_cipher != WPA_CIPHER_NONE) {
+		/*
+		 * Ignore Tx bit in GTK IE if a pairwise key is used.
+		 * One AP seemed to set this bit (incorrectly, since Tx
+		 * is only when doing Group Key only APs) and without
+		 * this workaround, the data connection does not work
+		 * because wpa_supplicant configured non-zero keyidx to
+		 * be used for unicast.
+		 */
+		wpa_printf(MSG_INFO, "RSN: Tx bit set for GTK IE, but "
+		    "pairwise keys are used - ignore Tx bit");
+		tx = 0;
+	}
+
+	gtk += 2;
+	gtk_len -= 2;
+	wpa_hexdump(MSG_DEBUG, "WPA: Group Key", gtk, gtk_len);
+
+	switch (wpa_s->group_cipher) {
+	case WPA_CIPHER_CCMP:
+		if (gtk_len != 16) {
+			wpa_printf(MSG_WARNING, "WPA: Unsupported CCMP"
+			    " Group Cipher key length %d.", gtk_len);
+			return;
+		}
+		key_rsc_len = 6;
+		alg = WPA_ALG_CCMP;
+		break;
+	case WPA_CIPHER_TKIP:
+		if (gtk_len != 32) {
+			wpa_printf(MSG_WARNING, "WPA: Unsupported TKIP"
+			    " Group Cipher key length %d.", gtk_len);
+			return;
+		}
+		key_rsc_len = 6;
+		alg = WPA_ALG_TKIP;
+		break;
+	case WPA_CIPHER_WEP104:
+		if (gtk_len != 13) {
+			wpa_printf(MSG_WARNING, "WPA: Unsupported "
+			    "WEP104 Group Cipher key length " "%d.", gtk_len);
+			return;
+		}
+		alg = WPA_ALG_WEP;
+		break;
+	case WPA_CIPHER_WEP40:
+		if (gtk_len != 5) {
+			wpa_printf(MSG_WARNING, "WPA: Unsupported "
+			    "WEP40 Group Cipher key length %d.", gtk_len);
+			return;
+		}
+		alg = WPA_ALG_WEP;
+		break;
+	default:
+		wpa_printf(MSG_WARNING, "WPA: Unsupport Group Cipher "
+		    "%d", wpa_s->group_cipher);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPA: Installing GTK to the driver "
+	    "(keyidx=%d tx=%d).", keyidx, tx);
+	wpa_hexdump(MSG_DEBUG, "WPA: RSC", key->key_rsc, key_rsc_len);
+	if (wpa_s->group_cipher == WPA_CIPHER_TKIP) {
+		uint8_t tmpbuf[8];
+		/*
+		 * Swap Tx/Rx keys for Michael MIC
+		 */
+		(void) memcpy(tmpbuf, gtk + 16, 8);
+		(void) memcpy(gtk + 16, gtk + 24, 8);
+		(void) memcpy(gtk + 24, tmpbuf, 8);
+	}
+	if (wpa_s->pairwise_cipher == WPA_CIPHER_NONE) {
+		if (wpa_s->driver->set_key(wpa_s->ifname, alg,
+		    (uint8_t *)"\xff\xff\xff\xff\xff\xff",
+		    keyidx, 1, key->key_rsc,
+		    key_rsc_len, gtk, gtk_len) < 0)
+			wpa_printf(MSG_WARNING, "WPA: Failed to set "
+			    "GTK to the driver (Group only).");
+	} else if (wpa_s->driver->set_key(wpa_s->ifname, alg,
+		    (uint8_t *)"\xff\xff\xff\xff\xff\xff",
+		    keyidx, tx,
+		    key->key_rsc, key_rsc_len,
+		    gtk, gtk_len) < 0) {
+		wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to "
+		    "the driver.");
+	}
+
+	wpa_printf(MSG_INFO, "WPA: Key negotiation completed with "
+		MACSTR, MAC2STR(src_addr));
+	eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
+	wpa_supplicant_cancel_auth_timeout(wpa_s);
+	wpa_s->wpa_state = WPA_COMPLETED;
+}
+
+static void
+wpa_supplicant_process_3_of_4(struct wpa_supplicant *wpa_s,
+    unsigned char *src_addr, struct wpa_eapol_key *key,
+    int extra_len, int ver)
+{
+	int rlen;
+	struct ieee802_1x_hdr *hdr;
+	struct wpa_eapol_key *reply;
+	unsigned char *rbuf;
+	struct l2_ethhdr *ethhdr;
+	int key_info, ie_len = 0, keylen, gtk_len = 0;
+	uint8_t *ie = NULL, *gtk = NULL, *key_rsc;
+	uint8_t null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+	wpa_s->wpa_state = WPA_4WAY_HANDSHAKE;
+	wpa_printf(MSG_DEBUG, "WPA: RX message 3 of 4-Way Handshake from "
+	    MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
+
+	key_info = BE_16(key->key_info);
+
+	if (wpa_s->proto == WPA_PROTO_RSN) {
+		uint8_t *pos = (uint8_t *)(key + 1);
+		uint8_t *end = pos + BE_16(key->key_data_length);
+		while (pos + 1 < end) {
+			if (pos + 2 + pos[1] > end) {
+				wpa_printf(MSG_DEBUG, "RSN: key data "
+				    "underflow (ie=%d len=%d)",
+				    pos[0], pos[1]);
+				break;
+			}
+			if (*pos == RSN_INFO_ELEM) {
+				ie = pos;
+				ie_len = pos[1] + 2;
+			} else if (pos[0] == GENERIC_INFO_ELEM &&
+				    pos + 1 + RSN_SELECTOR_LEN < end &&
+				    pos[1] > RSN_SELECTOR_LEN + 2 &&
+				    memcmp(pos + 2, RSN_KEY_DATA_GROUPKEY,
+				    RSN_SELECTOR_LEN) == 0) {
+				if (!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+					wpa_printf(MSG_WARNING, "WPA: GTK IE "
+					    "in unencrypted key data");
+					return;
+				}
+				gtk = pos + 2 + RSN_SELECTOR_LEN;
+				gtk_len = pos[1] - RSN_SELECTOR_LEN;
+			} else if (pos[0] == GENERIC_INFO_ELEM && pos[1] == 0)
+				break;
+
+			pos += 2 + pos[1];
+		}
+	} else {
+		ie = (uint8_t *)(key + 1);
+		ie_len = BE_16(key->key_data_length);
+		if (ie_len > extra_len) {
+			wpa_printf(MSG_INFO, "WPA: Truncated EAPOL-Key packet:"
+			    " ie_len=%d > extra_len=%d",
+			    ie_len, extra_len);
+			return;
+		}
+	}
+
+	if (wpa_s->ap_wpa_ie &&
+	    (wpa_s->ap_wpa_ie_len != ie_len ||
+		memcmp(wpa_s->ap_wpa_ie, ie, ie_len) != 0)) {
+		wpa_printf(MSG_WARNING, "WPA: WPA IE in 3/4 msg does not match"
+		    " with WPA IE in Beacon/ProbeResp (src=" MACSTR ")",
+		    MAC2STR(src_addr));
+		wpa_hexdump(MSG_INFO, "WPA: WPA IE in Beacon/ProbeResp",
+		    wpa_s->ap_wpa_ie, wpa_s->ap_wpa_ie_len);
+		wpa_hexdump(MSG_INFO, "WPA: WPA IE in 3/4 msg", ie, ie_len);
+		wpa_supplicant_disassociate(wpa_s, REASON_IE_IN_4WAY_DIFFERS);
+		wpa_supplicant_req_scan(wpa_s, 0, 0);
+		return;
+	}
+
+	if (memcmp(wpa_s->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) {
+		wpa_printf(MSG_WARNING, "WPA: ANonce from message 1 of 4-Way "
+		    "Handshake differs from 3 of 4-Way Handshake - drop"
+		    " packet (src=" MACSTR ")", MAC2STR(src_addr));
+		return;
+	}
+
+	keylen = BE_16(key->key_length);
+	switch (wpa_s->pairwise_cipher) {
+	case WPA_CIPHER_CCMP:
+		if (keylen != 16) {
+			wpa_printf(MSG_WARNING, "WPA: Invalid CCMP key length "
+			    "%d (src=" MACSTR ")",
+			    keylen, MAC2STR(src_addr));
+			return;
+		}
+		break;
+	case WPA_CIPHER_TKIP:
+		if (keylen != 32) {
+			wpa_printf(MSG_WARNING, "WPA: Invalid TKIP key length "
+			    "%d (src=" MACSTR ")",
+			    keylen, MAC2STR(src_addr));
+			return;
+		}
+		break;
+	}
+
+	rlen = sizeof (*ethhdr) + sizeof (*hdr) + sizeof (*reply);
+	rbuf = malloc(rlen);
+	if (rbuf == NULL)
+		return;
+
+	(void) memset(rbuf, 0, rlen);
+	ethhdr = (struct l2_ethhdr *)rbuf;
+	(void) memcpy(ethhdr->h_dest, src_addr, IEEE80211_ADDR_LEN);
+	(void) memcpy(ethhdr->h_source, wpa_s->own_addr, IEEE80211_ADDR_LEN);
+	ethhdr->h_proto = htons(ETHERTYPE_EAPOL);
+
+	hdr = (struct ieee802_1x_hdr *)(ethhdr + 1);
+	hdr->version = wpa_s->conf->eapol_version;
+	hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
+	hdr->length = htons(sizeof (*reply));
+
+	reply = (struct wpa_eapol_key *)(hdr + 1);
+	reply->type = wpa_s->proto == WPA_PROTO_RSN ?
+		EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
+	reply->key_info = BE_16(ver | WPA_KEY_INFO_KEY_TYPE |
+					WPA_KEY_INFO_MIC |
+					(key_info & WPA_KEY_INFO_SECURE));
+	reply->key_length = key->key_length;
+	(void) memcpy(reply->replay_counter, key->replay_counter,
+		WPA_REPLAY_COUNTER_LEN);
+
+	reply->key_data_length = BE_16(0);
+
+	(void) memcpy(reply->key_nonce, wpa_s->snonce, WPA_NONCE_LEN);
+	wpa_eapol_key_mic(wpa_s->ptk.mic_key, ver, (uint8_t *)hdr,
+	    rlen - sizeof (*ethhdr), reply->key_mic);
+
+	wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4");
+	wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key 4/4", rbuf, rlen);
+	(void) l2_packet_send(wpa_s->l2, rbuf, rlen);
+
+	free(rbuf);
+
+	/*
+	 * SNonce was successfully used in msg 3/4, so mark it to be renewed
+	 * for the next 4-Way Handshake. If msg 3 is received again, the old
+	 * SNonce will still be used to avoid changing PTK.
+	 */
+	wpa_s->renew_snonce = 1;
+
+	if (key_info & WPA_KEY_INFO_INSTALL) {
+		int alg, keylen, rsclen;
+		wpa_printf(MSG_DEBUG, "WPA: Installing PTK to the driver.");
+		switch (wpa_s->pairwise_cipher) {
+		case WPA_CIPHER_CCMP:
+			alg = WPA_ALG_CCMP;
+			keylen = 16;
+			rsclen = 6;
+			break;
+		case WPA_CIPHER_TKIP:
+			alg = WPA_ALG_TKIP;
+			keylen = 32;
+			rsclen = 6;
+			break;
+		case WPA_CIPHER_NONE:
+			wpa_printf(MSG_DEBUG, "WPA: Pairwise Cipher Suite: "
+			    "NONE - do not use pairwise keys");
+			return;
+		default:
+			wpa_printf(MSG_WARNING, "WPA: Unsupported pairwise "
+			    "cipher %d", wpa_s->pairwise_cipher);
+			return;
+		}
+		if (wpa_s->proto == WPA_PROTO_RSN) {
+			key_rsc = null_rsc;
+		} else {
+			key_rsc = key->key_rsc;
+			wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, rsclen);
+		}
+
+		if (wpa_s->driver->set_key(wpa_s->ifname, alg, src_addr,
+		    0, 1, key_rsc, rsclen,
+		    (uint8_t *)&wpa_s->ptk.tk1, keylen) < 0) {
+			wpa_printf(MSG_WARNING, "WPA: Failed to set PTK to the"
+			    " driver.");
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "%s: key_info=%x gtk=%p\n",
+	    "wpa_supplicant_process_3_of_4", key_info, gtk);
+	wpa_s->wpa_state = WPA_GROUP_HANDSHAKE;
+
+	if (gtk)
+		wpa_supplicant_process_3_of_4_gtk(wpa_s,
+		    src_addr, key, gtk, gtk_len);
+}
+
+static void
+wpa_supplicant_process_1_of_2(struct wpa_supplicant *wpa_s,
+    unsigned char *src_addr, struct wpa_eapol_key *key,
+    int extra_len, int ver)
+{
+	int rlen;
+	struct ieee802_1x_hdr *hdr;
+	struct wpa_eapol_key *reply;
+	unsigned char *rbuf;
+	struct l2_ethhdr *ethhdr;
+	int key_info, keylen, keydatalen, maxkeylen, keyidx, key_rsc_len = 0;
+	int alg, tx;
+	uint8_t ek[32], tmpbuf[8], gtk[32];
+	uint8_t *gtk_ie = NULL;
+	size_t gtk_ie_len = 0;
+
+	wpa_s->wpa_state = WPA_GROUP_HANDSHAKE;
+	wpa_printf(MSG_DEBUG, "WPA: RX message 1 of Group Key Handshake from "
+	    MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
+
+	key_info = BE_16(key->key_info);
+	keydatalen = BE_16(key->key_data_length);
+
+	if (wpa_s->proto == WPA_PROTO_RSN) {
+		uint8_t *pos = (uint8_t *)(key + 1);
+		uint8_t *end = pos + keydatalen;
+		while (pos + 1 < end) {
+			if (pos + 2 + pos[1] > end) {
+				wpa_printf(MSG_DEBUG, "RSN: key data "
+				    "underflow (ie=%d len=%d)",
+				    pos[0], pos[1]);
+				break;
+			}
+			if (pos[0] == GENERIC_INFO_ELEM &&
+			    pos + 1 + RSN_SELECTOR_LEN < end &&
+			    pos[1] > RSN_SELECTOR_LEN + 2 &&
+			    memcmp(pos + 2, RSN_KEY_DATA_GROUPKEY,
+			    RSN_SELECTOR_LEN) == 0) {
+				if (!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+					wpa_printf(MSG_WARNING, "WPA: GTK IE "
+					    "in unencrypted key data");
+					return;
+				}
+				gtk_ie = pos + 2 + RSN_SELECTOR_LEN;
+				gtk_ie_len = pos[1] - RSN_SELECTOR_LEN;
+				break;
+			} else if (pos[0] == GENERIC_INFO_ELEM &&
+				    pos[1] == 0)
+				break;
+
+			pos += 2 + pos[1];
+		}
+
+		if (gtk_ie == NULL) {
+			wpa_printf(MSG_INFO, "WPA: No GTK IE in Group Key "
+			    "message 1/2");
+			return;
+		}
+		maxkeylen = keylen = gtk_ie_len - 2;
+	} else {
+		keylen = BE_16(key->key_length);
+		maxkeylen = keydatalen;
+		if (keydatalen > extra_len) {
+			wpa_printf(MSG_INFO, "WPA: Truncated EAPOL-Key packet:"
+			    " key_data_length=%d > extra_len=%d",
+			    keydatalen, extra_len);
+			return;
+		}
+		if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES)
+			maxkeylen -= 8;
+	}
+
+	switch (wpa_s->group_cipher) {
+	case WPA_CIPHER_CCMP:
+		if (keylen != 16 || maxkeylen < 16) {
+			wpa_printf(MSG_WARNING, "WPA: Unsupported CCMP Group "
+			    "Cipher key length %d (%d).", keylen, maxkeylen);
+			return;
+		}
+		key_rsc_len = 6;
+		alg = WPA_ALG_CCMP;
+		break;
+	case WPA_CIPHER_TKIP:
+		if (keylen != 32 || maxkeylen < 32) {
+			wpa_printf(MSG_WARNING, "WPA: Unsupported TKIP Group "
+			    "Cipher key length %d (%d).", keylen, maxkeylen);
+			return;
+		}
+		key_rsc_len = 6; /* key->key_data; */
+		alg = WPA_ALG_TKIP;
+		break;
+	case WPA_CIPHER_WEP104:
+		if (keylen != 13 || maxkeylen < 13) {
+			wpa_printf(MSG_WARNING, "WPA: Unsupported WEP104 Group"
+			    " Cipher key length %d (%d).", keylen, maxkeylen);
+			return;
+		}
+		alg = WPA_ALG_WEP;
+		break;
+	case WPA_CIPHER_WEP40:
+		if (keylen != 5 || maxkeylen < 5) {
+			wpa_printf(MSG_WARNING, "WPA: Unsupported WEP40 Group "
+			    "Cipher key length %d (%d).", keylen, maxkeylen);
+			return;
+		}
+		alg = WPA_ALG_WEP;
+		break;
+	default:
+		wpa_printf(MSG_WARNING, "WPA: Unsupport Group Cipher %d",
+		    wpa_s->group_cipher);
+		return;
+	}
+
+	if (wpa_s->proto == WPA_PROTO_RSN) {
+		wpa_hexdump(MSG_DEBUG,
+		    "WPA: received GTK in group key handshake",
+		    gtk_ie, gtk_ie_len);
+		keyidx = gtk_ie[0] & 0x3;
+		tx = !!(gtk_ie[0] & BIT(2));
+		if (gtk_ie_len - 2 > sizeof (gtk)) {
+			wpa_printf(MSG_INFO, "WPA: Too long GTK in GTK IE "
+			    "(len=%d)", gtk_ie_len - 2);
+			return;
+		}
+		(void) memcpy(gtk, gtk_ie + 2, gtk_ie_len - 2);
+	} else {
+		keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
+			WPA_KEY_INFO_KEY_INDEX_SHIFT;
+		if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) {
+			(void) memcpy(ek, key->key_iv, 16);
+			(void) memcpy(ek + 16, wpa_s->ptk.encr_key, 16);
+			rc4_skip(ek, 32, 256, (uint8_t *)(key + 1), keydatalen);
+			(void) memcpy(gtk, key + 1, keylen);
+		} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+			if (keydatalen % 8) {
+				wpa_printf(MSG_WARNING, "WPA: Unsupported "
+				    "AES-WRAP len %d", keydatalen);
+				return;
+			}
+			if (aes_unwrap(wpa_s->ptk.encr_key, maxkeylen / 8,
+					(uint8_t *)(key + 1), gtk)) {
+				wpa_printf(MSG_WARNING, "WPA: AES unwrap "
+				    "failed - could not decrypt GTK");
+				return;
+			}
+		}
+		tx = !!(key_info & WPA_KEY_INFO_TXRX);
+		if (tx && wpa_s->pairwise_cipher != WPA_CIPHER_NONE) {
+			/*
+			 * Ignore Tx bit in Group Key message if a pairwise key
+			 * is used. Some APs seem to setting this bit
+			 * (incorrectly, since Tx is only when doing Group Key
+			 * only APs) and without this workaround, the data
+			 * connection does not work because wpa_supplicant
+			 * configured non-zero keyidx to be used for unicast.
+			 */
+			wpa_printf(MSG_INFO, "WPA: Tx bit set for GTK, but "
+			    "pairwise keys are used - ignore Tx bit");
+			tx = 0;
+		}
+	}
+	wpa_hexdump(MSG_DEBUG, "WPA: Group Key", gtk, keylen);
+	wpa_printf(MSG_DEBUG, "WPA: Installing GTK to the driver (keyidx=%d "
+	    "tx=%d).", keyidx, tx);
+	wpa_hexdump(MSG_DEBUG, "WPA: RSC", key->key_rsc, key_rsc_len);
+	if (wpa_s->group_cipher == WPA_CIPHER_TKIP) {
+		/*
+		 * Swap Tx/Rx keys for Michael MIC
+		 */
+		(void) memcpy(tmpbuf, gtk + 16, 8);
+		(void) memcpy(gtk + 16, gtk + 24, 8);
+		(void) memcpy(gtk + 24, tmpbuf, 8);
+	}
+	if (wpa_s->pairwise_cipher == WPA_CIPHER_NONE) {
+		if (wpa_s->driver->set_key(wpa_s->ifname, alg,
+		    (uint8_t *)"\xff\xff\xff\xff\xff\xff",
+		    keyidx, 1, key->key_rsc,
+		    key_rsc_len, gtk, keylen) < 0)
+			wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to the"
+			    " driver (Group only).");
+	} else if (wpa_s->driver->set_key(wpa_s->ifname, alg,
+	    (uint8_t *)"\xff\xff\xff\xff\xff\xff",
+	    keyidx, tx,
+	    key->key_rsc, key_rsc_len,
+	    gtk, keylen) < 0) {
+		wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to the "
+		    "driver.");
+	}
+
+	rlen = sizeof (*ethhdr) + sizeof (*hdr) + sizeof (*reply);
+	rbuf = malloc(rlen);
+	if (rbuf == NULL)
+		return;
+
+	(void) memset(rbuf, 0, rlen);
+	ethhdr = (struct l2_ethhdr *)rbuf;
+	(void) memcpy(ethhdr->h_dest, src_addr, IEEE80211_ADDR_LEN);
+	(void) memcpy(ethhdr->h_source, wpa_s->own_addr, IEEE80211_ADDR_LEN);
+	ethhdr->h_proto = htons(ETHERTYPE_EAPOL);
+
+	hdr = (struct ieee802_1x_hdr *)(ethhdr + 1);
+	hdr->version = wpa_s->conf->eapol_version;
+	hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
+	hdr->length = htons(sizeof (*reply));
+
+	reply = (struct wpa_eapol_key *)(hdr + 1);
+	reply->type = wpa_s->proto == WPA_PROTO_RSN ?
+	    EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
+	reply->key_info =
+	    BE_16(ver | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE |
+	    (key_info & WPA_KEY_INFO_KEY_INDEX_MASK));
+	reply->key_length = key->key_length;
+	(void) memcpy(reply->replay_counter, key->replay_counter,
+	    WPA_REPLAY_COUNTER_LEN);
+
+	reply->key_data_length = BE_16(0);
+
+	wpa_eapol_key_mic(wpa_s->ptk.mic_key, ver, (uint8_t *)hdr,
+	    rlen - sizeof (*ethhdr), reply->key_mic);
+
+	wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2");
+	wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key 2/2", rbuf, rlen);
+	(void) l2_packet_send(wpa_s->l2, rbuf, rlen);
+	free(rbuf);
+
+	wpa_printf(MSG_INFO, "WPA: Key negotiation completed with " MACSTR,
+	    MAC2STR(src_addr));
+	eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
+	wpa_supplicant_cancel_auth_timeout(wpa_s);
+	wpa_s->wpa_state = WPA_COMPLETED;
+	wpa_printf(MSG_INFO, "-----------------------------------\n");
+}
+
+static int
+wpa_supplicant_verify_eapol_key_mic(struct wpa_supplicant *wpa_s,
+    struct wpa_eapol_key *key, int ver, uint8_t *buf, size_t len)
+{
+	uint8_t mic[16];
+	int ok = 0;
+
+	(void) memcpy(mic, key->key_mic, 16);
+	if (wpa_s->tptk_set) {
+		(void) memset(key->key_mic, 0, 16);
+		wpa_eapol_key_mic(wpa_s->tptk.mic_key, ver, buf, len,
+		    key->key_mic);
+		if (memcmp(mic, key->key_mic, 16) != 0) {
+			wpa_printf(MSG_WARNING, "WPA: Invalid EAPOL-Key MIC "
+			    "when using TPTK - ignoring TPTK");
+		} else {
+			ok = 1;
+			wpa_s->tptk_set = 0;
+			wpa_s->ptk_set = 1;
+			(void) memcpy(&wpa_s->ptk, &wpa_s->tptk,
+			    sizeof (wpa_s->ptk));
+		}
+	}
+
+	if (!ok && wpa_s->ptk_set) {
+		(void) memset(key->key_mic, 0, 16);
+		wpa_eapol_key_mic(wpa_s->ptk.mic_key, ver, buf, len,
+		    key->key_mic);
+		if (memcmp(mic, key->key_mic, 16) != 0) {
+			wpa_printf(MSG_WARNING, "WPA: Invalid EAPOL-Key MIC "
+			    "- dropping packet");
+			return (-1);
+		}
+		ok = 1;
+	}
+
+	if (!ok) {
+		wpa_printf(MSG_WARNING, "WPA: Could not verify EAPOL-Key MIC "
+		    "- dropping packet");
+		return (-1);
+	}
+
+	(void) memcpy(wpa_s->rx_replay_counter, key->replay_counter,
+	    WPA_REPLAY_COUNTER_LEN);
+	wpa_s->rx_replay_counter_set = 1;
+
+	return (0);
+}
+
+/* Decrypt RSN EAPOL-Key key data (RC4 or AES-WRAP) */
+static int
+wpa_supplicant_decrypt_key_data(struct wpa_supplicant *wpa_s,
+	struct wpa_eapol_key *key, int ver)
+{
+	int keydatalen = BE_16(key->key_data_length);
+
+	wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data",
+	    (uint8_t *)(key + 1), keydatalen);
+	if (!wpa_s->ptk_set) {
+		wpa_printf(MSG_WARNING, "WPA: PTK not available, "
+		    "cannot decrypt EAPOL-Key key data.");
+		return (-1);
+	}
+
+	/*
+	 * Decrypt key data here so that this operation does not need
+	 * to be implemented separately for each message type.
+	 */
+	if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) {
+		uint8_t ek[32];
+		(void) memcpy(ek, key->key_iv, 16);
+		(void) memcpy(ek + 16, wpa_s->ptk.encr_key, 16);
+		rc4_skip(ek, 32, 256, (uint8_t *)(key + 1), keydatalen);
+	} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+		uint8_t *buf;
+		if (keydatalen % 8) {
+			wpa_printf(MSG_WARNING, "WPA: Unsupported "
+			    "AES-WRAP len %d", keydatalen);
+			return (-1);
+		}
+		keydatalen -= 8; /* AES-WRAP adds 8 bytes */
+		buf = malloc(keydatalen);
+		if (buf == NULL) {
+			wpa_printf(MSG_WARNING, "WPA: No memory for "
+			    "AES-UNWRAP buffer");
+			return (-1);
+		}
+		if (aes_unwrap(wpa_s->ptk.encr_key, keydatalen / 8,
+		    (uint8_t *)(key + 1), buf)) {
+			free(buf);
+			wpa_printf(MSG_WARNING, "WPA: AES unwrap failed - "
+			    "could not decrypt EAPOL-Key key data");
+			return (-1);
+		}
+		(void) memcpy(key + 1, buf, keydatalen);
+		free(buf);
+		key->key_data_length = BE_16(keydatalen);
+	}
+	wpa_hexdump(MSG_DEBUG, "WPA: decrypted EAPOL-Key key data",
+	    (uint8_t *)(key + 1), keydatalen);
+
+	return (0);
+}
+
+static void
+wpa_sm_rx_eapol(struct wpa_supplicant *wpa_s,
+    unsigned char *src_addr, unsigned char *buf, size_t len)
+{
+	size_t plen, data_len, extra_len;
+	struct ieee802_1x_hdr *hdr;
+	struct wpa_eapol_key *key;
+	int key_info, ver;
+
+	wpa_printf(MSG_DEBUG, "WPA: EAPOL frame len %u\n ", len);
+
+	hdr = (struct ieee802_1x_hdr *)buf;
+	key = (struct wpa_eapol_key *)(hdr + 1);
+	wpa_printf(MSG_DEBUG, "hdr_len=%u, key_len=%u",
+	    sizeof (*hdr), sizeof (*key));
+	if (len < sizeof (*hdr) + sizeof (*key)) {
+		wpa_printf(MSG_DEBUG, "WPA: EAPOL frame too short, len %u, "
+		    "expecting at least %u",
+		    len, sizeof (*hdr) + sizeof (*key));
+		return;
+	}
+	plen = ntohs(hdr->length);
+	data_len = plen + sizeof (*hdr);
+	wpa_printf(MSG_DEBUG, "IEEE 802.1X RX: version=%d type=%d length=%d",
+	    hdr->version, hdr->type, plen);
+
+	if (hdr->type != IEEE802_1X_TYPE_EAPOL_KEY) {
+		wpa_printf(MSG_DEBUG, "WPA: EAPOL frame (type %u) discarded, "
+		    "not a Key frame", hdr->type);
+		return;
+	}
+	if (plen > len - sizeof (*hdr) || plen < sizeof (*key)) {
+		wpa_printf(MSG_DEBUG, "WPA: EAPOL frame payload size %u "
+		    "invalid (frame size %u)", plen, len);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "  EAPOL-Key type=%d", key->type);
+	if (key->type != EAPOL_KEY_TYPE_WPA && key->type !=
+	    EAPOL_KEY_TYPE_RSN) {
+		wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key type (%d) unknown, "
+		    "discarded", key->type);
+		return;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", buf, len);
+	if (data_len < len) {
+		wpa_printf(MSG_DEBUG, "WPA: ignoring %d bytes after the IEEE "
+		    "802.1X data", len - data_len);
+	}
+	key_info = BE_16(key->key_info);
+	ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+	if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 &&
+	    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+		wpa_printf(MSG_INFO, "WPA: Unsupported EAPOL-Key descriptor "
+		    "version %d.", ver);
+		return;
+	}
+
+	if (wpa_s->pairwise_cipher == WPA_CIPHER_CCMP &&
+	    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+		wpa_printf(MSG_INFO, "WPA: CCMP is used, but EAPOL-Key "
+		    "descriptor version (%d) is not 2.", ver);
+		if (wpa_s->group_cipher != WPA_CIPHER_CCMP &&
+		    !(key_info & WPA_KEY_INFO_KEY_TYPE)) {
+			/*
+			 * Earlier versions of IEEE 802.11i did not explicitly
+			 * require version 2 descriptor for all EAPOL-Key
+			 * packets, so allow group keys to use version 1 if
+			 * CCMP is not used for them.
+			 */
+			wpa_printf(MSG_INFO, "WPA: Backwards compatibility: "
+			    "allow invalid version for non-CCMP group keys");
+		} else
+			return;
+	}
+
+	if (wpa_s->rx_replay_counter_set &&
+	    memcmp(key->replay_counter, wpa_s->rx_replay_counter,
+	    WPA_REPLAY_COUNTER_LEN) <= 0) {
+		wpa_printf(MSG_WARNING, "WPA: EAPOL-Key Replay Counter did not"
+		    " increase - dropping packet");
+		return;
+	}
+
+	if (!(key_info & WPA_KEY_INFO_ACK)) {
+		wpa_printf(MSG_INFO, "WPA: No Ack bit in key_info");
+		return;
+	}
+
+	if (key_info & WPA_KEY_INFO_REQUEST) {
+		wpa_printf(MSG_INFO, "WPA: EAPOL-Key with Request bit - "
+		    "dropped");
+		return;
+	}
+
+	if ((key_info & WPA_KEY_INFO_MIC) &&
+		wpa_supplicant_verify_eapol_key_mic(wpa_s, key, ver, buf,
+		    data_len))
+		return;
+
+	extra_len = data_len - sizeof (*hdr) - sizeof (*key);
+
+	if (wpa_s->proto == WPA_PROTO_RSN &&
+	    (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) &&
+	    wpa_supplicant_decrypt_key_data(wpa_s, key, ver))
+		return;
+
+	if (key_info & WPA_KEY_INFO_KEY_TYPE) {
+		if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) {
+			wpa_printf(MSG_WARNING, "WPA: Ignored EAPOL-Key "
+			    "(Pairwise) with non-zero key index");
+			return;
+		}
+		if (key_info & WPA_KEY_INFO_MIC) {
+			/* 3/4 4-Way Handshake */
+			wpa_supplicant_process_3_of_4(wpa_s, src_addr, key,
+			    extra_len, ver);
+		} else {
+			/* 1/4 4-Way Handshake */
+			wpa_supplicant_process_1_of_4(wpa_s, src_addr, key,
+			    ver);
+		}
+	} else {
+		if (key_info & WPA_KEY_INFO_MIC) {
+			/* 1/2 Group Key Handshake */
+			wpa_supplicant_process_1_of_2(wpa_s, src_addr, key,
+			    extra_len, ver);
+		} else {
+			wpa_printf(MSG_WARNING, "WPA: EAPOL-Key (Group) "
+			    "without Mic bit - dropped");
+		}
+	}
+}
+
+void
+wpa_supplicant_rx_eapol(void *ctx, unsigned char *src_addr,
+    unsigned char *buf, size_t len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	wpa_printf(MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr));
+	wpa_hexdump(MSG_MSGDUMP, "RX EAPOL", buf, len);
+
+	if (wpa_s->eapol_received == 0) {
+		/* Timeout for completing IEEE 802.1X and WPA authentication */
+		wpa_supplicant_req_auth_timeout(
+		    wpa_s, wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X ?
+		    70 : 10, 0);
+	}
+	wpa_s->eapol_received++;
+
+	/*
+	 * Source address of the incoming EAPOL frame could be compared to the
+	 * current BSSID. However, it is possible that a centralized
+	 * Authenticator could be using another MAC address than the BSSID of
+	 * an AP, so just allow any address to be used for now. The replies are
+	 * still sent to the current BSSID (if available), though.
+	 */
+	wpa_sm_rx_eapol(wpa_s, src_addr, buf, len);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa.xml	Fri Apr 27 09:21:03 2007 -0700
@@ -0,0 +1,95 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+	Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+	Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License (the "License").
+ You may not use this file except in compliance with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+	ident	"%Z%%M%	%I%	%E% SMI"
+
+	NOTE:  This service manifest is not editable; its contents will
+	be overwritten by package or patch operations, including
+	operating system upgrade.  Make customizations in a different
+	file.
+-->
+
+<service_bundle type='manifest' name='SUNWsupr:wpad'>
+
+<service
+	name='network/wpa'
+	type='service'
+	version='1'>
+
+	<!--
+	    The wpa service will use the crypto framework of
+	    PKCS #11 when we come to the enterprise mode.
+	-->
+	<dependency
+	    name='cryptosvc'
+	    grouping='require_all'
+	    restart_on='none'
+	    type='service'>
+		<service_fmri value='svc:/system/cryptosvc' />
+	</dependency>
+
+	<exec_method
+		type='method'
+		name='start'
+		exec='/usr/lib/inet/wpad'
+		timeout_seconds='60' >
+		<method_context>
+			<method_credential
+				user='root'
+				group='root'
+				limit_privileges=':default'
+				privileges='basic,sys_net_config,net_rawaccess'
+			/>
+		</method_context>
+	</exec_method>
+
+	<exec_method
+		type='method'
+		name='stop'
+		exec=':kill'
+		timeout_seconds='60' />
+
+        <property_group name='general' type='framework'>
+                <!-- to start stop wpad -->
+                <propval name='action_authorization' type='astring'
+                        value='solaris.smf.manage.wpa' />
+        </property_group>
+
+	<stability value='Unstable' />
+
+	<template>
+		<common_name>
+			<loctext xml:lang='C'>
+			Wireless WPA Supplicant
+			</loctext>
+		</common_name>
+		<documentation>
+			<manpage title='wpad' section='1M'
+			    manpath='/usr/share/man' />
+		</documentation>
+	</template>
+</service>
+
+</service_bundle>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_enc.c	Fri Apr 27 09:21:03 2007 -0700
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Sun elects to license this software under the BSD license.
+ * See README for more details.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/types.h>
+
+#include <openssl/aes.h>
+#include <openssl/hmac.h>
+#include <openssl/rc4.h>
+
+#include "wpa_enc.h"
+
+/*
+ * @kek: key encryption key (KEK)
+ * @n: length of the wrapped key in 64-bit units; e.g., 2 = 128-bit = 16 bytes
+ * @plain: plaintext key to be wrapped, n * 64 bit
+ * @cipher: wrapped key, (n + 1) * 64 bit
+ */
+void
+aes_wrap(uint8_t *kek, int n, uint8_t *plain, uint8_t *cipher)
+{
+	uint8_t *a, *r, b[16];
+	int i, j;
+	AES_KEY key;
+
+	a = cipher;
+	r = cipher + 8;
+
+	/* 1) Initialize variables. */
+	(void) memset(a, 0xa6, 8);
+	(void) memcpy(r, plain, 8 * n);
+
+	AES_set_encrypt_key(kek, 128, &key);
+
+	/*
+	 * 2) Calculate intermediate values.
+	 * For j = 0 to 5
+	 * 	For i=1 to n
+	 * 		B = AES(K, A | R[i])
+	 * 		A = MSB(64, B) ^ t where t = (n*j)+i
+	 * 		R[i] = LSB(64, B)
+	 */
+	for (j = 0; j <= 5; j++) {
+		r = cipher + 8;
+		for (i = 1; i <= n; i++) {
+			(void) memcpy(b, a, 8);
+			(void) memcpy(b + 8, r, 8);
+			AES_encrypt(b, b, &key);
+			(void) memcpy(a, b, 8);
+			a[7] ^= n * j + i;
+			(void) memcpy(r, b + 8, 8);
+			r += 8;
+		}
+	}
+
+	/*
+	 * 3) Output the results.
+	 *
+	 * These are already in @cipher due to the location of temporary
+	 * variables.
+	 */
+}
+
+/*
+ * @kek: key encryption key (KEK)
+ * @n: length of the wrapped key in 64-bit units; e.g., 2 = 128-bit = 16 bytes
+ * @cipher: wrapped key to be unwrapped, (n + 1) * 64 bit
+ * @plain: plaintext key, n * 64 bit
+ */
+int
+aes_unwrap(uint8_t *kek, int n, uint8_t *cipher, uint8_t *plain)
+{
+	uint8_t a[8], *r, b[16];
+	int i, j;
+	AES_KEY key;
+
+	/* 1) Initialize variables. */
+	(void) memcpy(a, cipher, 8);
+	r = plain;
+	(void) memcpy(r, cipher + 8, 8 * n);
+
+	AES_set_decrypt_key(kek, 128, &key);
+
+	/*
+	 * 2) Compute intermediate values.
+	 * For j = 5 to 0
+	 * 	For i = n to 1
+	 * 		B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i
+	 * 		A = MSB(64, B)
+	 * 		R[i] = LSB(64, B)
+	 */
+	for (j = 5; j >= 0; j--) {
+		r = plain + (n - 1) * 8;
+		for (i = n; i >= 1; i--) {
+			(void) memcpy(b, a, 8);
+			b[7] ^= n * j + i;
+
+			(void) memcpy(b + 8, r, 8);
+			AES_decrypt(b, b, &key);
+			(void) memcpy(a, b, 8);
+			(void) memcpy(r, b + 8, 8);
+			r -= 8;
+		}
+	}
+
+	/*
+	 * 3) Output results.
+	 *
+	 * These are already in @plain due to the location of temporary
+	 * variables. Just verify that the IV matches with the expected value.
+	 */
+	for (i = 0; i < 8; i++) {
+		if (a[i] != 0xa6) {
+			return (-1);
+		}
+	}
+
+	return (0);
+}
+
+/* RFC 2104 */
+void
+hmac_sha1(unsigned char *key, unsigned int key_len,
+    unsigned char *data, unsigned int data_len, unsigned char *mac)
+{
+	unsigned int mac_len = 0;
+	HMAC(EVP_sha1(), key, key_len, data, data_len, mac, &mac_len);
+}
+
+
+void
+hmac_sha1_vector(unsigned char *key, unsigned int key_len, size_t num_elem,
+    unsigned char *addr[], unsigned int *len, unsigned char *mac)
+{
+	unsigned char *buf, *ptr;
+	int i, buf_len;
+
+	buf_len = 0;
+	for (i = 0; i < num_elem; i ++)
+		buf_len += len[i];
+
+	buf = malloc(buf_len);
+	ptr = buf;
+
+	for (i = 0; i < num_elem; i ++) {
+		(void) memcpy(ptr, addr[i], len[i]);
+		ptr += len[i];
+	}
+
+	hmac_sha1(key, key_len, buf, buf_len, mac);
+
+	free(buf);
+}
+
+
+void
+sha1_prf(unsigned char *key, unsigned int key_len,
+    char *label, unsigned char *data, unsigned int data_len,
+    unsigned char *buf, size_t buf_len)
+{
+	uint8_t zero = 0, counter = 0;
+	size_t pos, plen;
+	uint8_t hash[SHA1_MAC_LEN];
+	size_t label_len = strlen(label);
+
+	unsigned char *addr[4];
+	unsigned int len[4];
+
+	addr[0] = (uint8_t *)label;
+	len[0] = label_len;
+	addr[1] = &zero;
+	len[1] = 1;
+	addr[2] = data;
+	len[2] = data_len;
+	addr[3] = &counter;
+	len[3] = 1;
+
+	pos = 0;
+	while (pos < buf_len) {
+		plen = buf_len - pos;
+		if (plen >= SHA1_MAC_LEN) {
+			hmac_sha1_vector(key, key_len, 4, addr, len, &buf[pos]);
+			pos += SHA1_MAC_LEN;
+		} else {
+			hmac_sha1_vector(key, key_len, 4, addr, len, hash);
+			(void) memcpy(&buf[pos], hash, plen);
+			break;
+		}
+		counter++;
+	}
+}
+
+void
+pbkdf2_sha1(char *passphrase, char *ssid, size_t ssid_len, int iterations,
+    unsigned char *buf, size_t buflen)
+{
+	PKCS5_PBKDF2_HMAC_SHA1(passphrase, -1, (unsigned char *)ssid, ssid_len,
+	    iterations, buflen, buf);
+}
+
+void
+rc4_skip(uint8_t *key, size_t keylen, size_t skip,
+    uint8_t *data, size_t data_len)
+{
+	uint8_t *buf;
+	size_t buf_len;
+
+	buf_len = skip + data_len;
+	buf = malloc(buf_len);
+
+	bzero(buf, buf_len);
+	bcopy(data, buf + skip, data_len);
+
+	rc4(buf, buf_len, key, keylen);
+
+	bcopy(buf + skip, data, data_len);
+	free(buf);
+}
+
+void
+rc4(uint8_t *buf, size_t len, uint8_t *key, size_t key_len)
+{
+	RC4_KEY k;
+
+	RC4_set_key(&k, key_len, key);
+	RC4(&k, len, buf, buf);
+}
+
+void
+hmac_md5_vector(uint8_t *key, size_t key_len, size_t num_elem,
+    uint8_t *addr[], size_t *len, uint8_t *mac)
+{
+	unsigned char *buf, *ptr;
+	int i, buf_len;
+
+	buf_len = 0;
+	for (i = 0; i < num_elem; i ++)
+		buf_len += len[i];
+
+	buf = malloc(buf_len);
+	ptr = buf;
+
+	for (i = 0; i < num_elem; i ++) {
+		(void) memcpy(ptr, addr[i], len[i]);
+		ptr += len[i];
+	}
+
+	hmac_md5(key, key_len, buf, buf_len, mac);
+	free(buf);
+}
+
+/* RFC 2104 */
+void
+hmac_md5(uint8_t *key, size_t key_len, uint8_t *data,
+    size_t data_len, uint8_t *mac)
+{
+	unsigned int mac_len = 0;
+	HMAC(EVP_md5(), key, key_len, data, data_len, mac, &mac_len);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_enc.h	Fri Apr 27 09:21:03 2007 -0700
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Sun elects to license this software under the BSD license.
+ * See README for more details.
+ */
+#ifndef __WPA_ENC_H
+#define	__WPA_ENC_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <openssl/sha.h>
+#include <openssl/md5.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#define	SHA1_MAC_LEN	SHA_DIGEST_LENGTH
+#define	MD5_MAC_LEN	MD5_DIGEST_LENGTH
+
+void aes_wrap(uint8_t *, int, uint8_t *, uint8_t *);
+int aes_unwrap(uint8_t *, int, uint8_t *, uint8_t *);
+
+void hmac_sha1_vector(unsigned char *, unsigned int,
+    size_t, unsigned char *[], unsigned int *, unsigned char *);
+
+void hmac_sha1(unsigned char *, unsigned int,
+    unsigned char *, unsigned int, unsigned char *);
+
+void sha1_prf(unsigned char *, unsigned int,
+    char *, unsigned char *, unsigned int, unsigned char *, size_t);
+
+void pbkdf2_sha1(char *, char *, size_t, int, unsigned char *, size_t);
+
+void rc4_skip(uint8_t *, size_t, size_t, uint8_t *, size_t);
+void rc4(uint8_t *, size_t, uint8_t *, size_t);
+
+void hmac_md5_vector(uint8_t *, size_t, size_t,
+    uint8_t *[], size_t *, uint8_t *);
+void hmac_md5(uint8_t *, size_t, uint8_t *, size_t, uint8_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __WPA_ENC_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_impl.h	Fri Apr 27 09:21:03 2007 -0700
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Sun elects to license this software under the BSD license.
+ * See README for more details.
+ */
+#ifndef __WPA_IMPL_H
+#define	__WPA_IMPL_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <net/wpa.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#define	BIT(n)			(1 << (n))
+
+#define	WPA_CIPHER_NONE		BIT(0)
+#define	WPA_CIPHER_WEP40	BIT(1)
+#define	WPA_CIPHER_WEP104	BIT(2)
+#define	WPA_CIPHER_TKIP		BIT(3)
+#define	WPA_CIPHER_CCMP		BIT(4)
+
+#define	WPA_KEY_MGMT_IEEE8021X	BIT(0)
+#define	WPA_KEY_MGMT_PSK	BIT(1)
+#define	WPA_KEY_MGMT_NONE	BIT(2)
+#define	WPA_KEY_MGMT_IEEE8021X_NO_WPA	BIT(3)
+
+#define	WPA_PROTO_WPA		BIT(0)
+#define	WPA_PROTO_RSN		BIT(1)
+
+#pragma pack(1)
+struct ieee802_1x_hdr {
+	uint8_t		version;
+	uint8_t		type;
+	uint16_t	length;
+	/* followed by length octets of data */
+};
+#pragma pack()
+
+#define	EAPOL_VERSION	2
+
+enum {	IEEE802_1X_TYPE_EAP_PACKET	= 0,
+	IEEE802_1X_TYPE_EAPOL_START	= 1,
+	IEEE802_1X_TYPE_EAPOL_LOGOFF	= 2,
+	IEEE802_1X_TYPE_EAPOL_KEY	= 3,
+	IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT	= 4
+};
+
+enum {	EAPOL_KEY_TYPE_RC4 = 1,
+	EAPOL_KEY_TYPE_RSN = 2,
+	EAPOL_KEY_TYPE_WPA = 254
+};
+
+#define	WPA_NONCE_LEN		32
+#define	WPA_REPLAY_COUNTER_LEN	8
+#define	MAX_PSK_LENGTH		64
+#define	WPA_PMK_LEN		32
+
+#pragma pack(1)
+struct wpa_eapol_key {
+	uint8_t		type;
+	uint16_t	key_info;
+	uint16_t	key_length;
+	uint8_t		replay_counter[WPA_REPLAY_COUNTER_LEN];
+	uint8_t		key_nonce[WPA_NONCE_LEN];
+	uint8_t		key_iv[16];
+	uint8_t		key_rsc[8];
+	uint8_t		key_id[8]; /* Reserved in IEEE 802.11i/RSN */
+	uint8_t		key_mic[16];
+	uint16_t	key_data_length;
+	/* followed by key_data_length bytes of key_data */
+};
+#pragma pack()
+
+#define	WPA_KEY_INFO_TYPE_MASK		(BIT(0) | BIT(1) | BIT(2))
+#define	WPA_KEY_INFO_TYPE_HMAC_MD5_RC4	BIT(0)
+#define	WPA_KEY_INFO_TYPE_HMAC_SHA1_AES	BIT(1)
+#define	WPA_KEY_INFO_KEY_TYPE		BIT(3) /* 1: Pairwise, 0: Group key */
+/* bit4..5 is used in WPA, but is reserved in IEEE 802.11i/RSN */
+#define	WPA_KEY_INFO_KEY_INDEX_MASK	(BIT(4) | BIT(5))
+#define	WPA_KEY_INFO_KEY_INDEX_SHIFT	4
+#define	WPA_KEY_INFO_INSTALL		BIT(6) /* pairwise */
+#define	WPA_KEY_INFO_TXRX		BIT(6) /* group */
+#define	WPA_KEY_INFO_ACK		BIT(7)
+#define	WPA_KEY_INFO_MIC		BIT(8)
+#define	WPA_KEY_INFO_SECURE		BIT(9)
+#define	WPA_KEY_INFO_ERROR		BIT(10)
+#define	WPA_KEY_INFO_REQUEST		BIT(11)
+#define	WPA_KEY_INFO_ENCR_KEY_DATA	BIT(12) /* IEEE 802.11i/RSN only */
+
+#define	WPA_CAPABILITY_PREAUTH		BIT(0)
+
+#define	GENERIC_INFO_ELEM		0xdd
+#define	RSN_INFO_ELEM			0x30
+
+#define	MAX_LOGBUF			4096
+#define	MAX_SCANRESULTS			64
+
+enum {
+	REASON_UNSPECIFIED			= 1,
+	REASON_DEAUTH_LEAVING			= 3,
+	REASON_INVALID_IE			= 13,
+	REASON_MICHAEL_MIC_FAILURE		= 14,
+	REASON_4WAY_HANDSHAKE_TIMEOUT		= 15,
+	REASON_GROUP_KEY_UPDATE_TIMEOUT		= 16,
+	REASON_IE_IN_4WAY_DIFFERS		= 17,
+	REASON_GROUP_CIPHER_NOT_VALID		= 18,
+	REASON_PAIRWISE_CIPHER_NOT_VALID	= 19,
+	REASON_AKMP_NOT_VALID			= 20,
+	REASON_UNSUPPORTED_RSN_IE_VERSION	= 21,
+	REASON_INVALID_RSN_IE_CAPAB		= 22,
+	REASON_IEEE_802_1X_AUTH_FAILED		= 23,
+	REASON_CIPHER_SUITE_REJECTED		= 24
+};
+
+/*
+ * wpa_supplicant
+ */
+#define	PMKID_LEN 			16
+#define	PMK_LEN				32
+
+#define	MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
+#define	MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
+
+struct rsn_pmksa_cache {
+	struct rsn_pmksa_cache	*next;
+	uint8_t			pmkid[PMKID_LEN];
+	uint8_t			pmk[PMK_LEN];
+	time_t			expiration;
+	int			akmp; /* WPA_KEY_MGMT_* */
+	uint8_t			aa[IEEE80211_ADDR_LEN];
+};
+
+struct rsn_pmksa_candidate {
+	struct rsn_pmksa_candidate *next;
+	uint8_t			bssid[IEEE80211_ADDR_LEN];
+};
+
+
+#pragma pack(1)
+struct wpa_ptk {
+	uint8_t mic_key[16]; /* EAPOL-Key MIC Key (MK) */
+	uint8_t encr_key[16]; /* EAPOL-Key Encryption Key (EK) */
+	uint8_t tk1[16]; /* Temporal Key 1 (TK1) */
+	union {
+		uint8_t tk2[16]; /* Temporal Key 2 (TK2) */
+		struct {
+			uint8_t tx_mic_key[8];
+			uint8_t rx_mic_key[8];
+		} auth;
+	} u;
+};
+#pragma pack()
+
+
+struct wpa_supplicant {
+	struct l2_packet_data	*l2;
+	unsigned char		own_addr[IEEE80211_ADDR_LEN];
+
+	char			ifname[WPA_STRSIZE];
+	char			kname[WPA_STRSIZE];
+
+	uint8_t			pmk[PMK_LEN];
+
+	uint8_t			snonce[WPA_NONCE_LEN];
+	uint8_t			anonce[WPA_NONCE_LEN];
+	/* ANonce from the last 1/4 msg */
+
+	struct wpa_ptk		ptk, tptk;
+	int			ptk_set, tptk_set;
+	int			renew_snonce;
+
+	struct wpa_config	*conf;
+
+	uint8_t			request_counter[WPA_REPLAY_COUNTER_LEN];
+	uint8_t			rx_replay_counter[WPA_REPLAY_COUNTER_LEN];
+	int			rx_replay_counter_set;
+
+	uint8_t			bssid[IEEE80211_ADDR_LEN];
+	int			reassociate; /* reassociation requested */
+
+	uint8_t			*ap_wpa_ie;
+	size_t			ap_wpa_ie_len;
+
+	/*
+	 * Selected configuration
+	 * based on Beacon/ProbeResp WPA IE
+	 */
+	int			proto;
+	int 			pairwise_cipher;
+	int 			group_cipher;
+	int			key_mgmt;
+
+	struct wpa_driver_ops	*driver;
+
+	enum {
+		WPA_DISCONNECTED,
+		WPA_SCANNING,
+		WPA_ASSOCIATING,
+		WPA_ASSOCIATED,
+		WPA_4WAY_HANDSHAKE,
+		WPA_GROUP_HANDSHAKE,
+		WPA_COMPLETED
+	} wpa_state;
+
+	struct rsn_pmksa_cache	*pmksa; /* PMKSA cache */
+	int	pmksa_count; /* number of entries in PMKSA cache */
+	struct rsn_pmksa_cache	*cur_pmksa; /* current PMKSA entry */
+	struct rsn_pmksa_candidate	*pmksa_candidates;
+
+	/*
+	 * number of EAPOL packets received after the
+	 * previous association event
+	 */
+	int			eapol_received;
+};
+
+struct wpa_ie_data {
+	int	proto;
+	int	pairwise_cipher;
+	int	group_cipher;
+	int	key_mgmt;
+	int	capabilities;
+};
+
+/* WPA configuration */
+struct wpa_ssid {
+	uint8_t	*ssid;
+	size_t	ssid_len;
+
+	uint8_t	bssid[IEEE80211_ADDR_LEN];
+	int	bssid_set;
+
+	uint8_t	psk[PMK_LEN];
+	int	psk_set;
+	char	*passphrase;
+
+	/* Bitfields of allowed Pairwise/Group Ciphers, WPA_CIPHER_* */
+	int	pairwise_cipher;
+	int	group_cipher;
+
+	int	key_mgmt;
+	int	proto; /* Bitfield of allowed protocols (WPA_PROTO_*) */
+};
+
+struct wpa_config {
+	struct wpa_ssid *ssid; /* global network list */
+	int eapol_version;
+	/* int ap_scan; */
+};
+
+struct wpa_config *wpa_config_read(void *);
+void wpa_config_free(struct wpa_config *);
+
+/*
+ * Debugging function - conditional printf and hex dump.
+ * Driver wrappers can use these for debugging purposes.
+ */
+enum { MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR };
+
+void wpa_printf(int, char *, ...);
+void wpa_hexdump(int, const char *, const uint8_t *, size_t);
+
+void wpa_event_handler(void *, wpa_event_type);
+void wpa_supplicant_rx_eapol(void *, unsigned char *, unsigned char *, size_t);
+
+void wpa_supplicant_scan(void *, void *);
+void wpa_supplicant_req_scan(struct wpa_supplicant *, int, int);
+
+void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *, int, int);
+void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *);
+void wpa_supplicant_disassociate(struct wpa_supplicant *, int);
+
+void pmksa_cache_free(struct wpa_supplicant *);
+void pmksa_candidate_free(struct wpa_supplicant *);
+struct rsn_pmksa_cache *pmksa_cache_get(struct wpa_supplicant *,
+    uint8_t *, uint8_t *);
+
+int wpa_parse_wpa_ie(struct wpa_supplicant *, uint8_t *,
+	size_t, struct wpa_ie_data *);
+int wpa_gen_wpa_ie(struct wpa_supplicant *, uint8_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __WPA_IMPL_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_supplicant.c	Fri Apr 27 09:21:03 2007 -0700
@@ -0,0 +1,927 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Sun elects to license this software under the BSD license.
+ * See README for more details.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <door.h>
+#include <libdladm.h>
+#include <libdllink.h>
+#include <sys/ethernet.h>
+
+#include "wpa_impl.h"
+#include "wpa_enc.h"
+#include "driver.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+static const char *wpa_supplicant_version =
+"wpa_supplicant v1.0";
+
+extern struct wpa_driver_ops wpa_driver_wifi_ops;
+int wpa_debug_level = MSG_ERROR;
+
+/*
+ * wpa_printf - conditional printf
+ * @level: priority level (MSG_*) of the message
+ * @fmt: printf format string, followed by optional arguments
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration.
+ */
+void
+wpa_printf(int level, char *fmt, ...)
+{
+	va_list ap;
+	char buffer[MAX_LOGBUF];
+
+	if (level < wpa_debug_level)
+		return;
+
+	va_start(ap, fmt);
+
+	/* LINTED E_SEC_PRINTF_VAR_FMT */
+	(void) vsnprintf(buffer, sizeof (buffer), fmt, ap);
+
+	va_end(ap);
+
+	syslog(LOG_NOTICE | LOG_DAEMON, "%s", buffer);
+}
+
+/*
+ * wpa_hexdump - conditional hex dump
+ * @level: priority level (MSG_*) of the message
+ * @title: title of for the message
+ * @buf: data buffer to be dumped
+ * @len: length of the @buf
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. The contents of @buf is printed out has hex dump.
+ */
+void
+wpa_hexdump(int level, const char *title, const uint8_t *buf, size_t len)
+{
+	size_t i;
+	char buffer[MAX_LOGBUF], tmp[4];
+	int n;
+
+	if (level < wpa_debug_level)
+		return;
+
+	(void) snprintf(buffer, sizeof (buffer), "%s - hexdump(len=%d):",
+	    title, len);
+	n = strlen(buffer);
+
+	for (i = 0; i < len; i++) {
+		(void) sprintf(tmp, " %02x", buf[i]);
+
+		n += strlen(tmp);
+		if (n >= MAX_LOGBUF) break;
+
+		(void) strlcat(buffer, tmp, sizeof (buffer));
+	}
+
+	syslog(LOG_NOTICE | LOG_DAEMON, "%s", buffer);
+}
+
+static const char *
+wpa_ssid_txt(char *ssid, size_t ssid_len)
+{
+	static char ssid_txt[MAX_ESSID_LENGTH + 1];
+	char *pos;
+
+	if (ssid_len > MAX_ESSID_LENGTH)
+		ssid_len = MAX_ESSID_LENGTH;
+	(void) memcpy(ssid_txt, ssid, ssid_len);
+	ssid_txt[ssid_len] = '\0';
+	for (pos = ssid_txt; *pos != '\0'; pos ++) {
+		if ((uint8_t)*pos < 32 || (uint8_t)*pos >= 127)
+			*pos = '_';
+	}
+	return (ssid_txt);
+}
+
+/* ARGSUSED */
+void
+wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct wpa_ssid *ssid;
+
+	if (wpa_s->conf == NULL)
+		return;
+
+	if (wpa_s->wpa_state == WPA_DISCONNECTED)
+		wpa_s->wpa_state = WPA_SCANNING;
+
+	ssid = wpa_s->conf->ssid;
+	wpa_printf(MSG_DEBUG, "Starting AP scan (%s SSID)",
+	    ssid ? "specific": "broadcast");
+
+	if (ssid) {
+		wpa_printf(MSG_DEBUG, "Scan SSID: %s", ssid->ssid);
+	}
+
+	if (wpa_s->driver->scan(wpa_s->ifname)) {
+		wpa_printf(MSG_WARNING, "Failed to initiate AP scan.");
+	}
+}
+
+void
+wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec)
+{
+	wpa_printf(MSG_DEBUG, "Setting scan request: %d sec %d usec",
+	    sec, usec);
+	(void) eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
+	(void) eloop_register_timeout(sec, usec, wpa_supplicant_scan,
+	    wpa_s, NULL);
+}
+
+void
+wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s)
+{
+	wpa_printf(MSG_DEBUG, "Cancelling scan request");
+	eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
+}
+
+/* ARGSUSED */
+static void
+wpa_supplicant_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+
+	wpa_printf(MSG_INFO, "Authentication with " MACSTR " timed out.",
+	    MAC2STR(wpa_s->bssid));
+
+	wpa_s->reassociate = 1;
+	wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+void
+wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s,
+				int sec, int usec)
+{
+	wpa_printf(MSG_DEBUG, "Setting authentication timeout: %d sec "
+	    "%d usec", sec, usec);
+	eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
+	(void) eloop_register_timeout(sec, usec, wpa_supplicant_timeout,
+	    wpa_s, NULL);
+}
+
+void
+wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s)
+{
+	wpa_printf(MSG_DEBUG, "Cancelling authentication timeout");
+	eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
+}
+
+static void
+wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
+{
+	l2_packet_deinit(wpa_s->l2);
+	wpa_s->l2 = NULL;
+
+	if (wpa_s->conf != NULL) {
+		wpa_config_free(wpa_s->conf);
+		wpa_s->conf = NULL;
+	}
+
+	free(wpa_s->ap_wpa_ie);
+	pmksa_candidate_free(wpa_s);
+	pmksa_cache_free(wpa_s);
+}
+
+static void
+wpa_clear_keys(struct wpa_supplicant *wpa_s, uint8_t *addr)
+{
+	wpa_s->driver->set_key(wpa_s->ifname, WPA_ALG_NONE,
+	    (uint8_t *)"\xff\xff\xff\xff\xff\xff", 0, 0, NULL, 0, NULL, 0);
+	wpa_s->driver->set_key(wpa_s->ifname, WPA_ALG_NONE,
+	    (uint8_t *)"\xff\xff\xff\xff\xff\xff", 1, 0, NULL, 0, NULL, 0);
+	wpa_s->driver->set_key(wpa_s->ifname, WPA_ALG_NONE,
+	    (uint8_t *)"\xff\xff\xff\xff\xff\xff", 2, 0, NULL, 0, NULL, 0);
+	wpa_s->driver->set_key(wpa_s->ifname, WPA_ALG_NONE,
+	    (uint8_t *)"\xff\xff\xff\xff\xff\xff", 3, 0, NULL, 0, NULL, 0);
+	if (addr) {
+		wpa_s->driver->set_key(wpa_s->ifname, WPA_ALG_NONE, addr,
+		    0, 0, NULL, 0, NULL, 0);
+	}
+}
+
+static void
+wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s)
+{
+	wpa_s->wpa_state = WPA_DISCONNECTED;
+	(void) memset(wpa_s->bssid, 0, IEEE80211_ADDR_LEN);
+}
+
+static int
+wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
+    dladm_wlan_ess_t *bss, struct wpa_ssid *ssid,
+    uint8_t *wpa_ie, int *wpa_ie_len)
+{
+	struct wpa_ie_data ie;
+	int sel, proto;
+	uint8_t *ap_ie;
+	size_t ap_ie_len;
+
+	/* RSN or WPA */
+	if (bss->we_wpa_ie_len && bss->we_wpa_ie[0] == RSN_INFO_ELEM &&
+	    (ssid->proto & WPA_PROTO_RSN)) {
+		wpa_printf(MSG_DEBUG, "RSN: using IEEE 802.11i/D9.0");
+		proto = WPA_PROTO_RSN;
+	} else {
+		wpa_printf(MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0");
+		proto = WPA_PROTO_WPA;
+	}
+
+	ap_ie = bss->we_wpa_ie;
+	ap_ie_len = bss->we_wpa_ie_len;
+
+	if (wpa_parse_wpa_ie(wpa_s, ap_ie, ap_ie_len, &ie)) {
+		wpa_printf(MSG_WARNING, "WPA: Failed to parse WPA IE for "
+		    "the selected BSS.");
+		return (-1);
+	}
+
+	wpa_s->proto = proto;
+	free(wpa_s->ap_wpa_ie);
+	wpa_s->ap_wpa_ie = malloc(ap_ie_len);
+	(void) memcpy(wpa_s->ap_wpa_ie, ap_ie, ap_ie_len);
+	wpa_s->ap_wpa_ie_len = ap_ie_len;
+
+	sel = ie.group_cipher & ssid->group_cipher;
+	if (sel & WPA_CIPHER_CCMP) {
+		wpa_s->group_cipher = WPA_CIPHER_CCMP;
+	} else if (sel & WPA_CIPHER_TKIP) {
+		wpa_s->group_cipher = WPA_CIPHER_TKIP;
+	} else if (sel & WPA_CIPHER_WEP104) {
+		wpa_s->group_cipher = WPA_CIPHER_WEP104;
+	} else if (sel & WPA_CIPHER_WEP40) {
+		wpa_s->group_cipher = WPA_CIPHER_WEP40;
+	} else {
+		wpa_printf(MSG_WARNING, "WPA: Failed to select group cipher.");
+		return (-1);
+	}
+
+	sel = ie.pairwise_cipher & ssid->pairwise_cipher;
+	if (sel & WPA_CIPHER_CCMP) {
+		wpa_s->pairwise_cipher = WPA_CIPHER_CCMP;
+	} else if (sel & WPA_CIPHER_TKIP) {
+		wpa_s->pairwise_cipher = WPA_CIPHER_TKIP;
+	} else if (sel & WPA_CIPHER_NONE) {
+		wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
+	} else {
+		wpa_printf(MSG_WARNING, "WPA: Failed to select pairwise "
+		    "cipher.");
+		return (-1);
+	}
+
+	sel = ie.key_mgmt & ssid->key_mgmt;
+	if (sel & WPA_KEY_MGMT_IEEE8021X) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+	} else if (sel & WPA_KEY_MGMT_PSK) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_PSK;
+	} else {
+		wpa_printf(MSG_WARNING, "WPA: Failed to select authenticated "
+		    "key management type.");
+		return (-1);
+	}
+
+	*wpa_ie_len = wpa_gen_wpa_ie(wpa_s, wpa_ie);
+	if (*wpa_ie_len < 0) {
+		wpa_printf(MSG_WARNING, "WPA: Failed to generate WPA IE.");
+		return (-1);
+	}
+	wpa_hexdump(MSG_DEBUG, "WPA: Own WPA IE", wpa_ie, *wpa_ie_len);
+
+	if (ssid->key_mgmt & WPA_KEY_MGMT_PSK)
+		(void) memcpy(wpa_s->pmk, ssid->psk, PMK_LEN);
+	else if (wpa_s->cur_pmksa)
+		(void) memcpy(wpa_s->pmk, wpa_s->cur_pmksa->pmk, PMK_LEN);
+	else {
+		(void) memset(wpa_s->pmk, 0, PMK_LEN);
+	}
+
+	return (0);
+}
+
+static void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
+    dladm_wlan_ess_t *bss, struct wpa_ssid *ssid)
+{
+	uint8_t wpa_ie[IEEE80211_MAX_OPT_IE];
+	int wpa_ie_len;
+
+	wpa_s->reassociate = 0;
+	wpa_printf(MSG_DEBUG, "Trying to associate with " MACSTR
+	    " (SSID='%s' freq=%d MHz)", MAC2STR(bss->we_bssid.wb_bytes),
+	    wpa_ssid_txt((char *)ssid->ssid, ssid->ssid_len), bss->we_freq);
+	wpa_supplicant_cancel_scan(wpa_s);
+
+	if (bss->we_wpa_ie_len &&
+	    (ssid->key_mgmt & (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK))) {
+		wpa_s->cur_pmksa = pmksa_cache_get(wpa_s,
+		    bss->we_bssid.wb_bytes, NULL);
+		if (wpa_s->cur_pmksa) {
+			wpa_hexdump(MSG_DEBUG, "RSN: PMKID",
+			    wpa_s->cur_pmksa->pmkid, PMKID_LEN);
+		}
+		if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
+		    wpa_ie, &wpa_ie_len)) {
+			wpa_printf(MSG_WARNING, "WPA: Failed to set WPA key "
+			    "management and encryption suites");
+			return;
+		}
+	} else {
+		wpa_ie_len = 0;
+	}
+
+	wpa_clear_keys(wpa_s, bss->we_bssid.wb_bytes);
+	wpa_s->wpa_state = WPA_ASSOCIATING;
+	wpa_s->driver->associate(wpa_s->ifname,
+	    (const char *)bss->we_bssid.wb_bytes, wpa_ie, wpa_ie_len);
+
+	/* Timeout for IEEE 802.11 authentication and association */
+	wpa_supplicant_req_auth_timeout(wpa_s, 15, 0);
+}
+
+void
+wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s, int reason_code)
+{
+	uint8_t *addr = NULL;
+	wpa_s->wpa_state = WPA_DISCONNECTED;
+	if (memcmp(wpa_s->bssid, "\x00\x00\x00\x00\x00\x00",
+	    IEEE80211_ADDR_LEN) != 0) {
+		wpa_s->driver->disassociate(wpa_s->ifname, reason_code);
+		addr = wpa_s->bssid;
+	}
+	wpa_clear_keys(wpa_s, addr);
+}
+
+static dladm_wlan_ess_t *
+wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, struct wpa_ssid *group,
+    dladm_wlan_ess_t *results, int num, struct wpa_ssid **selected_ssid)
+{
+	struct wpa_ssid *ssid;
+	dladm_wlan_ess_t *bss, *selected = NULL;
+	int i;
+
+	struct wpa_ie_data ie;
+
+	wpa_printf(MSG_DEBUG, "Selecting BSS from scan results (%d)", num);
+
+	bss = NULL;
+	ssid = NULL;
+
+	/* try to find matched AP */
+	for (i = 0; i < num && !selected; i++) {
+		bss = &results[i];
+		wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' "
+		    "wpa_ie_len=%d",
+		    i, MAC2STR(bss->we_bssid.wb_bytes),
+		    wpa_ssid_txt(bss->we_ssid.we_bytes, bss->we_ssid_len),
+		    bss->we_wpa_ie_len);
+		if (bss->we_wpa_ie_len == 0) {
+			wpa_printf(MSG_DEBUG, "   skip - no WPA/RSN IE");
+		}
+
+		ssid = group;
+		if (bss->we_ssid_len != ssid->ssid_len ||
+		    memcmp(bss->we_ssid.we_bytes, ssid->ssid,
+		    bss->we_ssid_len) != 0) {
+			wpa_printf(MSG_DEBUG, "   skip - SSID mismatch");
+			continue;
+		}
+		if (!((ssid->proto & (WPA_PROTO_RSN | WPA_PROTO_WPA)) &&
+		    wpa_parse_wpa_ie(wpa_s, bss->we_wpa_ie,
+		    bss->we_wpa_ie_len, &ie) == 0)) {
+			wpa_printf(MSG_DEBUG, "   skip - "
+			    "could not parse WPA/RSN IE");
+			continue;
+		}
+		if (!(ie.proto & ssid->proto)) {
+			wpa_printf(MSG_DEBUG, "   skip - proto mismatch");
+			continue;
+		}
+		if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) {
+			wpa_printf(MSG_DEBUG, "   skip - PTK cipher mismatch");
+			continue;
+		}
+		if (!(ie.group_cipher & ssid->group_cipher)) {
+			wpa_printf(MSG_DEBUG, "   skip - GTK cipher mismatch");
+			continue;
+		}
+		if (!(ie.key_mgmt & ssid->key_mgmt)) {
+			wpa_printf(MSG_DEBUG, "   skip - key mgmt mismatch");
+			continue;
+		}
+
+		selected = bss;
+		*selected_ssid = ssid;
+		wpa_printf(MSG_DEBUG, "   selected");
+	}
+
+	return (selected);
+}
+
+
+static void
+wpa_supplicant_scan_results(struct wpa_supplicant *wpa_s)
+{
+	dladm_wlan_ess_t results[MAX_SCANRESULTS];
+	int num;
+	dladm_wlan_ess_t *selected = NULL;
+	struct wpa_ssid *ssid;
+
+	(void) memset(results, 0, sizeof (dladm_wlan_ess_t) * MAX_SCANRESULTS);
+	num = wpa_s->driver->get_scan_results(wpa_s->ifname, results,
+	    MAX_SCANRESULTS);
+	wpa_printf(MSG_DEBUG, "Scan results: %d", num);
+	if (num < 0)
+		return;
+	if (num > MAX_SCANRESULTS) {
+		wpa_printf(MSG_INFO, "Not enough room for all APs (%d < %d)",
+		    num, MAX_SCANRESULTS);
+		num = MAX_SCANRESULTS;
+	}
+
+	selected = wpa_supplicant_select_bss(wpa_s,
+	    wpa_s->conf->ssid, results, num, &ssid);
+
+	if (selected) {
+		if (wpa_s->reassociate ||
+		    memcmp(selected->we_bssid.wb_bytes, wpa_s->bssid,
+		    IEEE80211_ADDR_LEN) != 0) {
+			wpa_supplicant_associate(wpa_s, selected, ssid);
+		} else {
+			wpa_printf(MSG_DEBUG, "Already associated with the "
+			    "selected AP.");
+		}
+	} else {
+		wpa_printf(MSG_DEBUG, "No suitable AP found.");
+		wpa_supplicant_req_scan(wpa_s, 5, 0);	/* wait 5 seconds */
+	}
+}
+
+/*
+ * wpa_event_handler - report a driver event for wpa_supplicant
+ * @wpa_s: pointer to wpa_supplicant data; this is the @ctx variable registered
+ *	with wpa_driver_events_init()
+ * @event: event type (defined above)
+ *
+ * Driver wrapper code should call this function whenever an event is received
+ * from the driver.
+ */
+void
+wpa_event_handler(void *cookie, wpa_event_type event)
+{
+	struct wpa_supplicant *wpa_s = cookie;
+	uint8_t bssid[IEEE80211_ADDR_LEN];
+
+	switch (event) {
+	case EVENT_ASSOC:
+		wpa_s->wpa_state = WPA_ASSOCIATED;
+		wpa_printf(MSG_DEBUG, "\nAssociation event - clear replay "
+		    "counter\n");
+		(void) memset(wpa_s->rx_replay_counter, 0,
+		    WPA_REPLAY_COUNTER_LEN);
+		wpa_s->rx_replay_counter_set = 0;
+		wpa_s->renew_snonce = 1;
+		if (wpa_s->driver->get_bssid(wpa_s->ifname,
+		    (char *)bssid) >= 0 &&
+		    memcmp(bssid, wpa_s->bssid, IEEE80211_ADDR_LEN) != 0) {
+			wpa_printf(MSG_DEBUG, "Associated to a new BSS: "
+			    "BSSID=" MACSTR, MAC2STR(bssid));
+			(void) memcpy(wpa_s->bssid, bssid, IEEE80211_ADDR_LEN);
+			if (wpa_s->key_mgmt != WPA_KEY_MGMT_NONE)
+				wpa_clear_keys(wpa_s, bssid);
+		}
+
+		wpa_s->eapol_received = 0;
+		if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) {
+			wpa_supplicant_cancel_auth_timeout(wpa_s);
+		} else {
+			/* Timeout for receiving the first EAPOL packet */
+			wpa_supplicant_req_auth_timeout(wpa_s, 10, 0);
+		}
+		break;
+	case EVENT_DISASSOC:
+		if (wpa_s->wpa_state >= WPA_ASSOCIATED)
+			wpa_supplicant_req_scan(wpa_s, 0, 100000);
+		wpa_supplicant_mark_disassoc(wpa_s);
+		wpa_printf(MSG_DEBUG, "Disconnect event - remove keys");
+		if (wpa_s->key_mgmt != WPA_KEY_MGMT_NONE)
+			wpa_clear_keys(wpa_s, wpa_s->bssid);
+		break;
+	case EVENT_SCAN_RESULTS:
+		wpa_supplicant_scan_results(wpa_s);
+		break;
+	default:
+		wpa_printf(MSG_INFO, "Unknown event %d", event);
+		break;
+	}
+}
+
+/* ARGSUSED */
+static void
+wpa_supplicant_terminate(int sig, void *eloop_ctx, void *signal_ctx)
+{
+	wpa_printf(MSG_INFO, "Signal %d received - terminating", sig);
+	eloop_terminate();
+}
+
+static int
+wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s)
+{
+	wpa_s->l2 = l2_packet_init(wpa_s->ifname, ETHERTYPE_EAPOL,
+	    wpa_supplicant_rx_eapol, wpa_s);
+	if (wpa_s->l2 == NULL)
+		return (-1);
+
+	if (l2_packet_get_own_addr(wpa_s->l2, wpa_s->own_addr)) {
+		(void) fprintf(stderr, "Failed to get own L2 address\n");
+		return (-1);
+	}
+
+	if (wpa_s->driver->set_wpa(wpa_s->ifname, 1) < 0) {
+		wpa_printf(MSG_ERROR, "Failed to enable WPA in the driver.");
+		return (-1);
+	}
+
+	wpa_clear_keys(wpa_s, NULL);
+	wpa_supplicant_req_scan(wpa_s, 0, 100000);
+
+	return (0);
+}
+
+static int door_id = -1;
+
+/* ARGSUSED */
+static void
+event_handler(void *cookie, char *argp, size_t asize,
+    door_desc_t *dp, uint_t n_desc)
+{
+	wpa_event_type event;
+
+	event = ((wl_events_t *)argp)->event;
+	wpa_event_handler(cookie, event);
+
+	(void) door_return(NULL, 0, NULL, 0);
+}
+
+/*
+ * Create the driver to wpad door
+ */
+int
+wpa_supplicant_door_setup(void *cookie, char *doorname)
+{
+	struct stat stbuf;
+	int error = 0;
+
+	wpa_printf(MSG_DEBUG, "wpa_supplicant_door_setup(%s)", doorname);
+	/*
+	 * Create the door
+	 */
+	door_id = door_create(event_handler, cookie,
+	    DOOR_UNREF | DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
+
+	if (door_id < 0) {
+		error = -1;
+		goto out;
+	}
+
+	if (stat(doorname, &stbuf) < 0) {
+		int newfd;
+		if ((newfd = creat(doorname, 0666)) < 0) {
+			(void) door_revoke(door_id);
+			door_id = -1;
+			error = -1;
+
+			goto out;
+		}
+		(void) close(newfd);
+	}
+
+	if (fattach(door_id, doorname) < 0) {
+		if ((errno != EBUSY) || (fdetach(doorname) < 0) ||
+		    (fattach(door_id, doorname) < 0)) {
+			(void) door_revoke(door_id);
+			door_id = -1;
+			error = -1;
+
+			goto out;
+		}
+	}
+
+out:
+	return (error);
+}
+
+void
+wpa_supplicant_door_destroy(char *doorname)
+{
+	wpa_printf(MSG_DEBUG, "wpa_supplicant_door_destroy(%s)\n", doorname);
+
+	if (door_revoke(door_id) == -1) {
+		wpa_printf(MSG_ERROR, "failed to door_revoke(%d) %s, exiting.",
+		    door_id, strerror(errno));
+	}
+
+	if (fdetach(doorname) == -1) {
+		wpa_printf(MSG_ERROR, "failed to fdetach %s: %s, exiting.",
+		    doorname, strerror(errno));
+	}
+
+	(void) close(door_id);
+}
+
+static int
+wpa_config_parse_ssid(struct wpa_ssid *ssid, int line, const char *value)
+{
+	free(ssid->ssid);
+
+	ssid->ssid = (uint8_t *)strdup(value);
+	ssid->ssid_len = strlen(value);
+
+	if (ssid->ssid == NULL) {
+		wpa_printf(MSG_ERROR, "Invalid SSID '%s'.", line, value);
+		return (-1);
+	}
+	if (ssid->ssid_len > MAX_ESSID_LENGTH) {
+		free(ssid->ssid);
+		wpa_printf(MSG_ERROR, "Too long SSID '%s'.", line, value);
+		return (-1);
+	}
+	wpa_printf(MSG_MSGDUMP, "SSID: %s", ssid->ssid);
+	return (0);
+}
+
+static struct wpa_ssid *
+wpa_config_read_network(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_ssid *ssid;
+	char buf[MAX_ESSID_LENGTH + 1];
+	dladm_secobj_class_t cl;
+	uint8_t psk[MAX_PSK_LENGTH + 1];
+	uint_t key_len;
+
+	wpa_printf(MSG_MSGDUMP, "Start of a new network configration");
+
+	ssid = (struct wpa_ssid *)malloc(sizeof (*ssid));
+	if (ssid == NULL)
+		return (NULL);
+	(void) memset(ssid, 0, sizeof (*ssid));
+
+	/*
+	 * Set default supported values
+	 */
+	ssid->proto = WPA_PROTO_WPA | WPA_PROTO_RSN;
+	ssid->pairwise_cipher = WPA_CIPHER_CCMP | WPA_CIPHER_TKIP;
+	ssid->group_cipher = WPA_CIPHER_CCMP | WPA_CIPHER_TKIP |
+	    WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40;
+	ssid->key_mgmt = WPA_KEY_MGMT_PSK; /* | WPA_KEY_MGMT_IEEE8021X; */
+
+	(void) memset(buf, 0, MAX_ESSID_LENGTH + 1);
+	wpa_s->driver->get_ssid(wpa_s->ifname, (char *)buf);
+
+	(void) wpa_config_parse_ssid(ssid, 0, buf);
+
+	key_len = sizeof (psk);
+	(void) dladm_get_secobj((const char *)wpa_s->kname, &cl, psk, &key_len,
+	    DLADM_OPT_TEMP);
+	psk[key_len] = '\0';
+	ssid->passphrase = strdup((const char *)psk);
+
+	if (ssid->passphrase) {
+		pbkdf2_sha1(ssid->passphrase, (char *)ssid->ssid,
+		    ssid->ssid_len, 4096, ssid->psk, PMK_LEN);
+		wpa_hexdump(MSG_MSGDUMP, "PSK (from passphrase)",
+		    ssid->psk, PMK_LEN);
+		ssid->psk_set = 1;
+	}
+
+	if ((ssid->key_mgmt & WPA_KEY_MGMT_PSK) && !ssid->psk_set) {
+		wpa_printf(MSG_ERROR, "WPA-PSK accepted for key "
+		    "management, but no PSK configured.");
+		free(ssid);
+		ssid = NULL;
+	}
+
+	return (ssid);
+}
+
+struct wpa_config *
+wpa_config_read(void *arg)
+{
+	struct wpa_ssid *ssid;
+	struct wpa_config *config;
+	struct wpa_supplicant *wpa_s = arg;
+
+	config = malloc(sizeof (*config));
+	if (config == NULL)
+		return (NULL);
+	(void) memset(config, 0, sizeof (*config));
+	config->eapol_version = 1;	/* fixed value */
+
+	wpa_printf(MSG_DEBUG, "Reading configuration parameters from driver\n");
+
+	ssid = wpa_config_read_network(wpa_s);
+	if (ssid == NULL) {
+		wpa_config_free(config);
+		config = NULL;
+	} else {
+		config->ssid = ssid;
+	}
+
+	return (config);
+}
+
+void
+wpa_config_free(struct wpa_config *config)
+{
+	struct wpa_ssid *ssid = config->ssid;
+
+	free(ssid->ssid);
+	free(ssid->passphrase);
+	free(ssid);
+	free(config);
+}
+
+static int
+daemon(boolean_t nochdir, boolean_t noclose)
+{
+	int retv;
+
+	if ((retv = fork()) == -1)
+		return (-1);
+	if (retv != 0)
+		_exit(EXIT_SUCCESS);
+	if (setsid() == -1)
+		return (-1);
+
+	if (!nochdir && chdir("/") == -1)
+		return (-1);
+
+	if (!noclose) {
+		(void) close(0);
+		(void) close(1);
+		(void) close(2);
+		if ((retv = open("/dev/null", O_RDWR)) != -1) {
+			(void) dup2(retv, 1);
+			(void) dup2(retv, 2);
+		}
+	}
+
+	return (0);
+}
+
+static void
+usage(void)
+{
+	(void) printf("%s\n\n"
+		"usage:\n"
+		"  wpa_supplicant [-hv] -i<ifname> -k<keyname>"
+		"\n"
+		"options:\n"
+		"  -h = show this help text\n"
+		"  -v = show version\n",
+		wpa_supplicant_version);
+}
+
+int
+main(int argc, char *argv[])
+{
+	struct wpa_supplicant wpa_s;
+	char *link = NULL;
+	char *key = NULL;
+	int c;
+	int exitcode;
+	char door_file[WPA_STRSIZE];
+
+	(void) memset(&wpa_s, 0, sizeof (wpa_s));
+
+	for (;;) {
+		c = getopt(argc, argv, "Dk:hi:v");
+		if (c < 0)
+			break;
+		switch (c) {
+		case 'D':
+			wpa_debug_level = MSG_DEBUG;
+			break;
+		case 'h':
+			usage();
+			return (-1);
+		case 'i':
+			link = optarg;
+			break;
+		case 'k':
+			key = optarg;
+			break;
+		case 'v':
+			(void) printf("%s\n", wpa_supplicant_version);
+			return (-1);
+		default:
+			usage();
+			return (-1);
+		}
+	}
+
+	wpa_s.driver = &wpa_driver_wifi_ops;
+	eloop_init(&wpa_s);
+	/*
+	 * key name is required to retrieve PSK value through libwdladm APIs.
+	 * key is saved by dladm command by keyname
+	 * see dladm.
+	 */
+	if ((link == NULL) || (key == NULL)) {
+		wpa_printf(MSG_ERROR, "\nLink & key is required.");
+		return (-1);
+	}
+
+	if ((strlen(link) >= sizeof (wpa_s.ifname)) ||
+	    (strlen(key) >= sizeof (wpa_s.kname)))  {
+		wpa_printf(MSG_ERROR, "Too long link/key name '%s', '%s'.",
+		    link, key);
+		return (-1);
+	}
+
+	(void) strlcpy(wpa_s.ifname, link, sizeof (wpa_s.ifname));
+	(void) strlcpy(wpa_s.kname, key, sizeof (wpa_s.kname));
+
+	/*
+	 * Setup door file to communicate with driver
+	 * Since this is multiple instance service, different instance
+	 * has different doors.
+	 */
+	(void) snprintf(door_file, WPA_STRSIZE, "%s_%s", WPA_DOOR, link);
+
+	/*
+	 * Setup default WPA/WPA2 configuration
+	 * get ESSID and PSK value
+	 */
+	wpa_s.conf = wpa_config_read(&wpa_s);
+	if (wpa_s.conf == NULL || wpa_s.conf->ssid == NULL) {
+		wpa_printf(MSG_ERROR, "\nNo networks (SSID) configured.\n");
+		return (-1);
+	}
+
+	exitcode = 0;
+
+	if (daemon(0, 0)) {
+		exitcode = -1;
+		goto cleanup;
+	}
+
+	if (wpa_supplicant_door_setup(&wpa_s, door_file) != 0) {
+		wpa_printf(MSG_ERROR, "Failed to setup door(%s)", door_file);
+		exitcode = -1;
+		goto cleanup;
+	}
+
+	wpa_s.renew_snonce = 1;
+	if (wpa_supplicant_driver_init(&wpa_s) < 0) {
+		exitcode = -1;
+		goto cleanup;
+	}
+
+	wpa_printf(MSG_DEBUG, "=> eloop_run");
+
+	(void) eloop_register_signal(SIGINT, wpa_supplicant_terminate, NULL);
+	(void) eloop_register_signal(SIGTERM, wpa_supplicant_terminate, NULL);
+	(void) eloop_register_signal(SIGKILL, wpa_supplicant_terminate, NULL);
+
+	eloop_run();
+
+	wpa_printf(MSG_DEBUG, "<= eloop_run()");
+	wpa_supplicant_disassociate(&wpa_s, REASON_DEAUTH_LEAVING);
+
+cleanup:
+	if (wpa_s.driver->set_wpa(wpa_s.ifname, 0) < 0) {
+		wpa_printf(MSG_ERROR, "Failed to disable WPA in the driver.\n");
+	}
+
+	wpa_supplicant_door_destroy(door_file);
+	wpa_supplicant_cleanup(&wpa_s);
+	eloop_destroy();
+
+	return (exitcode);
+}
--- a/usr/src/cmd/dladm/dladm.c	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/cmd/dladm/dladm.c	Fri Apr 27 09:21:03 2007 -0700
@@ -219,7 +219,7 @@
 	    "\n"
 	    "\tscan-wifi       [-p] [-o <field>,...] [<name>]\n"
 	    "\tconnect-wifi    [-e <essid>] [-i <bssid>] [-k <key>,...]"
-	    " [-s wep]\n"
+	    " [-s wep|wpa]\n"
 	    "\t                [-a open|shared] [-b bss|ibss] [-c] [-m a|b|g]\n"
 	    "\t                [-T <time>] [<name>]\n"
 	    "\tdisconnect-wifi [-a] [<name>]\n"
@@ -2024,17 +2024,17 @@
 }
 
 static int
-parse_wep_keys(char *str, dladm_wlan_wepkey_t **keys, uint_t *key_countp)
+parse_wlan_keys(char *str, dladm_wlan_key_t **keys, uint_t *key_countp)
 {
 	uint_t			i;
 	split_t			*sp;
-	dladm_wlan_wepkey_t	*wk;
-
-	sp = split(str, DLADM_WLAN_MAX_WEPKEYS, DLADM_WLAN_MAX_WEPKEYNAME_LEN);
+	dladm_wlan_key_t	*wk;
+
+	sp = split(str, DLADM_WLAN_MAX_WEPKEYS, DLADM_WLAN_MAX_KEYNAME_LEN);
 	if (sp == NULL)
 		return (-1);
 
-	wk = malloc(sp->s_nfields * sizeof (dladm_wlan_wepkey_t));
+	wk = malloc(sp->s_nfields * sizeof (dladm_wlan_key_t));
 	if (wk == NULL)
 		goto fail;
 
@@ -2044,7 +2044,7 @@
 		dladm_status_t		status;
 
 		(void) strlcpy(wk[i].wk_name, sp->s_fields[i],
-		    DLADM_WLAN_MAX_WEPKEYNAME_LEN);
+		    DLADM_WLAN_MAX_KEYNAME_LEN);
 
 		wk[i].wk_idx = 1;
 		if ((s = strrchr(wk[i].wk_name, ':')) != NULL) {
@@ -2054,7 +2054,7 @@
 			wk[i].wk_idx = (uint_t)(s[1] - '0');
 			*s = '\0';
 		}
-		wk[i].wk_len = DLADM_WLAN_MAX_WEPKEY_LEN;
+		wk[i].wk_len = DLADM_WLAN_MAX_KEY_LEN;
 
 		status = dladm_get_secobj(wk[i].wk_name, &class,
 		    wk[i].wk_val, &wk[i].wk_len, 0);
@@ -2067,6 +2067,7 @@
 			if (status != DLADM_STATUS_OK)
 				goto fail;
 		}
+		wk[i].wk_class = class;
 	}
 	*keys = wk;
 	*key_countp = i;
@@ -2086,10 +2087,11 @@
 	dladm_status_t		status = DLADM_STATUS_OK;
 	int			timeout = DLADM_WLAN_CONNECT_TIMEOUT_DEFAULT;
 	const char		*link = NULL;
-	dladm_wlan_wepkey_t	*keys = NULL;
+	dladm_wlan_key_t	*keys = NULL;
 	uint_t			key_count = 0;
 	uint_t			flags = 0;
 	dladm_wlan_secmode_t	keysecmode = DLADM_WLAN_SECMODE_NONE;
+	char			buf[DLADM_STRSIZE];
 
 	opterr = 0;
 	(void) memset(&attr, 0, sizeof (attr));
@@ -2145,10 +2147,13 @@
 			attr.wa_valid |= DLADM_WLAN_ATTR_SECMODE;
 			break;
 		case 'k':
-			if (parse_wep_keys(optarg, &keys, &key_count) < 0)
+			if (parse_wlan_keys(optarg, &keys, &key_count) < 0)
 				die("invalid key(s) '%s'", optarg);
 
-			keysecmode = DLADM_WLAN_SECMODE_WEP;
+			if (keys[0].wk_class == DLADM_SECOBJ_CLASS_WEP)
+				keysecmode = DLADM_WLAN_SECMODE_WEP;
+			else
+				keysecmode = DLADM_WLAN_SECMODE_WPA;
 			break;
 		case 'T':
 			if (strcasecmp(optarg, "forever") == 0) {
@@ -2160,6 +2165,7 @@
 			break;
 		case 'c':
 			flags |= DLADM_WLAN_CONNECT_CREATEIBSS;
+			flags |= DLADM_WLAN_CONNECT_CREATEIBSS;
 			break;
 		default:
 			die_opterr(optopt, option);
@@ -2168,16 +2174,17 @@
 	}
 
 	if (keysecmode == DLADM_WLAN_SECMODE_NONE) {
-		if ((attr.wa_valid & DLADM_WLAN_ATTR_SECMODE) != 0 &&
-		    attr.wa_secmode == DLADM_WLAN_SECMODE_WEP)
-			die("key required for security mode 'wep'");
+		if ((attr.wa_valid & DLADM_WLAN_ATTR_SECMODE) != 0) {
+			die("key required for security mode '%s'",
+			    dladm_wlan_secmode2str(&attr.wa_secmode, buf));
+		}
 	} else {
 		if ((attr.wa_valid & DLADM_WLAN_ATTR_SECMODE) != 0 &&
 		    attr.wa_secmode != keysecmode)
 			die("incompatible -s and -k options");
+		attr.wa_valid |= DLADM_WLAN_ATTR_SECMODE;
+		attr.wa_secmode = keysecmode;
 	}
-	attr.wa_secmode = keysecmode;
-	attr.wa_valid |= DLADM_WLAN_ATTR_SECMODE;
 
 	if (optind == (argc - 1))
 		link = argv[optind];
@@ -2803,29 +2810,39 @@
 {
 	int error = 0;
 
-	if (class != DLADM_SECOBJ_CLASS_WEP)
-		return (ENOENT);
-
-	switch (len) {
-	case 5:			/* ASCII key sizes */
-	case 13:
+	if (class == DLADM_SECOBJ_CLASS_WPA) {
+		if (len < 8 || len > 63)
+			return (EINVAL);
 		(void) memcpy(obj_val, buf, len);
 		*obj_lenp = len;
-		break;
-	case 10:		/* Hex key sizes, not preceded by 0x */
-	case 26:
-		error = hexascii_to_octet(buf, len, obj_val, obj_lenp);
-		break;
-	case 12:		/* Hex key sizes, preceded by 0x */
-	case 28:
-		if (strncmp(buf, "0x", 2) != 0)
+		return (error);
+	}
+
+	if (class == DLADM_SECOBJ_CLASS_WEP) {
+		switch (len) {
+		case 5:			/* ASCII key sizes */
+		case 13:
+			(void) memcpy(obj_val, buf, len);
+			*obj_lenp = len;
+			break;
+		case 10:		/* Hex key sizes, not preceded by 0x */
+		case 26:
+			error = hexascii_to_octet(buf, len, obj_val, obj_lenp);
+			break;
+		case 12:		/* Hex key sizes, preceded by 0x */
+		case 28:
+			if (strncmp(buf, "0x", 2) != 0)
+				return (EINVAL);
+			error = hexascii_to_octet(buf + 2, len - 2,
+			    obj_val, obj_lenp);
+			break;
+		default:
 			return (EINVAL);
-		error = hexascii_to_octet(buf + 2, len - 2, obj_val, obj_lenp);
-		break;
-	default:
-		return (EINVAL);
+		}
+		return (error);
 	}
-	return (error);
+
+	return (ENOENT);
 }
 
 /* ARGSUSED */
@@ -3026,7 +3043,7 @@
 			status = dladm_str2secobjclass(optarg, &class);
 			if (status != DLADM_STATUS_OK) {
 				die("invalid secure object class '%s', "
-				    "valid values are: wep", optarg);
+				    "valid values are: wep, wpa", optarg);
 			}
 			break;
 		case 't':
@@ -3139,7 +3156,7 @@
 		die("secure object name required");
 
 	success = check_auth(LINK_SEC_AUTH);
-	audit_secobj(LINK_SEC_AUTH, "wep", argv[optind], success, B_FALSE);
+	audit_secobj(LINK_SEC_AUTH, "unknown", argv[optind], success, B_FALSE);
 	if (!success)
 		die("authorization '%s' is required", LINK_SEC_AUTH);
 
--- a/usr/src/lib/libdladm/Makefile.com	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/lib/libdladm/Makefile.com	Fri Apr 27 09:21:03 2007 -0700
@@ -35,7 +35,7 @@
 include ../../Makefile.rootfs
 
 LIBS =		$(DYNLIB) $(LINTLIB)
-LDLIBS +=	-ldevinfo -ldlpi -lc -linetutil -lsocket
+LDLIBS +=	-ldevinfo -ldlpi -lc -linetutil -lsocket -lscf
 
 SRCDIR =	../common
 $(LINTLIB) :=	SRCS = $(SRCDIR)/$(LINTSRC)
--- a/usr/src/lib/libdladm/common/libdllink.h	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/lib/libdladm/common/libdllink.h	Fri Apr 27 09:21:03 2007 -0700
@@ -64,6 +64,7 @@
 #define	DLADM_PROP_VAL_MAX	25
 
 #define		DLADM_SECOBJ_CLASS_WEP	0
+#define		DLADM_SECOBJ_CLASS_WPA	1
 typedef int	dladm_secobj_class_t;
 
 typedef void (dladm_walkcb_t)(void *, const char *);
--- a/usr/src/lib/libdladm/common/libdlwlan.c	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/lib/libdladm/common/libdlwlan.c	Fri Apr 27 09:21:03 2007 -0700
@@ -37,9 +37,10 @@
 #include <net/if.h>
 #include <net/if_dl.h>
 #include <net/if_types.h>
+#include <libscf.h>
 #include <libdlwlan.h>
 #include <libdlwlan_impl.h>
-#include <inet/wifi_ioctl.h>
+#include <net/wpa.h>
 
 typedef struct val_desc {
 	char		*vd_name;
@@ -63,6 +64,9 @@
 	wl_pd_checkf_t	*pd_check;
 } prop_desc_t;
 
+static int	wpa_instance_create(const char *, void *);
+static int	wpa_instance_delete(const char *);
+
 static int 	do_get_bsstype(int, wldp_t *);
 static int 	do_get_essid(int, wldp_t *);
 static int 	do_get_bssid(int, wldp_t *);
@@ -76,13 +80,15 @@
 static int	do_get_powermode(int, wldp_t *);
 static int	do_get_radio(int, wldp_t *);
 static int	do_get_mode(int, wldp_t *);
+static int	do_get_capability(int, wldp_t *);
+static int	do_get_wpamode(int, wldp_t *);
 
 static int	do_set_bsstype(int, wldp_t *, dladm_wlan_bsstype_t *);
 static int	do_set_authmode(int, wldp_t *, dladm_wlan_auth_t *);
 static int	do_set_encryption(int, wldp_t *, dladm_wlan_secmode_t *);
 static int	do_set_essid(int, wldp_t *, dladm_wlan_essid_t *);
 static int	do_set_createibss(int, wldp_t *, boolean_t *);
-static int	do_set_wepkey(int, wldp_t *, dladm_wlan_wepkey_t *, uint_t);
+static int	do_set_key(int, wldp_t *, dladm_wlan_key_t *, uint_t);
 static int	do_set_rate(int, wldp_t *, dladm_wlan_rates_t *);
 static int	do_set_powermode(int, wldp_t *, dladm_wlan_powermode_t *);
 static int	do_set_radio(int, wldp_t *, dladm_wlan_radio_t *);
@@ -90,7 +96,7 @@
 
 static int	open_link(const char *);
 static int	do_scan(int, wldp_t *);
-static int	do_disconnect(int, wldp_t *);
+static int	do_disconnect(const char *, int, wldp_t *);
 static boolean_t find_val_by_name(const char *, val_desc_t *, uint_t, uint_t *);
 static boolean_t find_name_by_val(uint_t, val_desc_t *, uint_t, char **);
 static void	generate_essid(dladm_wlan_essid_t *);
@@ -110,7 +116,8 @@
 
 static val_desc_t 	secmode_vals[] = {
 	{ "none",	DLADM_WLAN_SECMODE_NONE		},
-	{ "wep",	DLADM_WLAN_SECMODE_WEP		}
+	{ "wep",	DLADM_WLAN_SECMODE_WEP		},
+	{ "wpa",	DLADM_WLAN_SECMODE_WPA		}
 };
 
 static val_desc_t 	strength_vals[] = {
@@ -293,6 +300,8 @@
 
 	attrp->wa_secmode = (wlp->wl_ess_conf_wepenabled ==
 	    WL_ENC_WEP ? DLADM_WLAN_SECMODE_WEP : DLADM_WLAN_SECMODE_NONE);
+	if (wlp->wl_ess_conf_reserved[0] > 0)
+		attrp->wa_secmode = DLADM_WLAN_SECMODE_WPA;
 	attrp->wa_valid |= DLADM_WLAN_ATTR_SECMODE;
 
 	attrp->wa_bsstype = (wlp->wl_ess_conf_bsstype == WL_BSS_BSS ?
@@ -353,6 +362,11 @@
 		goto done;
 	}
 
+	if (func == NULL) {
+		status = DLADM_STATUS_OK;
+		goto done;
+	}
+
 	if (do_get_esslist(fd, gbuf) < 0) {
 		status = DLADM_STATUS_FAILED;
 		goto done;
@@ -373,7 +387,7 @@
 			goto done;
 		}
 		if (IS_CONNECTED(gbuf))
-			(void) do_disconnect(fd, gbuf);
+			(void) do_disconnect(link, fd, gbuf);
 	}
 
 	status = DLADM_STATUS_OK;
@@ -490,17 +504,20 @@
 	return (B_TRUE);
 }
 
+#define	IEEE80211_C_WPA		0x01800000
+
 static dladm_status_t
-do_connect(int fd, wldp_t *gbuf, dladm_wlan_attr_t *attrp,
+do_connect(const char *link, int fd, wldp_t *gbuf, dladm_wlan_attr_t *attrp,
     boolean_t create_ibss, void *keys, uint_t key_count, int timeout)
 {
 	dladm_wlan_secmode_t		secmode;
 	dladm_wlan_auth_t		authmode;
 	dladm_wlan_bsstype_t		bsstype;
 	dladm_wlan_essid_t		essid;
-	boolean_t		essid_valid = B_FALSE;
+	boolean_t			essid_valid = B_FALSE;
 	dladm_wlan_channel_t		channel;
-	hrtime_t		start;
+	hrtime_t			start;
+	wl_capability_t			*caps;
 
 	if ((attrp->wa_valid & DLADM_WLAN_ATTR_CHANNEL) != 0) {
 		channel = attrp->wa_channel;
@@ -529,8 +546,16 @@
 	if (secmode == DLADM_WLAN_SECMODE_WEP) {
 		if (keys == NULL || key_count == 0 || key_count > MAX_NWEPKEYS)
 			return (DLADM_STATUS_BADARG);
-		if (do_set_wepkey(fd, gbuf, keys, key_count) < 0)
+		if (do_set_key(fd, gbuf, keys, key_count) < 0)
 			goto fail;
+	} else if (secmode == DLADM_WLAN_SECMODE_WPA) {
+		if (keys == NULL || key_count == 0 || key_count > MAX_NWEPKEYS)
+			return (DLADM_STATUS_BADARG);
+		if (do_get_capability(fd, gbuf) < 0)
+			goto fail;
+		caps = (wl_capability_t *)(gbuf->wldp_buf);
+		if ((caps->caps & IEEE80211_C_WPA) == 0)
+			return (DLADM_STATUS_NOTSUP);
 	}
 
 	if (create_ibss) {
@@ -556,6 +581,13 @@
 	if (do_set_essid(fd, gbuf, &essid) < 0)
 		goto fail;
 
+	/*
+	 * Because wpa daemon needs getting essid from driver,
+	 * we need call do_set_essid() first, then call wpa_instance_create().
+	 */
+	if (secmode == DLADM_WLAN_SECMODE_WPA && keys != NULL)
+		(void) wpa_instance_create(link, keys);
+
 	start = gethrtime();
 	for (;;) {
 		if (do_get_linkstatus(fd, gbuf) < 0)
@@ -614,7 +646,7 @@
 	if ((flags & DLADM_WLAN_CONNECT_NOSCAN) != 0 ||
 	    (create_ibss && attrp != NULL &&
 	    (attrp->wa_valid & DLADM_WLAN_ATTR_ESSID) == 0)) {
-		status = do_connect(fd, gbuf, attrp,
+		status = do_connect(link, fd, gbuf, attrp,
 		    create_ibss, keys, key_count, timeout);
 		goto done;
 	}
@@ -632,7 +664,7 @@
 			status = DLADM_STATUS_NOTFOUND;
 			goto done;
 		}
-		status = do_connect(fd, gbuf, attrp, create_ibss,
+		status = do_connect(link, fd, gbuf, attrp, create_ibss,
 		    keys, key_count, timeout);
 		goto done;
 	}
@@ -654,7 +686,7 @@
 	for (i = 0; i < state.cs_count; i++) {
 		dladm_wlan_attr_t	*ap = wl_list[i];
 
-		status = do_connect(fd, gbuf, ap, create_ibss, keys,
+		status = do_connect(link, fd, gbuf, ap, create_ibss, keys,
 		    key_count, timeout);
 		if (status == DLADM_STATUS_OK)
 			break;
@@ -662,15 +694,15 @@
 		if (!set_authmode) {
 			ap->wa_auth = DLADM_WLAN_AUTH_SHARED;
 			ap->wa_valid |= DLADM_WLAN_ATTR_AUTH;
-			status = do_connect(fd, gbuf, ap, create_ibss, keys,
-			    key_count, timeout);
+			status = do_connect(link, fd, gbuf, ap, create_ibss,
+			    keys, key_count, timeout);
 			if (status == DLADM_STATUS_OK)
 				break;
 		}
 	}
 done:
 	if ((status != DLADM_STATUS_OK) && (status != DLADM_STATUS_ISCONN))
-		(void) do_disconnect(fd, gbuf);
+		(void) do_disconnect(link, fd, gbuf);
 
 	while (state.cs_list != NULL) {
 		nodep = state.cs_list;
@@ -708,7 +740,7 @@
 		goto done;
 	}
 
-	if (do_disconnect(fd, gbuf) < 0) {
+	if (do_disconnect(link, fd, gbuf) < 0) {
 		status = DLADM_STATUS_FAILED;
 		goto done;
 	}
@@ -834,10 +866,9 @@
 	attrp->la_valid |= DLADM_WLAN_LINKATTR_STATUS;
 	if (!IS_CONNECTED(gbuf)) {
 		attrp->la_status = DLADM_WLAN_LINKSTATUS_DISCONNECTED;
-		status = DLADM_STATUS_OK;
-		goto done;
+	} else {
+		attrp->la_status = DLADM_WLAN_LINKSTATUS_CONNECTED;
 	}
-	attrp->la_status = DLADM_WLAN_LINKSTATUS_CONNECTED;
 
 	if (do_get_essid(fd, gbuf) < 0)
 		goto done;
@@ -856,6 +887,12 @@
 
 	wl_attrp->wa_valid |= DLADM_WLAN_ATTR_BSSID;
 
+	if (attrp->la_status == DLADM_WLAN_LINKSTATUS_DISCONNECTED) {
+		attrp->la_valid |= DLADM_WLAN_LINKATTR_WLAN;
+		status = DLADM_STATUS_OK;
+		goto done;
+	}
+
 	if (do_get_encryption(fd, gbuf) < 0)
 		goto done;
 
@@ -869,6 +906,9 @@
 	case WL_ENC_WEP:
 		wl_attrp->wa_secmode = DLADM_WLAN_SECMODE_WEP;
 		break;
+	case WL_ENC_WPA:
+		wl_attrp->wa_secmode = DLADM_WLAN_SECMODE_WPA;
+		break;
 	default:
 		wl_attrp->wa_valid &= ~DLADM_WLAN_ATTR_SECMODE;
 		break;
@@ -1445,8 +1485,12 @@
 }
 
 static int
-do_disconnect(int fd, wldp_t *gbuf)
+do_disconnect(const char *link, int fd, wldp_t *gbuf)
 {
+	if (do_get_wpamode(fd, gbuf) == 0 && ((wl_wpa_t *)(gbuf->
+	    wldp_buf))->wpa_flag > 0)
+		(void) wpa_instance_delete(link);
+
 	return (do_cmd_ioctl(fd, gbuf, WL_DISASSOCIATE));
 }
 
@@ -1695,6 +1739,8 @@
 	case DLADM_WLAN_SECMODE_WEP:
 		encryption = WL_ENC_WEP;
 		break;
+	case DLADM_WLAN_SECMODE_WPA:
+		return (0);
 	default:
 		return (-1);
 	}
@@ -1703,13 +1749,13 @@
 }
 
 static int
-do_set_wepkey(int fd, wldp_t *gbuf, dladm_wlan_wepkey_t *keys,
+do_set_key(int fd, wldp_t *gbuf, dladm_wlan_key_t *keys,
     uint_t key_count)
 {
 	int			i;
 	wl_wep_key_t		*wkp;
 	wl_wep_key_tab_t	wepkey_tab;
-	dladm_wlan_wepkey_t	*kp;
+	dladm_wlan_key_t	*kp;
 
 	if (key_count == 0 || key_count > MAX_NWEPKEYS || keys == NULL)
 		return (-1);
@@ -1935,3 +1981,587 @@
 	(void) snprintf(essid->we_bytes, DLADM_WLAN_MAX_ESSID_LEN, "%d",
 	    random());
 }
+
+static int
+do_get_capability(int fd, wldp_t *gbuf)
+{
+	return (do_get_ioctl(fd, gbuf, WL_CAPABILITY));
+}
+
+static int
+do_get_wpamode(int fd, wldp_t *gbuf)
+{
+	return (do_get_ioctl(fd, gbuf, WL_WPA));
+}
+
+static dladm_status_t
+ioctl_get(const char *link, int id, void *gbuf)
+{
+	int		fd;
+
+	if ((fd = open_link(link)) < 0)
+		return (DLADM_STATUS_LINKINVAL);
+	(void) do_get_ioctl(fd, gbuf, id);
+
+	(void) close(fd);
+	return (dladm_wlan_wlresult2status(gbuf));
+}
+
+static dladm_status_t
+ioctl_set(const char *link, int id, void *buf, uint_t buflen)
+{
+	int		fd;
+	wldp_t 		*gbuf;
+	dladm_status_t	status;
+
+	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL)
+		return (DLADM_STATUS_NOMEM);
+
+	if ((fd = open_link(link)) < 0)
+		return (DLADM_STATUS_LINKINVAL);
+	(void) do_set_ioctl(fd, gbuf, id, buf, buflen);
+
+	(void) close(fd);
+	status = dladm_wlan_wlresult2status(gbuf);
+	free(gbuf);
+
+	return (status);
+}
+
+dladm_status_t
+dladm_wlan_wpa_get_sr(const char *link, dladm_wlan_ess_t *sr, uint_t escnt,
+    uint_t *estot)
+{
+	int		i, n;
+	wldp_t 		*gbuf;
+	wl_wpa_ess_t	*es;
+	dladm_status_t	status;
+
+	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL)
+		return (DLADM_STATUS_NOMEM);
+
+	status = ioctl_get(link, WL_SCANRESULTS, gbuf);
+
+	if (status == DLADM_STATUS_OK) {
+		es = (wl_wpa_ess_t *)(gbuf->wldp_buf);
+		n = (es->count > escnt) ? escnt : es->count;
+		for (i = 0; i < n; i ++) {
+			(void) memcpy(sr[i].we_bssid.wb_bytes, es->ess[i].bssid,
+			    DLADM_WLAN_BSSID_LEN);
+			sr[i].we_ssid_len = es->ess[i].ssid_len;
+			(void) memcpy(sr[i].we_ssid.we_bytes, es->ess[i].ssid,
+			    es->ess[i].ssid_len);
+			sr[i].we_wpa_ie_len = es->ess[i].wpa_ie_len;
+			(void) memcpy(sr[i].we_wpa_ie, es->ess[i].wpa_ie,
+			    es->ess[i].wpa_ie_len);
+			sr[i].we_freq = es->ess[i].freq;
+		}
+		*estot = n;
+	}
+
+	free(gbuf);
+	return (status);
+}
+
+dladm_status_t
+dladm_wlan_wpa_set_ie(const char *link, uint8_t *wpa_ie,
+    uint_t wpa_ie_len)
+{
+	wl_wpa_ie_t *ie;
+	uint_t len;
+	dladm_status_t	status;
+
+	if (wpa_ie_len > DLADM_WLAN_MAX_WPA_IE_LEN)
+		return (DLADM_STATUS_BADARG);
+	len = sizeof (wl_wpa_ie_t) + wpa_ie_len;
+	ie = malloc(len);
+	if (ie == NULL)
+		return (DLADM_STATUS_NOMEM);
+
+	(void) memset(ie, 0, len);
+	ie->wpa_ie_len = wpa_ie_len;
+	(void) memcpy(ie->wpa_ie, wpa_ie, wpa_ie_len);
+
+	status = ioctl_set(link, WL_SETOPTIE, ie, len);
+	free(ie);
+
+	return (status);
+}
+
+dladm_status_t
+dladm_wlan_wpa_set_wpa(const char *link, boolean_t flag)
+{
+	wl_wpa_t wpa;
+
+	wpa.wpa_flag = flag;
+	return (ioctl_set(link, WL_WPA, &wpa, sizeof (wl_wpa_t)));
+}
+
+dladm_status_t
+dladm_wlan_wpa_del_key(const char *link, uint_t key_idx,
+    const dladm_wlan_bssid_t *addr)
+{
+	wl_del_key_t wk;
+
+	wk.idk_keyix = key_idx;
+	if (addr != NULL)
+		(void) memcpy((char *)wk.idk_macaddr, addr->wb_bytes,
+		    DLADM_WLAN_BSSID_LEN);
+
+	return (ioctl_set(link, WL_DELKEY, &wk, sizeof (wl_del_key_t)));
+}
+
+dladm_status_t
+dladm_wlan_wpa_set_key(const char *link, dladm_wlan_cipher_t cipher,
+    const dladm_wlan_bssid_t *addr, boolean_t set_tx, uint64_t seq,
+    uint_t key_idx, uint8_t *key, uint_t key_len)
+{
+	wl_key_t wk;
+
+	(void) memset(&wk, 0, sizeof (wl_key_t));
+	switch (cipher) {
+	case DLADM_WLAN_CIPHER_WEP:
+		wk.ik_type = IEEE80211_CIPHER_WEP;
+		break;
+	case DLADM_WLAN_CIPHER_TKIP:
+		wk.ik_type = IEEE80211_CIPHER_TKIP;
+		break;
+	case DLADM_WLAN_CIPHER_AES_OCB:
+		wk.ik_type = IEEE80211_CIPHER_AES_OCB;
+		break;
+	case DLADM_WLAN_CIPHER_AES_CCM:
+		wk.ik_type = IEEE80211_CIPHER_AES_CCM;
+		break;
+	case DLADM_WLAN_CIPHER_CKIP:
+		wk.ik_type = IEEE80211_CIPHER_CKIP;
+		break;
+	case DLADM_WLAN_CIPHER_NONE:
+		wk.ik_type = IEEE80211_CIPHER_NONE;
+		break;
+	default:
+		return (DLADM_STATUS_BADARG);
+	}
+	wk.ik_flags = IEEE80211_KEY_RECV;
+	if (set_tx) {
+		wk.ik_flags |= IEEE80211_KEY_XMIT | IEEE80211_KEY_DEFAULT;
+		(void) memcpy(wk.ik_macaddr, addr->wb_bytes,
+		    DLADM_WLAN_BSSID_LEN);
+	} else
+		(void) memset(wk.ik_macaddr, 0, DLADM_WLAN_BSSID_LEN);
+	wk.ik_keyix = key_idx;
+	wk.ik_keylen = key_len;
+	(void) memcpy(&wk.ik_keyrsc, &seq, 6);	/* only use 48-bit of seq */
+	(void) memcpy(wk.ik_keydata, key, key_len);
+
+	return (ioctl_set(link, WL_KEY, &wk, sizeof (wl_key_t)));
+}
+
+dladm_status_t
+dladm_wlan_wpa_set_mlme(const char *link, dladm_wlan_mlme_op_t op,
+    dladm_wlan_reason_t reason, dladm_wlan_bssid_t *bssid)
+{
+	wl_mlme_t mlme;
+
+	(void) memset(&mlme, 0, sizeof (wl_mlme_t));
+	switch (op) {
+	case DLADM_WLAN_MLME_ASSOC:
+		mlme.im_op = IEEE80211_MLME_ASSOC;
+		break;
+	case DLADM_WLAN_MLME_DISASSOC:
+		mlme.im_op = IEEE80211_MLME_DISASSOC;
+		break;
+	default:
+		return (DLADM_STATUS_BADARG);
+	}
+	mlme.im_reason = reason;
+	if (bssid != NULL)
+		(void) memcpy(mlme.im_macaddr, bssid->wb_bytes,
+		    DLADM_WLAN_BSSID_LEN);
+
+	return (ioctl_set(link, WL_MLME, &mlme, sizeof (wl_mlme_t)));
+}
+
+/*
+ * routines of create instance
+ */
+static scf_propertygroup_t *
+add_property_group_to_instance(scf_handle_t *handle, scf_instance_t *instance,
+    const char *pg_name, const char *pg_type)
+{
+	scf_propertygroup_t *pg;
+
+	pg = scf_pg_create(handle);
+	if (pg == NULL)
+		return (NULL);
+
+	if (scf_instance_add_pg(instance, pg_name, pg_type, 0, pg) != 0) {
+		scf_pg_destroy(pg);
+		return (NULL);
+	}
+
+	return (pg);
+}
+
+static int
+add_new_property(scf_handle_t *handle, const char *prop_name,
+    scf_type_t type, const char *val, scf_transaction_t *tx)
+{
+	scf_value_t *value = NULL;
+	scf_transaction_entry_t *entry = NULL;
+
+	entry = scf_entry_create(handle);
+	if (entry == NULL)
+		goto out;
+
+	value = scf_value_create(handle);
+	if (value == NULL)
+		goto out;
+
+	if (scf_transaction_property_new(tx, entry, prop_name, type) != 0)
+		goto out;
+
+	if (scf_value_set_from_string(value, type, val) != 0)
+		goto out;
+
+	if (scf_entry_add_value(entry, value) != 0)
+		goto out;
+
+	return (DLADM_WLAN_SVC_SUCCESS);
+
+out:
+	if (value != NULL)
+		scf_value_destroy(value);
+	if (entry != NULL)
+		scf_entry_destroy(entry);
+
+	return (DLADM_WLAN_SVC_FAILURE);
+}
+
+/*
+ * DLADM_WLAN_SVC_APP_FAILURE means allocate buffer failed.
+ */
+static int
+add_pg_method(scf_handle_t *handle, scf_instance_t *instance,
+    const char *pg_name, const char *flags)
+{
+	int			rv, size;
+	int			status = DLADM_WLAN_SVC_FAILURE;
+	char			*command = NULL;
+	scf_transaction_t	*tran = NULL;
+	scf_propertygroup_t	*pg;
+
+	pg = add_property_group_to_instance(handle, instance,
+	    pg_name, SCF_GROUP_METHOD);
+	if (pg == NULL)
+		goto out;
+
+	tran = scf_transaction_create(handle);
+	if (tran == NULL)
+		goto out;
+
+	size = strlen(SVC_METHOD) + strlen("  ") + strlen(flags) + 1;
+	command = malloc(size);
+	if (command == NULL) {
+		status = DLADM_WLAN_SVC_APP_FAILURE;
+		goto out;
+	}
+	(void) snprintf(command, size, "%s %s", SVC_METHOD, flags);
+
+	do {
+		if (scf_transaction_start(tran, pg) != 0)
+			goto out;
+
+		if (add_new_property(handle, SCF_PROPERTY_EXEC,
+		    SCF_TYPE_ASTRING, command, tran) !=
+		    DLADM_WLAN_SVC_SUCCESS) {
+			goto out;
+		}
+
+		rv = scf_transaction_commit(tran);
+		switch (rv) {
+		case 1:
+			status = DLADM_WLAN_SVC_SUCCESS;
+			goto out;
+		case 0:
+			scf_transaction_destroy_children(tran);
+			if (scf_pg_update(pg) == -1) {
+				goto out;
+			}
+			break;
+		case -1:
+		default:
+			goto out;
+		}
+	} while (rv == 0);
+
+out:
+	if (tran != NULL) {
+		scf_transaction_destroy_children(tran);
+		scf_transaction_destroy(tran);
+	}
+
+	if (pg != NULL)
+		scf_pg_destroy(pg);
+
+	if (command != NULL)
+		free(command);
+
+	return (status);
+}
+
+static int
+do_create_instance(scf_handle_t *handle, scf_service_t *svc,
+    const char *instance_name, const char *command)
+{
+	int status = DLADM_WLAN_SVC_FAILURE;
+	char *buf;
+	ssize_t max_fmri_len;
+	scf_instance_t *instance;
+
+	instance = scf_instance_create(handle);
+	if (instance == NULL)
+		goto out;
+
+	if (scf_service_add_instance(svc, instance_name, instance) != 0) {
+		if (scf_error() == SCF_ERROR_EXISTS)
+			/* Let the caller deal with the duplicate instance */
+			status = DLADM_WLAN_SVC_INSTANCE_EXISTS;
+		goto out;
+	}
+
+	if (add_pg_method(handle, instance, "start",
+	    command) != DLADM_WLAN_SVC_SUCCESS) {
+		goto out;
+	}
+
+	/* enabling the instance */
+	max_fmri_len = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
+	if ((buf = malloc(max_fmri_len + 1)) == NULL)
+		goto out;
+
+	if (scf_instance_to_fmri(instance, buf, max_fmri_len + 1) > 0) {
+		if ((smf_disable_instance(buf, 0) != 0) ||
+		    (smf_enable_instance(buf, SMF_TEMPORARY) != 0)) {
+			goto out;
+		}
+		status = DLADM_WLAN_SVC_SUCCESS;
+	}
+
+out:
+	if (instance != NULL)
+		scf_instance_destroy(instance);
+	return (status);
+}
+
+static int
+create_instance(const char *instance_name, const char *command)
+{
+	int status = DLADM_WLAN_SVC_FAILURE;
+	scf_service_t *svc = NULL;
+	scf_handle_t *handle = NULL;
+
+	handle = scf_handle_create(SCF_VERSION);
+	if (handle == NULL)
+		goto out;
+
+	if (scf_handle_bind(handle) == -1)
+		goto out;
+
+	if ((svc = scf_service_create(handle)) == NULL)
+		goto out;
+
+	if (scf_handle_decode_fmri(handle, SERVICE_NAME, NULL, svc,
+	    NULL, NULL, NULL, SCF_DECODE_FMRI_EXACT) != 0)
+		goto out;
+
+	status = do_create_instance(handle, svc, instance_name, command);
+
+out:
+	if (svc != NULL)
+		scf_service_destroy(svc);
+
+	if (handle != NULL) {
+		(void) scf_handle_unbind(handle);
+		scf_handle_destroy(handle);
+	}
+
+	return (status);
+}
+
+/*
+ * routines of delete instance
+ */
+#define	DEFAULT_TIMEOUT	60000000
+#define	INIT_WAIT_USECS	50000
+
+static void
+wait_until_disabled(scf_handle_t *handle, char *fmri)
+{
+	char		*state;
+	useconds_t	max;
+	useconds_t	usecs;
+	uint64_t	*cp = NULL;
+	scf_simple_prop_t *sp = NULL;
+
+	max = DEFAULT_TIMEOUT;
+
+	if (((sp = scf_simple_prop_get(handle, fmri, "stop",
+	    SCF_PROPERTY_TIMEOUT)) != NULL) &&
+	    ((cp = scf_simple_prop_next_count(sp)) != NULL) && (*cp != 0))
+		max = (*cp) * 1000000;	/* convert to usecs */
+
+	if (sp != NULL)
+		scf_simple_prop_free(sp);
+
+	for (usecs = INIT_WAIT_USECS; max > 0; max -= usecs) {
+		/* incremental wait */
+		usecs *= 2;
+		usecs = (usecs > max) ? max : usecs;
+
+		(void) usleep(usecs);
+
+		/* Check state after the wait */
+		if ((state = smf_get_state(fmri)) != NULL) {
+			if (strcmp(state, "disabled") == 0)
+				return;
+		}
+	}
+}
+
+static int
+delete_instance(const char *instance_name)
+{
+	int		status = DLADM_WLAN_SVC_FAILURE;
+	char		*buf;
+	ssize_t		max_fmri_len;
+	scf_scope_t	*scope = NULL;
+	scf_service_t	*svc = NULL;
+	scf_handle_t	*handle = NULL;
+	scf_instance_t	*instance;
+
+	handle = scf_handle_create(SCF_VERSION);
+	if (handle == NULL)
+		goto out;
+
+	if (scf_handle_bind(handle) == -1)
+		goto out;
+
+	if ((scope = scf_scope_create(handle)) == NULL)
+		goto out;
+
+	if ((svc = scf_service_create(handle)) == NULL)
+		goto out;
+
+	if (scf_handle_get_scope(handle, SCF_SCOPE_LOCAL, scope) == -1)
+		goto out;
+
+	if (scf_scope_get_service(scope, SERVICE_NAME, svc) < 0)
+		goto out;
+
+	instance = scf_instance_create(handle);
+	if (instance == NULL)
+		goto out;
+
+	if (scf_service_get_instance(svc, instance_name, instance) != 0) {
+		scf_error_t scf_errnum = scf_error();
+
+		if (scf_errnum == SCF_ERROR_NOT_FOUND)
+			status = DLADM_WLAN_SVC_SUCCESS;
+
+		scf_instance_destroy(instance);
+		goto out;
+	}
+
+	max_fmri_len = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
+	if ((buf = malloc(max_fmri_len + 1)) == NULL) {
+		scf_instance_destroy(instance);
+		goto out;
+	}
+
+	if (scf_instance_to_fmri(instance, buf, max_fmri_len + 1) > 0) {
+		char *state;
+
+		state = smf_get_state(buf);
+		if (state && (strcmp(state, SCF_STATE_STRING_ONLINE) == 0 ||
+		    strcmp(state, SCF_STATE_STRING_DEGRADED) == 0)) {
+			if (smf_disable_instance(buf, 0) == 0) {
+				/*
+				 * Wait for some time till timeout to avoid
+				 * a race with scf_instance_delete() below.
+				 */
+				wait_until_disabled(handle, buf);
+			}
+		}
+	}
+
+	if (scf_instance_delete(instance) != 0) {
+		scf_instance_destroy(instance);
+		goto out;
+	}
+
+	scf_instance_destroy(instance);
+
+	status = DLADM_WLAN_SVC_SUCCESS;
+
+out:
+	if (svc != NULL)
+		scf_service_destroy(svc);
+
+	if (scope != NULL)
+		scf_scope_destroy(scope);
+
+	if (handle != NULL) {
+		(void) scf_handle_unbind(handle);
+		scf_handle_destroy(handle);
+	}
+
+	return (status);
+}
+
+/*
+ * DLADM_WLAN_SVC_APP_FAILURE means allocate buffer failed.
+ */
+static int
+wpa_instance_create(const char *instance_name, void *key)
+{
+	int		status = DLADM_WLAN_SVC_FAILURE;
+	char		*command = NULL;
+	char		*wk_name = ((dladm_wlan_key_t *)key)->wk_name;
+	int		size;
+
+	size = strlen(instance_name) + strlen(" -i  -k ") + strlen(wk_name) + 1;
+	command = malloc(size);
+	if (command == NULL) {
+		status = DLADM_WLAN_SVC_APP_FAILURE;
+		goto out;
+	}
+	(void) snprintf(command, size, "-i %s -k %s", instance_name, wk_name);
+
+	status = create_instance(instance_name, command);
+	if (status == DLADM_WLAN_SVC_INSTANCE_EXISTS) {
+		/*
+		 * Delete the existing instance and create a new instance
+		 * with the supplied arguments.
+		 */
+		if ((status = delete_instance(instance_name)) ==
+		    DLADM_WLAN_SVC_SUCCESS) {
+			status = create_instance(instance_name, command);
+		}
+	}
+
+out:
+	if (command != NULL)
+		free(command);
+
+	return (status);
+}
+
+static int
+wpa_instance_delete(const char *instance_name)
+{
+	int status;
+
+	status = delete_instance(instance_name);
+
+	return (status);
+}
--- a/usr/src/lib/libdladm/common/libdlwlan.h	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/lib/libdladm/common/libdlwlan.h	Fri Apr 27 09:21:03 2007 -0700
@@ -49,6 +49,8 @@
 
 #define	DLADM_WLAN_MAX_ESSID_LEN	32	/* per 802.11 spec */
 #define	DLADM_WLAN_BSSID_LEN		6	/* per 802.11 spec */
+#define	DLADM_WLAN_WPA_KEY_LEN		32	/* per 802.11i spec */
+#define	DLADM_WLAN_MAX_WPA_IE_LEN	40	/* per 802.11i spec */
 
 #define	DLADM_WLAN_CONNECT_TIMEOUT_DEFAULT	10
 #define	DLADM_WLAN_CONNECT_CREATEIBSS		0x00000001
@@ -62,9 +64,38 @@
 	uint8_t	wb_bytes[DLADM_WLAN_BSSID_LEN];
 } dladm_wlan_bssid_t;
 
+typedef struct dladm_wlan_ess {
+	dladm_wlan_bssid_t	we_bssid;
+	dladm_wlan_essid_t	we_ssid;
+	uint_t			we_ssid_len;
+	uint8_t			we_wpa_ie[DLADM_WLAN_MAX_WPA_IE_LEN];
+	uint_t			we_wpa_ie_len;
+	int			we_freq;
+} dladm_wlan_ess_t;
+
+typedef enum {
+	DLADM_WLAN_CIPHER_WEP		= 0,
+	DLADM_WLAN_CIPHER_TKIP,
+	DLADM_WLAN_CIPHER_AES_OCB,
+	DLADM_WLAN_CIPHER_AES_CCM,
+	DLADM_WLAN_CIPHER_CKIP,
+	DLADM_WLAN_CIPHER_NONE
+} dladm_wlan_cipher_t;
+
+typedef enum {
+	DLADM_WLAN_MLME_ASSOC		= 1,	/* associate station */
+	DLADM_WLAN_MLME_DISASSOC	= 2	/* disassociate station */
+} dladm_wlan_mlme_op_t;
+
+typedef enum {
+	DLADM_WLAN_REASON_UNSPECIFIED	= 1,
+	DLADM_WLAN_REASON_DISASSOC_LEAVING	= 5
+} dladm_wlan_reason_t;
+
 typedef enum {
 	DLADM_WLAN_SECMODE_NONE = 1,
-	DLADM_WLAN_SECMODE_WEP
+	DLADM_WLAN_SECMODE_WEP,
+	DLADM_WLAN_SECMODE_WPA
 } dladm_wlan_secmode_t;
 
 typedef enum {
@@ -101,6 +132,13 @@
 typedef uint32_t dladm_wlan_speed_t;
 typedef	uint32_t dladm_wlan_channel_t;
 
+typedef enum {
+	DLADM_WLAN_SVC_SUCCESS,
+	DLADM_WLAN_SVC_FAILURE,
+	DLADM_WLAN_SVC_APP_FAILURE,
+	DLADM_WLAN_SVC_INSTANCE_EXISTS
+} dladm_wlan_svc_status_t;
+
 enum {
 	DLADM_WLAN_ATTR_ESSID	= 0x00000001,
 	DLADM_WLAN_ATTR_BSSID	= 0x00000002,
@@ -137,15 +175,16 @@
 
 #define	DLADM_WLAN_WEPKEY64_LEN		5 	/* per WEP spec */
 #define	DLADM_WLAN_WEPKEY128_LEN	13 	/* per WEP spec */
-#define	DLADM_WLAN_MAX_WEPKEY_LEN	13	/* per WEP spec */
+#define	DLADM_WLAN_MAX_KEY_LEN		64	/* per WEP/WPA spec */
 #define	DLADM_WLAN_MAX_WEPKEYS		4 	/* MAX_NWEPKEYS */
-#define	DLADM_WLAN_MAX_WEPKEYNAME_LEN	64
-typedef struct dladm_wlan_wepkey {
+#define	DLADM_WLAN_MAX_KEYNAME_LEN	64
+typedef struct dladm_wlan_key {
 	uint_t		wk_idx;
 	uint_t		wk_len;
-	uint8_t		wk_val[DLADM_WLAN_MAX_WEPKEY_LEN];
-	char		wk_name[DLADM_WLAN_MAX_WEPKEYNAME_LEN];
-} dladm_wlan_wepkey_t;
+	uint8_t		wk_val[DLADM_WLAN_MAX_KEY_LEN];
+	char		wk_name[DLADM_WLAN_MAX_KEYNAME_LEN];
+	uint_t		wk_class;
+} dladm_wlan_key_t;
 
 extern dladm_status_t	dladm_wlan_scan(const char *, void *,
 			    boolean_t (*)(void *, dladm_wlan_attr_t *));
@@ -163,6 +202,19 @@
 			    boolean_t (*)(void *, const char *));
 extern dladm_status_t	dladm_wlan_get_prop(const char *, dladm_prop_type_t,
 			    const char *, char **, uint_t *);
+/* WPA support routines */
+extern dladm_status_t	dladm_wlan_wpa_get_sr(const char *,
+			    dladm_wlan_ess_t *, uint_t, uint_t *);
+extern dladm_status_t	dladm_wlan_wpa_set_ie(const char *, uint8_t *, uint_t);
+extern dladm_status_t	dladm_wlan_wpa_set_wpa(const char *, boolean_t);
+extern dladm_status_t	dladm_wlan_wpa_del_key(const char *,
+			    uint_t, const dladm_wlan_bssid_t *);
+extern dladm_status_t	dladm_wlan_wpa_set_key(const char *,
+			    dladm_wlan_cipher_t, const dladm_wlan_bssid_t *,
+			    boolean_t, uint64_t, uint_t, uint8_t *, uint_t);
+extern dladm_status_t	dladm_wlan_wpa_set_mlme(const char *,
+			    dladm_wlan_mlme_op_t,
+			    dladm_wlan_reason_t, dladm_wlan_bssid_t *);
 
 extern const char	*dladm_wlan_essid2str(dladm_wlan_essid_t *, char *);
 extern const char	*dladm_wlan_bssid2str(dladm_wlan_bssid_t *, char *);
--- a/usr/src/lib/libdladm/common/mapfile-vers	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/lib/libdladm/common/mapfile-vers	Fri Apr 27 09:21:03 2007 -0700
@@ -91,6 +91,12 @@
 	dladm_wlan_str2auth;
 	dladm_wlan_str2bsstype;
 	dladm_wlan_str2linkstatus;
+	dladm_wlan_wpa_get_sr;
+	dladm_wlan_wpa_set_ie;
+	dladm_wlan_wpa_set_wpa;
+	dladm_wlan_wpa_del_key;
+	dladm_wlan_wpa_set_key;
+	dladm_wlan_wpa_set_mlme;
 
     local:
 	*;
--- a/usr/src/lib/libdladm/common/secobj.c	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/lib/libdladm/common/secobj.c	Fri Apr 27 09:21:03 2007 -0700
@@ -51,7 +51,8 @@
 } secobj_class_info_t;
 
 static secobj_class_info_t secobj_class_table[] = {
-	{"wep",	DLD_SECOBJ_CLASS_WEP}
+	{"wep",	DLD_SECOBJ_CLASS_WEP},
+	{"wpa",	DLD_SECOBJ_CLASS_WPA}
 };
 
 #define	NSECOBJCLASS \
@@ -154,10 +155,12 @@
 		return (dladm_errno2status(errno));
 
 	if (i_dladm_ioctl(fd, DLDIOCSECOBJSET, &secobj_set,
-	    sizeof (secobj_set)) < 0)
+	    sizeof (secobj_set)) < 0) {
 		status = dladm_errno2status(errno);
+	}
 
 	(void) close(fd);
+
 	if (status != DLADM_STATUS_OK)
 		return (status);
 
--- a/usr/src/lib/libsecdb/auth_attr.txt	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/lib/libsecdb/auth_attr.txt	Fri Apr 27 09:21:03 2007 -0700
@@ -98,6 +98,7 @@
 solaris.smf.manage.sendmail:::Manage Sendmail Service States::help=SmfSendmailStates.html
 solaris.smf.manage.ssh:::Manage Secure Shell Service States::help=SmfSshStates.html
 solaris.smf.manage.system-log:::Manage Syslog Service States::help=SmfSyslogStates.html
+solaris.smf.manage.wpa:::Manage WPA Service States::help=SmfWpaStates.html
 solaris.smf.value.:::Change Values of SMF Service Properties::help=SmfValueHeader.html
 solaris.smf.value.nwam:::Change Values of SMF Network Auto-Magic Properties::help=SmfValueNWAM.html
 solaris.smf.value.routing:::Change Values of SMF Routing Properties::help=SmfValueRouting.html
--- a/usr/src/lib/libsecdb/help/auths/Makefile	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/lib/libsecdb/help/auths/Makefile	Fri Apr 27 09:21:03 2007 -0700
@@ -84,6 +84,7 @@
 	SmfValueHeader.html \
 	SmfValueNWAM.html \
 	SmfValueRouting.html \
+	SmfWpaStates.html \
 	NetworkHeader.html \
 	WifiConfig.html \
 	WifiWep.html \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsecdb/help/auths/SmfWpaStates.html	Fri Apr 27 09:21:03 2007 -0700
@@ -0,0 +1,44 @@
+<HTML>
+
+<!--
+    Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+    Use is subject to license terms.
+
+    CDDL HEADER START
+
+    The contents of this file are subject to the terms of the
+    Common Development and Distribution License (the "License").
+    You may not use this file except in compliance with the License.
+
+    You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+    or http://www.opensolaris.org/os/licensing.
+    See the License for the specific language governing permissions
+    and limitations under the License.
+
+    When distributing Covered Code, include this CDDL HEADER in each
+    file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+    If applicable, add the following below this CDDL HEADER, with the
+    fields enclosed by brackets "[]" replaced with your own identifying
+    information: Portions Copyright [yyyy] [name of copyright owner]
+
+    CDDL HEADER END
+-->
+<!-- SCCS keyword
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+-->
+
+<HEAD>
+<!--
+META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"
+-->
+</HEAD>
+<BODY>
+When Manage WPA Service States is in the Authorizations
+Include column, it grants the authorization to enable, disable or
+refresh the wpa daemon.
+<p>
+If Manage WPA Service States is grayed, then you are not
+entitled to Add or Remove this authorization.
+<BR>&nbsp;
+</BODY>
+</HTML>
--- a/usr/src/lib/libsecdb/prof_attr.txt	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/lib/libsecdb/prof_attr.txt	Fri Apr 27 09:21:03 2007 -0700
@@ -49,7 +49,7 @@
 Maintenance and Repair:::Maintain and repair a system:auths=solaris.smf.manage.system-log;help=RtMaintAndRepair.html
 Media Backup:::Backup files and file systems:help=RtMediaBkup.html
 Media Restore:::Restore files and file systems from backups:help=RtMediaRestore.html
-Network Management:::Manage the host and network configuration:auths=solaris.smf.manage.name-service-cache,solaris.smf.manage.bind,solaris.smf.value.routing,solaris.smf.manage.routing,solaris.smf.value.nwam,solaris.smf.manage.nwam;profiles=Network Wifi Management;help=RtNetMngmnt.html
+Network Management:::Manage the host and network configuration:auths=solaris.smf.manage.name-service-cache,solaris.smf.manage.bind,solaris.smf.value.routing,solaris.smf.manage.routing,solaris.smf.value.nwam,solaris.smf.manage.nwam,solaris.smf.manage.wpa;profiles=Network Wifi Management;help=RtNetMngmnt.html
 Network Security:::Manage network and host security:auths=solaris.smf.manage.ssh;profiles=Network Wifi Security,Network Link Security;help=RtNetSecure.html
 Network Wifi Management:::Manage wifi network configuration:auths=solaris.network.wifi.config;help=RtNetWifiMngmnt.html
 Network Wifi Security:::Manage wifi network security:auths=solaris.network.wifi.wep;help=RtNetWifiSecure.html
--- a/usr/src/lib/libsecdb/user_attr.txt	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/lib/libsecdb/user_attr.txt	Fri Apr 27 09:21:03 2007 -0700
@@ -1,13 +1,12 @@
 #
-# Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # CDDL HEADER START
 #
 # The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
 #
 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 # or http://www.opensolaris.org/os/licensing.
@@ -31,3 +30,4 @@
 root::::auths=solaris.*,solaris.grant;profiles=All;lock_after_retries=no
 lp::::profiles=Printer Management
 adm::::profiles=Log Management
+dladm::::auths=solaris.smf.manage.wpa,solaris.smf.modify
--- a/usr/src/pkgdefs/Makefile	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/pkgdefs/Makefile	Fri Apr 27 09:21:03 2007 -0700
@@ -381,6 +381,8 @@
 	SUNWxcu4  \
 	SUNWwlanr  \
 	SUNWwlanu  \
+	SUNWwpar  \
+	SUNWwpau  \
 	SUNWxcu6  \
 	SUNWxwdv \
 	SUNWpmr \
--- a/usr/src/pkgdefs/SUNW0on/prototype_com	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/pkgdefs/SUNW0on/prototype_com	Fri Apr 27 09:21:03 2007 -0700
@@ -244,6 +244,7 @@
 f none usr/lib/help/auths/locale/SmfValueHeader.html 444 root bin
 f none usr/lib/help/auths/locale/SmfValueNWAM.html 444 root bin
 f none usr/lib/help/auths/locale/SmfValueRouting.html 444 root bin
+f none usr/lib/help/auths/locale/SmfWpaStates.html 444 root bin
 f none usr/lib/help/auths/locale/NetworkHeader.html 444 root bin
 f none usr/lib/help/auths/locale/WifiConfig.html 444 root bin
 f none usr/lib/help/auths/locale/WifiWep.html 444 root bin
--- a/usr/src/pkgdefs/SUNWcsu/prototype_com	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/pkgdefs/SUNWcsu/prototype_com	Fri Apr 27 09:21:03 2007 -0700
@@ -490,6 +490,7 @@
 f none usr/lib/help/auths/locale/C/SmfValueHeader.html 444 root bin
 f none usr/lib/help/auths/locale/C/SmfValueNWAM.html 444 root bin
 f none usr/lib/help/auths/locale/C/SmfValueRouting.html 444 root bin
+f none usr/lib/help/auths/locale/C/SmfWpaStates.html 444 root bin
 f none usr/lib/help/auths/locale/C/SysDate.html 444 root bin
 f none usr/lib/help/auths/locale/C/SysHeader.html 444 root bin
 f none usr/lib/help/auths/locale/C/SysShutdown.html 444 root bin
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWwpar/Makefile	Fri Apr 27 09:21:03 2007 -0700
@@ -0,0 +1,38 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+include ../Makefile.com
+
+DATAFILES += i.manifest r.manifest
+
+.KEEP_STATE:
+
+all: $(FILES)
+install: all pkg
+
+include ../Makefile.targ
+include ../Makefile.prtarg
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWwpar/depend	Fri Apr 27 09:21:03 2007 -0700
@@ -0,0 +1,52 @@
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+# This package information file defines software dependencies associated
+# with the pkg.  You can define three types of pkg dependencies with this file:
+#	 P indicates a prerequisite for installation
+#	 I indicates an incompatible package
+#	 R indicates a reverse dependency
+# <pkg.abbr> see pkginfo(4), PKG parameter
+# <name> see pkginfo(4), NAME parameter
+# <version> see pkginfo(4), VERSION parameter
+# <arch> see pkginfo(4), ARCH parameter
+# <type> <pkg.abbr> <name>
+# 	(<arch>)<version>
+# 	(<arch>)<version>
+# 	...
+# <type> <pkg.abbr> <name>
+# ...
+#
+
+P SUNWcar	Core Architecture, (Root)
+P SUNWcakr	Core Solaris Kernel Architecture (Root)
+P SUNWkvm	Core Architecture, (Kvm)
+P SUNWcsr	Core Solaris, (Root)
+P SUNWckr	Core Solaris Kernel (Root)
+P SUNWcnetr	Core Solaris Network Infrastructure (Root)
+P SUNWcsu	Core Solaris, (Usr)
+P SUNWcsl	Core Solaris Libraries
+P SUNWcsd	Core Solaris Devices
+R SUNWwpau      Wireless WPA Supplicant, (Usr)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWwpar/pkginfo.tmpl	Fri Apr 27 09:21:03 2007 -0700
@@ -0,0 +1,59 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+#
+# This required package information file describes characteristics of the
+# package, such as package abbreviation, full package name, package version,
+# and package architecture.
+#
+PKG="SUNWwpar"
+NAME="Wireless WPA Supplicant, (Root)"
+ARCH="ISA"
+VERSION="ONVERS,REV=0.0.0"
+SUNW_PRODNAME="SunOS"
+SUNW_PRODVERS="RELEASE/VERSION"
+SUNW_PKGTYPE="root"
+SUNW_PKGVERS="1.0"
+MAXINST="1000"
+CATEGORY="system"
+DESC="The service implements the IEEE802.11i (WPA/WPA2) specification."
+VENDOR="Sun Microsystems, Inc."
+HOTLINE="Please contact your local service provider"
+EMAIL=""
+CLASSES="none manifest"
+BASEDIR=/
+SUNW_PKG_ALLZONES="true"
+SUNW_PKG_HOLLOW="false"
+SUNW_PKG_THISZONE="false"
+#VSTOCK="<reserved by Release Engineering for package part #>"
+#ISTATES="<developer defined>"
+#RSTATES='<developer defined>'
+#ULIMIT="<developer defined>"
+#ORDER="<developer defined>"
+#PSTAMP="<developer defined>"
+#INTONLY="<developer defined>"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWwpar/prototype_com	Fri Apr 27 09:21:03 2007 -0700
@@ -0,0 +1,52 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#ident	"%Z%%M%	%I%	%E% SMI"
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>	# where to find pkg objects
+#!include <filename>			# include another 'prototype' file
+#!default <mode> <owner> <group>	# default used if not specified on entry
+#!<param>=<value>			# puts parameter in pkg environment
+
+# packaging files
+i pkginfo
+i copyright
+i depend
+i i.manifest
+i r.manifest
+#
+# source locations relative to the prototype file
+#
+# SUNWwpar
+#
+d none var 0755 root sys
+d none var/svc 0755 root sys
+d none var/svc/manifest 0755 root sys
+d none var/svc/manifest/network 0755 root sys
+f manifest var/svc/manifest/network/wpa.xml 0444 root sys
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWwpar/prototype_i386	Fri Apr 27 09:21:03 2007 -0700
@@ -0,0 +1,50 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>	# where to find pkg objects
+#!include <filename>			# include another 'prototype' file
+#!default <mode> <owner> <group>	# default used if not specified on entry
+#!<param>=<value>			# puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+#
+# List files which are I386 specific here
+#
+# source locations relative to the prototype file
+#
+#
+# SUNWwpar
+#
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWwpar/prototype_sparc	Fri Apr 27 09:21:03 2007 -0700
@@ -0,0 +1,50 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>        # where to find pkg objects
+#!include <filename>                    # include another 'prototype' file
+#!default <mode> <owner> <group>        # default used if not specified on entry
+#!<param>=<value>                       # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+#
+# List files which are SPARC specific here
+#
+# source locations relative to the prototype file
+#
+#
+# SUNWwpar
+#
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWwpau/Makefile	Fri Apr 27 09:21:03 2007 -0700
@@ -0,0 +1,35 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+.KEEP_STATE:
+
+all: $(FILES)
+install: all pkg
+
+include ../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWwpau/depend	Fri Apr 27 09:21:03 2007 -0700
@@ -0,0 +1,52 @@
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+# This package information file defines software dependencies associated
+# with the pkg.  You can define three types of pkg dependencies with this file:
+#	 P indicates a prerequisite for installation
+#	 I indicates an incompatible package
+#	 R indicates a reverse dependency
+# <pkg.abbr> see pkginfo(4), PKG parameter
+# <name> see pkginfo(4), NAME parameter
+# <version> see pkginfo(4), VERSION parameter
+# <arch> see pkginfo(4), ARCH parameter
+# <type> <pkg.abbr> <name>
+# 	(<arch>)<version>
+# 	(<arch>)<version>
+# 	...
+# <type> <pkg.abbr> <name>
+# ...
+#
+
+P SUNWcar	Core Architecture, (Root)
+P SUNWcakr	Core Solaris Kernel Architecture (Root)
+P SUNWkvm	Core Architecture, (Kvm)
+P SUNWcsr	Core Solaris, (Root)
+P SUNWckr	Core Solaris Kernel (Root)
+P SUNWcnetr	Core Solaris Network Infrastructure (Root)
+P SUNWcsu	Core Solaris, (Usr)
+P SUNWcsl	Core Solaris Libraries
+P SUNWcsd	Core Solaris Devices
+P SUNWwpar	Wireless WPA Supplicant, (Root)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWwpau/pkginfo.tmpl	Fri Apr 27 09:21:03 2007 -0700
@@ -0,0 +1,59 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+#
+# This required package information file describes characteristics of the
+# package, such as package abbreviation, full package name, package version,
+# and package architecture.
+#
+PKG="SUNWwpau"
+NAME="Wireless WPA Supplicant, (Usr)"
+ARCH="ISA"
+VERSION="ONVERS,REV=0.0.0"
+SUNW_PRODNAME="SunOS"
+SUNW_PRODVERS="RELEASE/VERSION"
+SUNW_PKGTYPE="usr"
+SUNW_PKGVERS="1.0"
+MAXINST="1000"
+CATEGORY="system"
+DESC="The service implements the IEEE802.11i (WPA/WPA2) specification."
+VENDOR="Sun Microsystems, Inc."
+HOTLINE="Please contact your local service provider"
+EMAIL=""
+CLASSES="none"
+BASEDIR=/
+SUNW_PKG_ALLZONES="true"
+SUNW_PKG_HOLLOW="false"
+SUNW_PKG_THISZONE="false"
+#VSTOCK="<reserved by Release Engineering for package part #>"
+#ISTATES="<developer defined>"
+#RSTATES='<developer defined>'
+#ULIMIT="<developer defined>"
+#ORDER="<developer defined>"
+#PSTAMP="<developer defined>"
+#INTONLY="<developer defined>"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWwpau/prototype_com	Fri Apr 27 09:21:03 2007 -0700
@@ -0,0 +1,49 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>	# where to find pkg objects
+#!include <filename>			# include another 'prototype' file
+#!default <mode> <owner> <group>	# default used if not specified on entry
+#!<param>=<value>			# puts parameter in pkg environment
+
+# packaging files
+i pkginfo
+i copyright
+i depend
+#
+# source locations relative to the prototype file
+#
+# SUNWwpau
+#
+d none usr 0755 root sys
+d none usr/lib 0755 root bin
+d none usr/lib/inet 755 root bin
+f none usr/lib/inet/wpad 555 root bin
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWwpau/prototype_i386	Fri Apr 27 09:21:03 2007 -0700
@@ -0,0 +1,50 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>	# where to find pkg objects
+#!include <filename>			# include another 'prototype' file
+#!default <mode> <owner> <group>	# default used if not specified on entry
+#!<param>=<value>			# puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+#
+# List files which are I386 specific here
+#
+# source locations relative to the prototype file
+#
+#
+# SUNWwpau
+#
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWwpau/prototype_sparc	Fri Apr 27 09:21:03 2007 -0700
@@ -0,0 +1,50 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>        # where to find pkg objects
+#!include <filename>                    # include another 'prototype' file
+#!default <mode> <owner> <group>        # default used if not specified on entry
+#!<param>=<value>                       # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+#
+# List files which are SPARC specific here
+#
+# source locations relative to the prototype file
+#
+#
+# SUNWwpau
+#
--- a/usr/src/pkgdefs/etc/exception_list_i386	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/pkgdefs/etc/exception_list_i386	Fri Apr 27 09:21:03 2007 -0700
@@ -613,6 +613,7 @@
 # PPPoE files not delivered to customers.
 usr/include/net/pppoe.h			i386
 usr/include/net/sppptun.h		i386
+usr/include/net/wpa.h			i386
 #
 # The ses driver is not currently delivered on Intel
 #
--- a/usr/src/pkgdefs/etc/exception_list_sparc	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/pkgdefs/etc/exception_list_sparc	Fri Apr 27 09:21:03 2007 -0700
@@ -614,6 +614,7 @@
 #
 usr/include/net/pppoe.h			sparc
 usr/include/net/sppptun.h		sparc
+usr/include/net/wpa.h			sparc
 #
 # User<->kernel interface used by cfgadm/USB only
 #
--- a/usr/src/uts/common/Makefile.files	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/uts/common/Makefile.files	Fri Apr 27 09:21:03 2007 -0700
@@ -562,7 +562,8 @@
 
 NET80211_OBJS += net80211.o net80211_proto.o net80211_input.o \
 		 net80211_output.o net80211_node.o net80211_crypto.o \
-		 net80211_crypto_none.o net80211_crypto_wep.o net80211_ioctl.o
+		 net80211_crypto_none.o net80211_crypto_wep.o net80211_ioctl.o \
+		 net80211_crypto_tkip.o net80211_crypto_ccmp.o
 
 IB_OBJS +=	ibnex.o ibnex_ioctl.o
 
--- a/usr/src/uts/common/inet/wifi_ioctl.h	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/uts/common/inet/wifi_ioctl.h	Fri Apr 27 09:21:03 2007 -0700
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -164,6 +163,7 @@
 
 #define	WL_NOENCRYPTION 0x0
 #define	WL_ENC_WEP 0x1
+#define	WL_ENC_WPA 0x2
 #define	WL_OPENSYSTEM 0x1
 #define	WL_SHAREDKEY 0x2
 
--- a/usr/src/uts/common/io/ath/ath_aux.c	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/uts/common/io/ath/ath_aux.c	Fri Apr 27 09:21:03 2007 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -573,6 +573,84 @@
 }
 
 /*
+ * Allocate tx/rx key slots for TKIP.  We allocate two slots for
+ * each key, one for decrypt/encrypt and the other for the MIC.
+ */
+static int
+key_alloc_2pair(ath_t *asc, ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix)
+{
+	uint16_t i, keyix;
+
+	ASSERT(asc->asc_splitmic);
+	for (i = 0; i < ATH_N(asc->asc_keymap)/4; i++) {
+		uint8_t b = asc->asc_keymap[i];
+		if (b != 0xff) {
+			/*
+			 * One or more slots in this byte are free.
+			 */
+			keyix = i*NBBY;
+			while (b & 1) {
+		again:
+				keyix++;
+				b >>= 1;
+			}
+			/* XXX IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV */
+			if (isset(asc->asc_keymap, keyix+32) ||
+			    isset(asc->asc_keymap, keyix+64) ||
+			    isset(asc->asc_keymap, keyix+32+64)) {
+				/* full pair unavailable */
+				if (keyix == (i+1)*NBBY) {
+					/* no slots were appropriate, advance */
+					continue;
+				}
+				goto again;
+			}
+			setbit(asc->asc_keymap, keyix);
+			setbit(asc->asc_keymap, keyix+64);
+			setbit(asc->asc_keymap, keyix+32);
+			setbit(asc->asc_keymap, keyix+32+64);
+			ATH_DEBUG((ATH_DBG_AUX,
+			    "key_alloc_2pair: key pair %u,%u %u,%u\n",
+			    keyix, keyix+64,
+			    keyix+32, keyix+32+64));
+			*txkeyix = *rxkeyix = keyix;
+			return (1);
+		}
+	}
+	ATH_DEBUG((ATH_DBG_AUX, "key_alloc_2pair:"
+	    " out of pair space\n"));
+	return (0);
+}
+/*
+ * Allocate a single key cache slot.
+ */
+static int
+key_alloc_single(ath_t *asc, ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix)
+{
+	uint16_t i, keyix;
+
+	/* try i,i+32,i+64,i+32+64 to minimize key pair conflicts */
+	for (i = 0; i < ATH_N(asc->asc_keymap); i++) {
+		uint8_t b = asc->asc_keymap[i];
+
+		if (b != 0xff) {
+			/*
+			 * One or more slots are free.
+			 */
+			keyix = i*NBBY;
+			while (b & 1)
+				keyix++, b >>= 1;
+			setbit(asc->asc_keymap, keyix);
+			ATH_DEBUG((ATH_DBG_AUX, "key_alloc_single:"
+			    " key %u\n", keyix));
+			*txkeyix = *rxkeyix = keyix;
+			return (1);
+		}
+	}
+	return (0);
+}
+
+/*
  * Allocate one or more key cache slots for a unicast key.  The
  * key itself is needed only to identify the cipher.  For hardware
  * TKIP with split cipher+MIC keys we allocate two key cache slot
@@ -586,17 +664,140 @@
 ath_key_alloc(ieee80211com_t *ic, const struct ieee80211_key *k,
     ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
 {
-	*keyix = *rxkeyix = 0;
+	ath_t *asc = (ath_t *)ic;
+
+	/*
+	 * We allocate two pair for TKIP when using the h/w to do
+	 * the MIC.  For everything else, including software crypto,
+	 * we allocate a single entry.  Note that s/w crypto requires
+	 * a pass-through slot on the 5211 and 5212.  The 5210 does
+	 * not support pass-through cache entries and we map all
+	 * those requests to slot 0.
+	 */
+	if (k->wk_flags & IEEE80211_KEY_SWCRYPT) {
+		return (key_alloc_single(asc, keyix, rxkeyix));
+	} else if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP &&
+	    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && asc->asc_splitmic) {
+		return (key_alloc_2pair(asc, keyix, rxkeyix));
+	} else {
+		return (key_alloc_single(asc, keyix, rxkeyix));
+	}
+}
+
+/*
+ * Delete an entry in the key cache allocated by ath_key_alloc.
+ */
+int
+ath_key_delete(ieee80211com_t *ic, const struct ieee80211_key *k)
+{
+	ath_t *asc = (ath_t *)ic;
+	struct ath_hal *ah = asc->asc_ah;
+	const struct ieee80211_cipher *cip = k->wk_cipher;
+	ieee80211_keyix keyix = k->wk_keyix;
+
+	ATH_DEBUG((ATH_DBG_AUX, "ath_key_delete:"
+	    " delete key %u ic_cipher=0x%x\n", keyix, cip->ic_cipher));
+
+	ATH_HAL_KEYRESET(ah, keyix);
+	/*
+	 * Handle split tx/rx keying required for TKIP with h/w MIC.
+	 */
+	if (cip->ic_cipher == IEEE80211_CIPHER_TKIP &&
+	    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && asc->asc_splitmic)
+		ATH_HAL_KEYRESET(ah, keyix+32);		/* RX key */
+
+	if (keyix >= IEEE80211_WEP_NKID) {
+		/*
+		 * Don't touch keymap entries for global keys so
+		 * they are never considered for dynamic allocation.
+		 */
+		clrbit(asc->asc_keymap, keyix);
+		if (cip->ic_cipher == IEEE80211_CIPHER_TKIP &&
+		    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 &&
+		    asc->asc_splitmic) {
+			clrbit(asc->asc_keymap, keyix+64);	/* TX key MIC */
+			clrbit(asc->asc_keymap, keyix+32);	/* RX key */
+			clrbit(asc->asc_keymap, keyix+32+64);	/* RX key MIC */
+		}
+	}
 	return (1);
 }
 
-int
-ath_key_delete(ieee80211com_t *ic, const struct ieee80211_key *k)
+static void
+ath_keyprint(const char *tag, uint_t ix,
+    const HAL_KEYVAL *hk, const uint8_t mac[IEEE80211_ADDR_LEN])
 {
-	struct ath_hal *ah = ((ath_t *)ic)->asc_ah;
+	static const char *ciphers[] = {
+		"WEP",
+		"AES-OCB",
+		"AES-CCM",
+		"CKIP",
+		"TKIP",
+		"CLR",
+	};
+	int i, n;
+	char buf[MAX_IEEE80211STR], buft[32];
+
+	(void) snprintf(buf, sizeof (buf), "%s: [%02u] %s ",
+	    tag, ix, ciphers[hk->kv_type]);
+	for (i = 0, n = hk->kv_len; i < n; i++) {
+		(void) snprintf(buft, sizeof (buft), "%02x", hk->kv_val[i]);
+		(void) strlcat(buf, buft, sizeof (buf));
+	}
+	(void) snprintf(buft, sizeof (buft), " mac %s",
+	    ieee80211_macaddr_sprintf(mac));
+	(void) strlcat(buf, buft, sizeof (buf));
+	if (hk->kv_type == HAL_CIPHER_TKIP) {
+		(void) snprintf(buft, sizeof (buft), " mic ");
+		(void) strlcat(buf, buft, sizeof (buf));
+		for (i = 0; i < sizeof (hk->kv_mic); i++) {
+			(void) snprintf(buft, sizeof (buft), "%02x",
+			    hk->kv_mic[i]);
+			(void) strlcat(buf, buft, sizeof (buf));
+		}
+	}
+	ATH_DEBUG((ATH_DBG_AUX, "%s", buf));
+}
 
-	ATH_HAL_KEYRESET(ah, k->wk_keyix);
-	return (1);
+/*
+ * Set a TKIP key into the hardware.  This handles the
+ * potential distribution of key state to multiple key
+ * cache slots for TKIP.
+ */
+static int
+ath_keyset_tkip(ath_t *asc, const struct ieee80211_key *k,
+	HAL_KEYVAL *hk, const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+#define	IEEE80211_KEY_XR	(IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV)
+	static const uint8_t zerobssid[IEEE80211_ADDR_LEN] = {0, 0, 0, 0, 0, 0};
+	struct ath_hal *ah = asc->asc_ah;
+
+	ASSERT(k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP);
+	if ((k->wk_flags & IEEE80211_KEY_XR) == IEEE80211_KEY_XR) {
+		/*
+		 * TX key goes at first index, RX key at +32.
+		 * The hal handles the MIC keys at index+64.
+		 */
+		(void) memcpy(hk->kv_mic, k->wk_txmic, sizeof (hk->kv_mic));
+		ath_keyprint("ath_keyset_tkip:", k->wk_keyix, hk, zerobssid);
+		if (!ATH_HAL_KEYSET(ah, k->wk_keyix, hk, zerobssid))
+			return (0);
+
+		(void) memcpy(hk->kv_mic, k->wk_rxmic, sizeof (hk->kv_mic));
+		ath_keyprint("ath_keyset_tkip:", k->wk_keyix+32, hk, mac);
+		return (ATH_HAL_KEYSET(ah, k->wk_keyix+32, hk, mac));
+	} else if (k->wk_flags & IEEE80211_KEY_XR) {
+		/*
+		 * TX/RX key goes at first index.
+		 * The hal handles the MIC keys are index+64.
+		 */
+		(void) memcpy(hk->kv_mic, k->wk_flags & IEEE80211_KEY_XMIT ?
+		    k->wk_txmic : k->wk_rxmic, sizeof (hk->kv_mic));
+		ath_keyprint("ath_keyset_tkip:", k->wk_keyix, hk, zerobssid);
+		return (ATH_HAL_KEYSET(ah, k->wk_keyix, hk, zerobssid));
+	}
+	return (0);
+#undef IEEE80211_KEY_XR
 }
 
 /*
@@ -612,7 +813,6 @@
 		HAL_CIPHER_TKIP,	/* IEEE80211_CIPHER_TKIP */
 		HAL_CIPHER_AES_OCB,	/* IEEE80211_CIPHER_AES_OCB */
 		HAL_CIPHER_AES_CCM,	/* IEEE80211_CIPHER_AES_CCM */
-		(uint8_t)-1,		/* 4 is not allocated */
 		HAL_CIPHER_CKIP,	/* IEEE80211_CIPHER_CKIP */
 		HAL_CIPHER_CLR,		/* IEEE80211_CIPHER_NONE */
 	};
@@ -636,7 +836,14 @@
 		hk.kv_type = HAL_CIPHER_CLR;
 	}
 
-	return (ATH_HAL_KEYSET(ah, k->wk_keyix, &hk, mac));
+	if (hk.kv_type == HAL_CIPHER_TKIP &&
+	    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 &&
+	    asc->asc_splitmic) {
+		return (ath_keyset_tkip(asc, k, &hk, mac));
+	} else {
+		ath_keyprint("ath_keyset:", k->wk_keyix, &hk, mac);
+		return (ATH_HAL_KEYSET(ah, k->wk_keyix, &hk, mac));
+	}
 }
 
 /*
--- a/usr/src/uts/common/io/ath/ath_impl.h	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/uts/common/io/ath/ath_impl.h	Fri Apr 27 09:21:03 2007 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -61,6 +61,12 @@
 #include <sys/net80211.h>
 #include "ath_hal.h"
 
+/* Bit map related macros. */
+#define	setbit(a, i)		((a)[(i)/NBBY] |= (1 << ((i)%NBBY)))
+#define	clrbit(a, i)		((a)[(i)/NBBY] &= ~(1 << ((i)%NBBY)))
+#define	isset(a, i)		((a)[(i)/NBBY] & (1 << ((i)%NBBY)))
+#define	isclr(a, i)		(!((a)[(i)/NBBY] & (1 << ((i)%NBBY))))
+
 /*
  * Bit flags in the ath_dbg_flags
  */
@@ -298,6 +304,9 @@
 	boolean_t		asc_resched_needed;
 	kmutex_t		asc_resched_lock;
 
+	uint32_t		asc_keymax;	/* size of key cache */
+	uint8_t			asc_keymap[16];	/* bit map of key cache use */
+
 	timeout_id_t		asc_scan_timer;
 	int			(*asc_newstate)(ieee80211com_t *,
 					enum ieee80211_state, int);
--- a/usr/src/uts/common/io/ath/ath_main.c	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/uts/common/io/ath/ath_main.c	Fri Apr 27 09:21:03 2007 -0700
@@ -668,7 +668,7 @@
 		 * packet length.
 		 */
 		hdrlen += cip->ic_header;
-		pktlen += cip->ic_header + cip->ic_trailer;
+		pktlen += cip->ic_trailer;
 		if ((k->wk_flags & IEEE80211_KEY_SWMIC) == 0)
 			pktlen += cip->ic_miclen;
 		keyix = k->wk_keyix;
@@ -992,6 +992,7 @@
 	ath_t *asc = arg;
 	ieee80211com_t *ic = (ieee80211com_t *)asc;
 	mblk_t *next;
+	int error = 0;
 
 	/*
 	 * No data frames go out unless we're associated; this
@@ -1009,10 +1010,15 @@
 	while (mp != NULL) {
 		next = mp->b_next;
 		mp->b_next = NULL;
-
-		if (ath_xmit(ic, mp, IEEE80211_FC0_TYPE_DATA) != 0) {
+		error = ath_xmit(ic, mp, IEEE80211_FC0_TYPE_DATA);
+		if (error != 0) {
 			mp->b_next = next;
-			break;
+			if (error == ENOMEM) {
+				break;
+			} else {
+				freemsgchain(mp);	/* CR6501759 issues */
+				return (NULL);
+			}
 		}
 		mp = next;
 	}
@@ -1173,6 +1179,8 @@
 		}
 	}
 	ic->ic_node_cleanup(in);
+	if (in->in_wpa_ie != NULL)
+		ieee80211_free(in->in_wpa_ie);
 	kmem_free(in, sizeof (struct ath_node));
 }
 
@@ -1898,6 +1906,30 @@
 	    "multi rate retry support=%x\n",
 	    asc->asc_mrretry));
 
+	/*
+	 * Get the hardware key cache size.
+	 */
+	asc->asc_keymax = ATH_HAL_KEYCACHESIZE(ah);
+	if (asc->asc_keymax > sizeof (asc->asc_keymap) * NBBY) {
+		ATH_DEBUG((ATH_DBG_ATTACH, "ath_attach:"
+		    " Warning, using only %u entries in %u key cache\n",
+		    sizeof (asc->asc_keymap) * NBBY, asc->asc_keymax));
+		asc->asc_keymax = sizeof (asc->asc_keymap) * NBBY;
+	}
+	/*
+	 * Reset the key cache since some parts do not
+	 * reset the contents on initial power up.
+	 */
+	for (i = 0; i < asc->asc_keymax; i++)
+		ATH_HAL_KEYRESET(ah, i);
+
+	for (i = 0; i < IEEE80211_WEP_NKID; i++) {
+		setbit(asc->asc_keymap, i);
+		setbit(asc->asc_keymap, i+32);
+		setbit(asc->asc_keymap, i+64);
+		setbit(asc->asc_keymap, i+32+64);
+	}
+
 	ATH_HAL_GETREGDOMAIN(ah, (uint32_t *)&ath_regdomain);
 	ATH_HAL_GETCOUNTRYCODE(ah, &ath_countrycode);
 	/*
@@ -1951,20 +1983,29 @@
 		ic->ic_caps |= IEEE80211_C_WEP;
 	if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_AES_OCB))
 		ic->ic_caps |= IEEE80211_C_AES;
-	if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_AES_CCM))
+	if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_AES_CCM)) {
+		ATH_DEBUG((ATH_DBG_ATTACH, "Atheros support H/W CCMP\n"));
 		ic->ic_caps |= IEEE80211_C_AES_CCM;
-	if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_CKIP)) {
+	}
+	if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_CKIP))
 		ic->ic_caps |= IEEE80211_C_CKIP;
+	if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_TKIP)) {
+		ATH_DEBUG((ATH_DBG_ATTACH, "Atheros support H/W TKIP\n"));
+		ic->ic_caps |= IEEE80211_C_TKIP;
 		/*
 		 * Check if h/w does the MIC and/or whether the
 		 * separate key cache entries are required to
 		 * handle both tx+rx MIC keys.
 		 */
-		if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_MIC))
+		if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_MIC)) {
+			ATH_DEBUG((ATH_DBG_ATTACH, "Support H/W TKIP MIC\n"));
 			ic->ic_caps |= IEEE80211_C_TKIPMIC;
+		}
 		if (ATH_HAL_TKIPSPLIT(ah))
 			asc->asc_splitmic = 1;
 	}
+	ic->ic_caps |= IEEE80211_C_WPA;	/* Support WPA/WPA2 */
+
 	asc->asc_hasclrkey = ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_CLR);
 	ic->ic_phytype = IEEE80211_T_OFDM;
 	ic->ic_opmode = IEEE80211_M_STA;
@@ -1974,6 +2015,11 @@
 	ic->ic_xmit = ath_xmit;
 	ieee80211_attach(ic);
 
+	/* different instance has different WPA door */
+	(void) snprintf(ic->ic_wpadoor, MAX_IEEE80211STR, "%s_%s%d", WPA_DOOR,
+		ddi_driver_name(devinfo),
+		ddi_get_instance(devinfo));
+
 	/* Override 80211 default routines */
 	ic->ic_reset = ath_reset;
 	asc->asc_newstate = ic->ic_newstate;
@@ -2147,7 +2193,7 @@
 
 static struct modldrv ath_modldrv = {
 	&mod_driverops,		/* Type of module.  This one is a driver */
-	"ath driver 1.2/HAL 0.9.17.2",	/* short description */
+	"ath driver 1.3/HAL 0.9.17.2",	/* short description */
 	&ath_dev_ops		/* driver specific ops */
 };
 
--- a/usr/src/uts/common/io/dld/dld_drv.c	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/uts/common/io/dld/dld_drv.c	Fri Apr 27 09:21:03 2007 -0700
@@ -726,7 +726,8 @@
 	ssp = (dld_ioc_secobj_set_t *)mp->b_cont->b_rptr;
 	sobjp = &ssp->ss_obj;
 
-	if (sobjp->so_class != DLD_SECOBJ_CLASS_WEP)
+	if (sobjp->so_class != DLD_SECOBJ_CLASS_WEP &&
+	    sobjp->so_class != DLD_SECOBJ_CLASS_WPA)
 		goto failed;
 
 	if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0' ||
@@ -767,7 +768,6 @@
 failed:
 	ASSERT(err != 0);
 	miocnak(q, mp, 0, err);
-
 }
 
 typedef struct dld_secobj_state {
--- a/usr/src/uts/common/io/ipw/ipw2100.c	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/uts/common/io/ipw/ipw2100.c	Fri Apr 27 09:21:03 2007 -0700
@@ -1530,7 +1530,6 @@
 			wd.wd_secalloc = WIFI_SEC_NONE;
 		wd.wd_opmode = ic->ic_opmode;
 		IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid);
-		ieee80211_free_node(in);
 		(void) mac_pdata_update(ic->ic_mach, &wd, sizeof (wd));
 
 		break;
--- a/usr/src/uts/common/io/mac/plugins/mac_wifi.c	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/uts/common/io/mac/plugins/mac_wifi.c	Fri Apr 27 09:21:03 2007 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -230,10 +230,11 @@
 		break;
 	}
 
-	/*
-	 * Fill in the fixed parts of the WEP-portion of the frame.
-	 */
-	if (wdp->wd_secalloc == WIFI_SEC_WEP) {
+	switch (wdp->wd_secalloc) {
+	case WIFI_SEC_WEP:
+		/*
+		 * Fill in the fixed parts of the WEP-portion of the frame.
+		 */
 		wh->i_fc[1] |= IEEE80211_FC1_WEP;
 		/*
 		 * The actual contents of the WEP-portion of the packet
@@ -241,6 +242,16 @@
 		 * just need to account for the size.
 		 */
 		mp->b_wptr += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
+		break;
+
+	case WIFI_SEC_WPA:
+		wh->i_fc[1] |= IEEE80211_FC1_WEP;
+		mp->b_wptr += IEEE80211_WEP_IVLEN +
+		    IEEE80211_WEP_KIDLEN + IEEE80211_WEP_EXTIVLEN;
+		break;
+
+	default:
+		break;
 	}
 
 	/*
@@ -265,6 +276,7 @@
 	struct ieee80211_frame	*wh;
 	struct ieee80211_llc	*llc;
 	uchar_t			*llcp;
+	wifi_data_t		*wdp = pdata;
 
 	if (MBLKL(mp) < sizeof (struct ieee80211_frame))
 		return (EINVAL);
@@ -279,8 +291,11 @@
 	 * packets, it will still have a WEP portion.  Skip past it to get to
 	 * the LLC header.
 	 */
-	if (wh->i_fc[1] & IEEE80211_FC1_WEP)
+	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
 		llcp += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
+		if (wdp->wd_secalloc == WIFI_SEC_WPA)
+			llcp += IEEE80211_WEP_EXTIVLEN;
+	}
 
 	if (mp->b_wptr - llcp < sizeof (struct ieee80211_llc))
 		return (EINVAL);
--- a/usr/src/uts/common/io/net80211/net80211.c	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/uts/common/io/net80211/net80211.c	Fri Apr 27 09:21:03 2007 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -45,6 +45,8 @@
 #include <sys/types.h>
 #include <sys/cmn_err.h>
 #include <sys/modctl.h>
+#include <sys/stropts.h>
+#include <sys/door.h>
 #include "net80211_impl.h"
 
 uint32_t ieee80211_debug = 0x0;	/* debug msg flags */
@@ -88,6 +90,111 @@
 }
 
 /*
+ * Alloc memory, and save the size
+ */
+void *
+ieee80211_malloc(size_t size)
+{
+	void *p = kmem_zalloc((size + 4), KM_SLEEP);
+	*(int *)p = size;
+	p = (char *)p + 4;
+
+	return (p);
+}
+
+void
+ieee80211_free(void *p)
+{
+	void *tp = (char *)p - 4;
+	kmem_free((char *)p - 4, *(int *)tp + 4);
+}
+
+void
+ieee80211_mac_update(ieee80211com_t *ic)
+{
+	wifi_data_t wd = { 0 };
+	ieee80211_node_t *in;
+
+	/*
+	 * We can send data now; update the fastpath with our
+	 * current associated BSSID and other relevant settings.
+	 */
+	in = ic->ic_bss;
+	wd.wd_secalloc = ieee80211_crypto_getciphertype(ic);
+	wd.wd_opmode = ic->ic_opmode;
+	IEEE80211_ADDR_COPY(wd.wd_bssid, in->in_bssid);
+	(void) mac_pdata_update(ic->ic_mach, &wd, sizeof (wd));
+	mac_tx_update(ic->ic_mach);
+	ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_mac_update"
+	    "(cipher = %d)\n", wd.wd_secalloc);
+}
+
+/*
+ * ieee80211_event_thread
+ * open door of wpa, send event to wpad service
+ */
+static void
+ieee80211_event_thread(void *arg)
+{
+	ieee80211com_t *ic = arg;
+	door_handle_t event_door = NULL;	/* Door for upcalls */
+	wl_events_t ev;
+	door_arg_t darg;
+
+	mutex_enter(&ic->ic_doorlock);
+
+	ev.event = ic->ic_eventq[ic->ic_evq_head];
+	ic->ic_evq_head ++;
+	if (ic->ic_evq_head >= MAX_EVENT)
+		ic->ic_evq_head = 0;
+
+	ieee80211_dbg(IEEE80211_MSG_DEBUG, "ieee80211_event(%d)\n", ev.event);
+	/*
+	 * Locate the door used for upcalls
+	 */
+	if (door_ki_open(ic->ic_wpadoor, &event_door) != 0) {
+		ieee80211_err("ieee80211_event: door_ki_open(%s) failed\n",
+		    ic->ic_wpadoor);
+		goto out;
+	}
+
+	darg.data_ptr = (char *)&ev;
+	darg.data_size = sizeof (wl_events_t);
+	darg.desc_ptr = NULL;
+	darg.desc_num = 0;
+	darg.rbuf = NULL;
+	darg.rsize = 0;
+
+	if (door_ki_upcall(event_door, &darg) != 0) {
+		ieee80211_err("ieee80211_event: door_ki_upcall() failed\n");
+	}
+
+	if (event_door) {	/* release our hold (if any) */
+		door_ki_rele(event_door);
+	}
+
+out:
+	mutex_exit(&ic->ic_doorlock);
+}
+
+/*
+ * Notify state transition event message to WPA daemon
+ */
+void
+ieee80211_notify(ieee80211com_t *ic, wpa_event_type event)
+{
+	if ((ic->ic_flags & IEEE80211_F_WPA) == 0)
+		return;		/* Not running on WPA mode */
+
+	ic->ic_eventq[ic->ic_evq_tail] = event;
+	ic->ic_evq_tail ++;
+	if (ic->ic_evq_tail >= MAX_EVENT) ic->ic_evq_tail = 0;
+
+	/* async */
+	(void) timeout(ieee80211_event_thread, (void *)ic, 0);
+}
+
+/*
  * Default reset method for use with the ioctl support.  This
  * method is invoked after any state change in the 802.11
  * layer that should be propagated to the hardware but not
@@ -435,6 +542,7 @@
 {
 	if (in == ic->ic_bss)
 		mac_link_update(ic->ic_mach, LINK_STATE_UP);
+	ieee80211_notify(ic, EVENT_ASSOC);	/* notify WPA service */
 }
 
 /*
@@ -447,6 +555,7 @@
 {
 	if (in == ic->ic_bss)
 		mac_link_update(ic->ic_mach, LINK_STATE_DOWN);
+	ieee80211_notify(ic, EVENT_DISASSOC);	/* notify WPA service */
 }
 
 /*
@@ -518,6 +627,7 @@
 	ASSERT(ic->ic_xmit != NULL);
 
 	mutex_init(&ic->ic_genlock, NULL, MUTEX_DRIVER, NULL);
+	mutex_init(&ic->ic_doorlock, NULL, MUTEX_DRIVER, NULL);
 
 	im = kmem_alloc(sizeof (ieee80211_impl_t), KM_SLEEP);
 	ic->ic_private = im;
@@ -599,11 +709,12 @@
 	ieee80211_crypto_detach(ic);
 
 	mutex_destroy(&ic->ic_genlock);
+	mutex_destroy(&ic->ic_doorlock);
 }
 
 static struct modlmisc	i_wifi_modlmisc = {
 	&mod_miscops,
-	"IEEE80211 Kernel Misc Module"
+	"IEEE80211 Kernel Module v1.2"
 };
 
 static struct modlinkage	i_wifi_modlinkage = {
--- a/usr/src/uts/common/io/net80211/net80211_crypto.c	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/uts/common/io/net80211/net80211_crypto.c	Fri Apr 27 09:21:03 2007 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -45,6 +45,8 @@
 #include "net80211_impl.h"
 
 extern const struct ieee80211_cipher wep;
+extern const struct ieee80211_cipher tkip;
+extern const struct ieee80211_cipher ccmp;
 
 /*
  * Table of registered cipher modules.
@@ -160,6 +162,17 @@
 			cip->ic_name);
 		flags |= IEEE80211_KEY_SWCRYPT;
 	}
+	/*
+	 * Hardware TKIP with software MIC is an important
+	 * combination; we handle it by flagging each key,
+	 * the cipher modules honor it.
+	 */
+	if (cipher == IEEE80211_CIPHER_TKIP &&
+	    (ic->ic_caps & IEEE80211_C_TKIPMIC) == 0) {
+		ieee80211_dbg(IEEE80211_MSG_CRYPTO,
+		    "no h/w support for TKIP MIC, falling back to s/w\n");
+		flags |= IEEE80211_KEY_SWMIC;
+	}
 
 	/*
 	 * Bind cipher to key instance.  Note we do this
@@ -345,10 +358,9 @@
 	uint32_t cipher;
 	static const uint8_t ciphermap[] = {
 		WIFI_SEC_WEP,	/* IEEE80211_CIPHER_WEP */
-		(uint8_t)-1,	/* IEEE80211_CIPHER_TKIP */
+		WIFI_SEC_WPA,	/* IEEE80211_CIPHER_TKIP */
 		(uint8_t)-1,	/* IEEE80211_CIPHER_AES_OCB */
-		(uint8_t)-1,	/* IEEE80211_CIPHER_AES_CCM */
-		(uint8_t)-1,	/* 4 is not allocated */
+		WIFI_SEC_WPA,	/* IEEE80211_CIPHER_AES_CCM */
 		(uint8_t)-1,	/* IEEE80211_CIPHER_CKIP */
 		WIFI_SEC_NONE,	/* IEEE80211_CIPHER_NONE */
 	};
@@ -453,6 +465,8 @@
 	cs->cs_key_update_end = nulldev_key_update;
 
 	ieee80211_crypto_register(&wep);
+	ieee80211_crypto_register(&tkip);
+	ieee80211_crypto_register(&ccmp);
 }
 
 /*
@@ -464,6 +478,8 @@
 	ieee80211_crypto_delglobalkeys(ic);
 
 	ieee80211_crypto_unregister(&wep);
+	ieee80211_crypto_unregister(&tkip);
+	ieee80211_crypto_unregister(&ccmp);
 }
 
 /*
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/net80211/net80211_crypto_ccmp.c	Fri Apr 27 09:21:03 2007 -0700
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * IEEE 802.11i CCMP crypto support.
+ */
+#include <sys/byteorder.h>
+#include <sys/crypto/common.h>
+#include <sys/crypto/api.h>
+#include <sys/crc32.h>
+#include <sys/random.h>
+#include "net80211_impl.h"
+
+struct ccmp_ctx {
+	struct ieee80211com *cc_ic;	/* for diagnostics */
+};
+
+static void *ccmp_attach(struct ieee80211com *, struct ieee80211_key *);
+static void ccmp_detach(struct ieee80211_key *);
+static int ccmp_setkey(struct ieee80211_key *);
+static int ccmp_encap(struct ieee80211_key *k, mblk_t *, uint8_t);
+static int ccmp_decap(struct ieee80211_key *, mblk_t *, int);
+static int ccmp_enmic(struct ieee80211_key *, mblk_t *, int);
+static int ccmp_demic(struct ieee80211_key *, mblk_t *, int);
+
+const struct ieee80211_cipher ccmp = {
+	"AES-CCM",
+	IEEE80211_CIPHER_AES_CCM,
+	IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN +
+	    IEEE80211_WEP_EXTIVLEN,
+	IEEE80211_WEP_MICLEN,
+	0,
+	ccmp_attach,
+	ccmp_detach,
+	ccmp_setkey,
+	ccmp_encap,
+	ccmp_decap,
+	ccmp_enmic,
+	ccmp_demic,
+};
+
+/* ARGSUSED */
+static void *
+ccmp_attach(struct ieee80211com *ic, struct ieee80211_key *k)
+{
+	struct ccmp_ctx *ctx;
+
+	ctx = kmem_zalloc(sizeof (struct ccmp_ctx), KM_SLEEP);
+	if (ctx == NULL)
+		return (NULL);
+
+	ctx->cc_ic = ic;
+	return (ctx);
+}
+
+static void
+ccmp_detach(struct ieee80211_key *k)
+{
+	struct ccmp_ctx *ctx = k->wk_private;
+
+	if (ctx != NULL)
+		kmem_free(ctx, sizeof (struct ccmp_ctx));
+}
+
+static int
+ccmp_setkey(struct ieee80211_key *k)
+{
+	if (k->wk_keylen != (128/NBBY))
+		return (0);
+
+	return (1);
+}
+
+/*
+ * Add privacy headers appropriate for the specified key.
+ */
+static int
+ccmp_encap(struct ieee80211_key *k, mblk_t *mp, uint8_t keyid)
+{
+	uint8_t *ivp;
+	int hdrlen;
+
+	hdrlen = ieee80211_hdrspace(mp->b_rptr);
+	/*
+	 * Copy down 802.11 header and add the IV, KeyID, and ExtIV.
+	 */
+	ivp = mp->b_rptr;
+	ivp += hdrlen;
+
+	k->wk_keytsc++;				/* wrap at 48 bits */
+	ivp[0] = k->wk_keytsc >> 0;		/* PN0 */
+	ivp[1] = k->wk_keytsc >> 8;		/* PN1 */
+	ivp[2] = 0;				/* Reserved */
+	ivp[3] = keyid | IEEE80211_WEP_EXTIV;	/* KeyID | ExtID */
+	ivp[4] = k->wk_keytsc >> 16;		/* PN2 */
+	ivp[5] = k->wk_keytsc >> 24;		/* PN3 */
+	ivp[6] = k->wk_keytsc >> 32;		/* PN4 */
+	ivp[7] = k->wk_keytsc >> 40;		/* PN5 */
+
+	/*
+	 * NB: software CCMP is not supported.
+	 */
+	if (k->wk_flags & IEEE80211_KEY_SWCRYPT)
+		return (0);
+
+	return (1);
+}
+
+/*
+ * Validate and strip privacy headers (and trailer) for a
+ * received frame. The specified key should be correct but
+ * is also verified.
+ */
+static int
+ccmp_decap(struct ieee80211_key *k, mblk_t *mp, int hdrlen)
+{
+	struct ieee80211_frame tmp;
+	uint8_t *ivp;
+	uint64_t pn;
+
+	/*
+	 * Header should have extended IV and sequence number;
+	 * verify the former and validate the latter.
+	 */
+	ivp = mp->b_rptr + hdrlen;
+	if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) {
+		/*
+		 * No extended IV; discard frame.
+		 */
+		return (0);
+	}
+
+	pn = ieee80211_read_6(ivp[0], ivp[1], ivp[4], ivp[5], ivp[6], ivp[7]);
+	if (pn <= k->wk_keyrsc) {
+		/*
+		 * Replay violation.
+		 */
+		return (0);
+	}
+
+	/*
+	 * NB: software CCMP is not supported.
+	 */
+	if (k->wk_flags & IEEE80211_KEY_SWCRYPT)
+		return (0);
+
+	/*
+	 * Copy up 802.11 header and strip crypto bits.
+	 */
+	bcopy(mp->b_rptr, &tmp, hdrlen);
+	bcopy(&tmp, mp->b_rptr + ccmp.ic_header, hdrlen);
+	mp->b_rptr += ccmp.ic_header;
+	mp->b_wptr -= ccmp.ic_trailer;
+
+	/*
+	 * Ok to update rsc now.
+	 */
+	k->wk_keyrsc = pn;
+
+	return (1);
+}
+
+/*
+ * Add MIC to the frame as needed.
+ */
+/* ARGSUSED */
+static int
+ccmp_enmic(struct ieee80211_key *k, mblk_t *mp, int force)
+{
+	return (1);
+}
+
+/*
+ * Verify and strip MIC from the frame.
+ */
+/* ARGSUSED */
+static int
+ccmp_demic(struct ieee80211_key *k, mblk_t *mp, int force)
+{
+	return (1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/net80211/net80211_crypto_tkip.c	Fri Apr 27 09:21:03 2007 -0700
@@ -0,0 +1,267 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * IEEE 802.11i TKIP crypto support.
+ */
+#include <sys/byteorder.h>
+#include <sys/crypto/common.h>
+#include <sys/crypto/api.h>
+#include <sys/crc32.h>
+#include <sys/random.h>
+#include "net80211_impl.h"
+
+static void *tkip_attach(struct ieee80211com *, struct ieee80211_key *);
+static void tkip_detach(struct ieee80211_key *);
+static int tkip_setkey(struct ieee80211_key *);
+static int tkip_encap(struct ieee80211_key *, mblk_t *, uint8_t);
+static int tkip_decap(struct ieee80211_key *, mblk_t *, int);
+static int tkip_enmic(struct ieee80211_key *, mblk_t *, int);
+static int tkip_demic(struct ieee80211_key *, mblk_t *, int);
+
+const struct ieee80211_cipher tkip  = {
+	"TKIP",
+	IEEE80211_CIPHER_TKIP,
+	IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN +
+	    IEEE80211_WEP_EXTIVLEN,
+	IEEE80211_WEP_CRCLEN,
+	IEEE80211_WEP_MICLEN,
+	tkip_attach,
+	tkip_detach,
+	tkip_setkey,
+	tkip_encap,
+	tkip_decap,
+	tkip_enmic,
+	tkip_demic,
+};
+
+struct tkip_ctx {
+	struct ieee80211com	*tc_ic;		/* for diagnostics */
+	uint16_t		tx_ttak[5];
+	int			tx_phase1_done;
+	uint8_t			tx_rc4key[16];
+	uint16_t		rx_ttak[5];
+	int			rx_phase1_done;
+	uint8_t			rx_rc4key[16];
+	uint64_t		rx_rsc;		/* held until MIC verified */
+};
+
+/* ARGSUSED */
+static void *
+tkip_attach(struct ieee80211com *ic, struct ieee80211_key *k)
+{
+	struct tkip_ctx *ctx;
+
+	ctx = kmem_zalloc(sizeof (struct tkip_ctx), KM_SLEEP);
+	if (ctx == NULL)
+		return (NULL);
+
+	ctx->tc_ic = ic;
+	return (ctx);
+}
+
+static void
+tkip_detach(struct ieee80211_key *k)
+{
+	struct tkip_ctx *ctx = k->wk_private;
+
+	if (ctx != NULL)
+		kmem_free(ctx, sizeof (struct tkip_ctx));
+}
+
+static int
+tkip_setkey(struct ieee80211_key *k)
+{
+	if (k->wk_keylen != (128/NBBY))
+		return (0);
+
+	k->wk_keytsc = 1;		/* TSC starts at 1 */
+	return (1);
+}
+
+/*
+ * Add privacy headers appropriate for the specified key.
+ */
+static int
+tkip_encap(struct ieee80211_key *k, mblk_t *mp, uint8_t keyid)
+{
+	struct tkip_ctx *ctx = k->wk_private;
+	struct ieee80211com *ic = ctx->tc_ic;
+	uint8_t *ivp;
+	int hdrlen;
+
+	/*
+	 * Handle TKIP counter measures requirement.
+	 */
+	if (ic->ic_flags & IEEE80211_F_COUNTERM)
+		return (0);
+
+	hdrlen = ieee80211_hdrspace(mp->b_rptr);
+	/*
+	 * Copy down 802.11 header and add the IV, KeyID, and ExtIV.
+	 */
+	ivp = mp->b_rptr;
+	ivp += hdrlen;
+
+	ivp[0] = k->wk_keytsc >> 8;		/* TSC1 */
+	ivp[1] = (ivp[0] | 0x20) & 0x7f;	/* WEP seed */
+	ivp[2] = k->wk_keytsc >> 0;		/* TSC0 */
+	ivp[3] = keyid | IEEE80211_WEP_EXTIV;	/* KeyID | ExtID */
+	ivp[4] = k->wk_keytsc >> 16;		/* TSC2 */
+	ivp[5] = k->wk_keytsc >> 24;		/* TSC3 */
+	ivp[6] = k->wk_keytsc >> 32;		/* TSC4 */
+	ivp[7] = k->wk_keytsc >> 40;		/* TSC5 */
+
+	/*
+	 * NB: software TKIP is not supported.
+	 */
+	if (k->wk_flags & IEEE80211_KEY_SWCRYPT)
+		return (0);
+	else
+		k->wk_keytsc++;		/* wrap at 48 bits */
+
+	return (1);
+}
+
+uint64_t
+ieee80211_read_6(uint8_t b0, uint8_t b1, uint8_t b2,
+    uint8_t b3, uint8_t b4, uint8_t b5)
+{
+	uint32_t iv32 = (b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24);
+	uint16_t iv16 = (b4 << 0) | (b5 << 8);
+	return ((((uint64_t)iv16) << 32) | iv32);
+}
+
+/*
+ * Validate and strip privacy headers (and trailer) for a
+ * received frame.  If necessary, decrypt the frame using
+ * the specified key.
+ */
+static int
+tkip_decap(struct ieee80211_key *k, mblk_t *mp, int hdrlen)
+{
+	struct tkip_ctx *ctx = k->wk_private;
+	struct ieee80211com *ic = ctx->tc_ic;
+	struct ieee80211_frame tmp;
+	uint8_t *ivp;
+	uint64_t pn;
+
+	/*
+	 * Header should have extended IV and sequence number;
+	 * verify the former and validate the latter.
+	 */
+	ivp = mp->b_rptr + hdrlen;
+	if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) {
+		/*
+		 * No extended IV; discard frame.
+		 */
+		return (0);
+	}
+	/*
+	 * Handle TKIP counter measures requirement.
+	 */
+	if (ic->ic_flags & IEEE80211_F_COUNTERM)
+		return (0);
+
+	/* NB: assume IEEEE80211_WEP_MINLEN covers the extended IV */
+	pn = ieee80211_read_6(ivp[2], ivp[0], ivp[4], ivp[5], ivp[6], ivp[7]);
+	ctx->rx_rsc = pn;
+	if (ctx->rx_rsc <= k->wk_keyrsc)
+		return (0);
+	/*
+	 * NB: We can't update the rsc in the key until MIC is verified.
+	 *
+	 * We assume we are not preempted between doing the check above
+	 * and updating wk_keyrsc when stripping the MIC in tkip_demic.
+	 * Otherwise we might process another packet and discard it as
+	 * a replay.
+	 */
+
+	/*
+	 * NB: software TKIP is not supported.
+	 */
+	if (k->wk_flags & IEEE80211_KEY_SWCRYPT)
+		return (0);
+
+	/*
+	 * Copy up 802.11 header and strip crypto bits.
+	 */
+	bcopy(mp->b_rptr, &tmp, hdrlen);
+	bcopy(&tmp, mp->b_rptr + tkip.ic_header, hdrlen);
+	mp->b_rptr += tkip.ic_header;
+	mp->b_wptr -= tkip.ic_trailer;
+
+	return (1);
+}
+
+/*
+ * Add MIC to the frame as needed.
+ */
+/* ARGSUSED */
+static int
+tkip_enmic(struct ieee80211_key *k, mblk_t *mp, int force)
+{
+	return (1);
+}
+
+/*
+ * Verify and strip MIC from the frame.
+ */
+/* ARGSUSED */
+static int
+tkip_demic(struct ieee80211_key *k, mblk_t *mp, int force)
+{
+	struct tkip_ctx *ctx = k->wk_private;
+
+	/*
+	 * NB: software TKIP is not supported.
+	 */
+	if (k->wk_flags & IEEE80211_KEY_SWMIC)
+		return (0);
+	/*
+	 * Strip MIC from the tail.
+	 */
+	mp->b_wptr -= tkip.ic_miclen;
+	/*
+	 * Ok to update rsc now that MIC has been verified.
+	 */
+	k->wk_keyrsc = ctx->rx_rsc;
+
+	return (1);
+}
--- a/usr/src/uts/common/io/net80211/net80211_impl.h	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/uts/common/io/net80211/net80211_impl.h	Fri Apr 27 09:21:03 2007 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -354,6 +354,11 @@
 void ieee80211_dbg(uint32_t, const int8_t *, ...);
 int ieee80211_hdrspace(const void *);
 
+void ieee80211_notify(ieee80211com_t *, wpa_event_type);
+void ieee80211_mac_update(ieee80211com_t *);
+
+uint64_t ieee80211_read_6(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t);
+
 /* node */
 void ieee80211_node_attach(ieee80211com_t *);
 void ieee80211_node_lateattach(ieee80211com_t *);
--- a/usr/src/uts/common/io/net80211/net80211_input.c	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/uts/common/io/net80211/net80211_input.c	Fri Apr 27 09:21:03 2007 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -645,6 +645,13 @@
 	}
 }
 
+static int
+iswpaoui(const uint8_t *frm)
+{
+	uint32_t c = *(uint32_t *)(frm + 2);
+	return (frm[1] > 3 && c == ((WPA_OUI_TYPE << 24) | WPA_OUI));
+}
+
 /*
  * Process a beacon/probe response frame.
  * When the device is in station mode, create a node and add it
@@ -762,6 +769,10 @@
 		case IEEE80211_ELEMID_RSN:
 			scan.wpa = frm;
 			break;
+		case IEEE80211_ELEMID_VENDOR:
+			if (iswpaoui(frm))
+				scan.wpa = frm;		/* IEEE802.11i D3.0 */
+			break;
 		default:
 			ieee80211_dbg(IEEE80211_MSG_ELEMID,
 				"ieee80211_recv_mgmt: ignore %s,"
--- a/usr/src/uts/common/io/net80211/net80211_ioctl.c	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/uts/common/io/net80211/net80211_ioctl.c	Fri Apr 27 09:21:03 2007 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -570,6 +570,8 @@
 	switch (cmd) {
 	case WLAN_GET_PARAM:
 		*ow_encryp = (ic->ic_flags & IEEE80211_F_PRIVACY) ? 1 : 0;
+		if (ic->ic_flags & IEEE80211_F_WPA)
+			*ow_encryp = WL_ENC_WPA;
 		break;
 	case WLAN_SET_PARAM:
 		ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_encrypt: "
@@ -993,6 +995,7 @@
 	    (in->in_capinfo & IEEE80211_CAPINFO_ESS ?
 	    WL_BSS_BSS : WL_BSS_IBSS);
 	conf->wl_ess_conf_sl = wifi_getrssi(in);
+	conf->wl_ess_conf_reserved[0] = (in->in_wpa_ie == NULL? 0 : 1);
 
 	/* physical (FH, DS, ERP) parameters */
 	if (IEEE80211_IS_CHAN_A(chan) || IEEE80211_IS_CHAN_T(chan)) {
@@ -1130,13 +1133,17 @@
 		return (0);
 
 	IEEE80211_UNLOCK(ic);
+
 	ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
 	IEEE80211_LOCK(ic);
 	if (ostate == IEEE80211_S_INIT)
 		ic->ic_flags |= IEEE80211_F_SCANONLY;
 
-	/* wait scan complete */
-	wifi_wait_scan(ic);
+	/* Don't wait on WPA mode */
+	if ((ic->ic_flags & IEEE80211_F_WPA) == 0) {
+		/* wait scan complete */
+		wifi_wait_scan(ic);
+	}
 
 	wifi_setupoutmsg(mp, 0);
 	return (0);
@@ -1158,6 +1165,8 @@
 	bzero(ic->ic_nickname, IEEE80211_NWID_LEN);
 	in->in_authmode = IEEE80211_AUTH_OPEN;
 	ic->ic_flags &= ~IEEE80211_F_PRIVACY;
+	ic->ic_flags &= ~IEEE80211_F_WPA;	/* mask WPA mode */
+	ic->ic_evq_head = ic->ic_evq_tail = 0;	/* reset Queue */
 	ic->ic_def_txkey = 0;
 	for (i = 0; i < MAX_NWEPKEYS; i++) {
 		ic->ic_nw_keys[i].wk_keylen = 0;
@@ -1187,6 +1196,462 @@
 	return (0);
 }
 
+/*
+ * Get the capabilities of drivers.
+ */
+static int
+wifi_cfg_caps(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
+{
+	mblk_t *omp;
+	wldp_t *outp;
+	wl_capability_t *o_caps;
+	int err = 0;
+
+	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_capability_t))) == NULL)
+		return (ENOMEM);
+	outp = (wldp_t *)omp->b_rptr;
+	o_caps = (wl_capability_t *)outp->wldp_buf;
+
+	switch (cmd) {
+	case WLAN_GET_PARAM:
+		ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_caps: "
+			"ic_caps = %u\n", ic->ic_caps);
+		o_caps->caps = ic->ic_caps;
+		break;
+	case WLAN_SET_PARAM:
+		outp->wldp_result = WL_READONLY;
+		err = EINVAL;
+		break;
+	default:
+		ieee80211_err("wifi_cfg_caps: unknown command %x\n", cmd);
+		outp->wldp_result = WL_NOTSUPPORTED;
+		err = EINVAL;
+		break;
+	}
+
+	freemsg(*mp);
+	*mp = omp;
+	return (err);
+}
+
+/*
+ * Operating on WPA mode.
+ */
+static int
+wifi_cfg_wpa(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
+{
+	mblk_t *omp;
+	wldp_t *outp;
+	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
+	wl_wpa_t *wpa = (wl_wpa_t *)inp->wldp_buf;
+	wl_wpa_t *o_wpa;
+	int err = 0;
+
+	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_wpa_t))) == NULL)
+		return (ENOMEM);
+	outp = (wldp_t *)omp->b_rptr;
+	o_wpa = (wl_wpa_t *)outp->wldp_buf;
+
+	switch (cmd) {
+	case WLAN_GET_PARAM:
+		ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_wpa: "
+			"get wpa=%u\n", wpa->wpa_flag);
+		o_wpa->wpa_flag = ((ic->ic_flags & IEEE80211_F_WPA)? 1 : 0);
+		break;
+	case WLAN_SET_PARAM:
+		ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_wpa: "
+			"set wpa=%u\n", wpa->wpa_flag);
+		if (wpa->wpa_flag > 0) {	/* enable WPA mode */
+			ic->ic_flags |= IEEE80211_F_PRIVACY;
+			ic->ic_flags |= IEEE80211_F_WPA;
+		} else {
+			ic->ic_flags &= ~IEEE80211_F_PRIVACY;
+			ic->ic_flags &= ~IEEE80211_F_WPA;
+		}
+		break;
+	default:
+		ieee80211_err("wifi_cfg_wpa: unknown command %x\n", cmd);
+		outp->wldp_result = WL_NOTSUPPORTED;
+		err = EINVAL;
+		break;
+	}
+
+	freemsg(*mp);
+	*mp = omp;
+	return (err);
+}
+
+/*
+ * WPA daemon set the WPA keys.
+ * The WPA keys are negotiated with APs through wpa service.
+ */
+static int
+wifi_cfg_wpakey(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
+{
+	mblk_t *omp;
+	wldp_t *outp;
+	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
+	wl_key_t *ik = (wl_key_t *)(inp->wldp_buf);
+	struct ieee80211_node *in;
+	struct ieee80211_key *wk;
+	uint16_t kid;
+	int err = 0;
+
+	if ((omp = wifi_getoutmsg(*mp, cmd, 0)) == NULL)
+		return (ENOMEM);
+	outp = (wldp_t *)omp->b_rptr;
+
+	switch (cmd) {
+	case WLAN_GET_PARAM:
+		outp->wldp_result = WL_WRITEONLY;
+		err = EINVAL;
+		break;
+	case WLAN_SET_PARAM:
+		ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_wpakey: "
+			"idx=%d\n", ik->ik_keyix);
+		/* NB: cipher support is verified by ieee80211_crypt_newkey */
+		/* NB: this also checks ik->ik_keylen > sizeof(wk->wk_key) */
+		if (ik->ik_keylen > sizeof (ik->ik_keydata)) {
+			ieee80211_err("wifi_cfg_wpakey: key too long\n");
+			outp->wldp_result = WL_NOTSUPPORTED;
+			err = EINVAL;
+			break;
+		}
+		kid = ik->ik_keyix;
+		if (kid == IEEE80211_KEYIX_NONE || kid >= IEEE80211_WEP_NKID) {
+			ieee80211_err("wifi_cfg_wpakey: incorrect keyix\n");
+			outp->wldp_result = WL_NOTSUPPORTED;
+			err = EINVAL;
+			break;
+
+		} else {
+			wk = &ic->ic_nw_keys[kid];
+			/*
+			 * Global slots start off w/o any assigned key index.
+			 * Force one here for consistency with WEPKEY.
+			 */
+			if (wk->wk_keyix == IEEE80211_KEYIX_NONE)
+				wk->wk_keyix = kid;
+			/* in = ic->ic_bss; */
+			in = NULL;
+		}
+
+		KEY_UPDATE_BEGIN(ic);
+		if (ieee80211_crypto_newkey(ic, ik->ik_type,
+			ik->ik_flags, wk)) {
+			wk->wk_keylen = ik->ik_keylen;
+			/* NB: MIC presence is implied by cipher type */
+			if (wk->wk_keylen > IEEE80211_KEYBUF_SIZE)
+				wk->wk_keylen = IEEE80211_KEYBUF_SIZE;
+			wk->wk_keyrsc = ik->ik_keyrsc;
+			wk->wk_keytsc = 0;		/* new key, reset */
+			wk->wk_flags |= ik->ik_flags &
+			    (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV);
+			(void) memset(wk->wk_key, 0, sizeof (wk->wk_key));
+			(void) memcpy(wk->wk_key, ik->ik_keydata,
+			    ik->ik_keylen);
+			if (!ieee80211_crypto_setkey(ic, wk,
+			    in != NULL ? in->in_macaddr : ik->ik_macaddr)) {
+				err = EIO;
+				outp->wldp_result = WL_HW_ERROR;
+			} else if ((ik->ik_flags & IEEE80211_KEY_DEFAULT)) {
+				ic->ic_def_txkey = kid;
+				ieee80211_mac_update(ic);
+			}
+		} else {
+			err = EIO;
+			outp->wldp_result = WL_HW_ERROR;
+		}
+		KEY_UPDATE_END(ic);
+		break;
+	default:
+		ieee80211_err("wifi_cfg_wpakey: unknown command %x\n", cmd);
+		outp->wldp_result = WL_NOTSUPPORTED;
+		err = EINVAL;
+		break;
+	}
+
+	freemsg(*mp);
+	*mp = omp;
+	return (err);
+}
+
+/*
+ * Delete obsolete keys - keys are dynamically exchanged between APs
+ * and wpa daemon.
+ */
+static int
+wifi_cfg_delkey(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
+{
+	mblk_t *omp;
+	wldp_t *outp;
+	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
+	wl_del_key_t *dk = (wl_del_key_t *)inp->wldp_buf;
+	int kid;
+	int err = 0;
+
+	if ((omp = wifi_getoutmsg(*mp, cmd, 0)) == NULL)
+		return (ENOMEM);
+	outp = (wldp_t *)omp->b_rptr;
+
+	switch (cmd) {
+	case WLAN_GET_PARAM:
+		outp->wldp_result = WL_WRITEONLY;
+		err = EINVAL;
+		break;
+	case WLAN_SET_PARAM:
+		ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_delkey: "
+			"keyix=%d\n", dk->idk_keyix);
+		kid = dk->idk_keyix;
+		if (kid == IEEE80211_KEYIX_NONE || kid >= IEEE80211_WEP_NKID) {
+			ieee80211_err("wifi_cfg_delkey: incorrect keyix\n");
+			outp->wldp_result = WL_NOTSUPPORTED;
+			err = EINVAL;
+			break;
+
+		} else {
+			(void) ieee80211_crypto_delkey(ic,
+			    &ic->ic_nw_keys[kid]);
+			ieee80211_mac_update(ic);
+		}
+		break;
+	default:
+		ieee80211_err("wifi_cfg_delkey: unknown command %x\n", cmd);
+		outp->wldp_result = WL_NOTSUPPORTED;
+		err = EINVAL;
+		break;
+	}
+
+	freemsg(*mp);
+	*mp = omp;
+	return (err);
+}
+
+/*
+ * The OPTIE will be used in the association request.
+ */
+static int
+wifi_cfg_setoptie(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
+{
+	mblk_t *omp;
+	wldp_t *outp;
+	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
+	wl_wpa_ie_t *ie_in = (wl_wpa_ie_t *)inp->wldp_buf;
+	char *ie;
+	int err = 0;
+
+	if ((omp = wifi_getoutmsg(*mp, cmd, 0)) == NULL)
+		return (ENOMEM);
+	outp = (wldp_t *)omp->b_rptr;
+
+	switch (cmd) {
+	case WLAN_GET_PARAM:
+		outp->wldp_result = WL_WRITEONLY;
+		err = EINVAL;
+		break;
+	case WLAN_SET_PARAM:
+		ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_setoptie\n");
+		/*
+		 * NB: Doing this for ap operation could be useful (e.g. for
+		 * WPA and/or WME) except that it typically is worthless
+		 * without being able to intervene when processing
+		 * association response frames--so disallow it for now.
+		 */
+		if (ic->ic_opmode != IEEE80211_M_STA) {
+			ieee80211_err("wifi_cfg_setoptie: opmode err\n");
+			err = EINVAL;
+			outp->wldp_result = WL_NOTSUPPORTED;
+			break;
+		}
+		if (ie_in->wpa_ie_len > IEEE80211_MAX_OPT_IE) {
+			ieee80211_err("wifi_cfg_setoptie: optie too long\n");
+			err = EINVAL;
+			outp->wldp_result = WL_NOTSUPPORTED;
+			break;
+		}
+
+		ie = ieee80211_malloc(ie_in->wpa_ie_len);
+		(void) memcpy(ie, ie_in->wpa_ie, ie_in->wpa_ie_len);
+		if (ic->ic_opt_ie != NULL)
+			ieee80211_free(ic->ic_opt_ie);
+		ic->ic_opt_ie = ie;
+		ic->ic_opt_ie_len = ie_in->wpa_ie_len;
+		break;
+	default:
+		ieee80211_err("wifi_cfg_setoptie: unknown command %x\n", cmd);
+		outp->wldp_result = WL_NOTSUPPORTED;
+		err = EINVAL;
+		break;
+	}
+
+	freemsg(*mp);
+	*mp = omp;
+	return (err);
+}
+
+/*
+ * To be compatible with drivers/tools of OpenSolaris.org,
+ * we use a different ID to filter out those APs of WPA mode.
+ */
+static int
+wifi_cfg_scanresults(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
+{
+	mblk_t *omp;
+	wldp_t *outp;
+	wl_wpa_ess_t *sr;
+	ieee80211_node_t *in;
+	ieee80211_node_table_t *nt;
+	int len, ap_num = 0;
+	int err = 0;
+
+	if ((omp = wifi_getoutmsg(*mp, cmd, MAX_BUF_LEN - WIFI_BUF_OFFSET)) ==
+	    NULL) {
+		return (ENOMEM);
+	}
+	outp = (wldp_t *)omp->b_rptr;
+	sr = (wl_wpa_ess_t *)outp->wldp_buf;
+	sr->count = 0;
+
+	switch (cmd) {
+	case WLAN_GET_PARAM:
+		ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_scanresults\n");
+		nt = &ic->ic_scan;
+		IEEE80211_NODE_LOCK(nt);
+		in = list_head(&nt->nt_node);
+		while (in != NULL) {
+			/* filter out non-WPA APs */
+			if (in->in_wpa_ie == NULL) {
+				in = list_next(&nt->nt_node, in);
+				continue;
+			}
+			bcopy(in->in_bssid, sr->ess[ap_num].bssid,
+			    IEEE80211_ADDR_LEN);
+			sr->ess[ap_num].ssid_len = in->in_esslen;
+			bcopy(in->in_essid, sr->ess[ap_num].ssid,
+			    in->in_esslen);
+			sr->ess[ap_num].freq = in->in_chan->ich_freq;
+
+			len = in->in_wpa_ie[1] + 2;
+			bcopy(in->in_wpa_ie, sr->ess[ap_num].wpa_ie, len);
+			sr->ess[ap_num].wpa_ie_len = len;
+
+			ap_num ++;
+			in = list_next(&nt->nt_node, in);
+		}
+		IEEE80211_NODE_UNLOCK(nt);
+		sr->count = ap_num;
+		outp->wldp_length = WIFI_BUF_OFFSET +
+		    offsetof(wl_wpa_ess_t, ess) +
+		    sr->count * sizeof (struct wpa_ess);
+		omp->b_wptr = omp->b_rptr + outp->wldp_length;
+		break;
+	case WLAN_SET_PARAM:
+		outp->wldp_result = WL_READONLY;
+		err = EINVAL;
+		break;
+	default:
+		ieee80211_err("wifi_cfg_scanresults: unknown cmmand %x\n", cmd);
+		outp->wldp_result = WL_NOTSUPPORTED;
+		err = EINVAL;
+		break;
+	}
+
+	freemsg(*mp);
+	*mp = omp;
+	return (err);
+}
+
+/*
+ * Manually control the state of AUTH | DEAUTH | DEASSOC | ASSOC
+ */
+static int
+wifi_cfg_setmlme(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
+{
+	mblk_t *omp;
+	wldp_t *outp;
+	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
+	wl_mlme_t *mlme = (wl_mlme_t *)inp->wldp_buf;
+	ieee80211_node_t *in;
+	int err = 0;
+	uint32_t flags;
+
+	if ((omp = wifi_getoutmsg(*mp, cmd, 0)) == NULL)
+		return (ENOMEM);
+	outp = (wldp_t *)omp->b_rptr;
+
+	switch (cmd) {
+	case WLAN_GET_PARAM:
+		outp->wldp_result = WL_WRITEONLY;
+		err = EINVAL;
+		break;
+	case WLAN_SET_PARAM:
+		ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_setmlme: "
+			"op=%d\n", mlme->im_op);
+		switch (mlme->im_op) {
+		case IEEE80211_MLME_DISASSOC:
+		case IEEE80211_MLME_DEAUTH:
+			if (ic->ic_opmode == IEEE80211_M_STA) {
+				/*
+				 * Mask ic_flags of IEEE80211_F_WPA to disable
+				 * ieee80211_notify temporarily.
+				 */
+				flags = ic->ic_flags;
+				ic->ic_flags &= ~IEEE80211_F_WPA;
+
+				IEEE80211_UNLOCK(ic);
+				ieee80211_new_state(ic, IEEE80211_S_INIT,
+				    mlme->im_reason);
+				IEEE80211_LOCK(ic);
+
+				ic->ic_flags = flags;
+			}
+			break;
+		case IEEE80211_MLME_ASSOC:
+			if (ic->ic_opmode != IEEE80211_M_STA) {
+				ieee80211_err("wifi_cfg_setmlme: opmode err\n");
+				err = EINVAL;
+				outp->wldp_result = WL_NOTSUPPORTED;
+				break;
+			}
+			if (ic->ic_des_esslen != 0) {
+			/*
+			 * Desired ssid specified; must match both bssid and
+			 * ssid to distinguish ap advertising multiple ssid's.
+			 */
+				in = ieee80211_find_node_with_ssid(&ic->ic_scan,
+				    mlme->im_macaddr,
+				    ic->ic_des_esslen, ic->ic_des_essid);
+			} else {
+			/*
+			 * Normal case; just match bssid.
+			 */
+				in = ieee80211_find_node(&ic->ic_scan,
+				    mlme->im_macaddr);
+			}
+			if (in == NULL) {
+				ieee80211_err("wifi_cfg_setmlme: "
+				    "no matched node\n");
+				err = EINVAL;
+				outp->wldp_result = WL_NOTSUPPORTED;
+				break;
+			}
+			IEEE80211_UNLOCK(ic);
+			ieee80211_sta_join(ic, in);
+			IEEE80211_LOCK(ic);
+		}
+		break;
+	default:
+		ieee80211_err("wifi_cfg_delkey: unknown command %x\n", cmd);
+		outp->wldp_result = WL_NOTSUPPORTED;
+		err = EINVAL;
+		break;
+	}
+
+	freemsg(*mp);
+	*mp = omp;
+	return (err);
+}
+
 static int
 wifi_cfg_getset(struct ieee80211com *ic, mblk_t **mp, uint32_t cmd)
 {
@@ -1256,6 +1721,30 @@
 	case WL_RSSI:
 		err = wifi_cfg_rssi(ic, cmd, mp);
 		break;
+	/*
+	 * WPA IOCTLs
+	 */
+	case WL_CAPABILITY:
+		err = wifi_cfg_caps(ic, cmd, mp);
+		break;
+	case WL_WPA:
+		err = wifi_cfg_wpa(ic, cmd, mp);
+		break;
+	case WL_KEY:
+		err = wifi_cfg_wpakey(ic, cmd, mp);
+		break;
+	case WL_DELKEY:
+		err = wifi_cfg_delkey(ic, cmd, mp);
+		break;
+	case WL_SETOPTIE:
+		err = wifi_cfg_setoptie(ic, cmd, mp);
+		break;
+	case WL_SCANRESULTS:
+		err = wifi_cfg_scanresults(ic, cmd, mp);
+		break;
+	case WL_MLME:
+		err = wifi_cfg_setmlme(ic, cmd, mp);
+		break;
 	default:
 		wifi_setupoutmsg(mp1, 0);
 		wp->wldp_result = WL_LACK_FEATURE;
--- a/usr/src/uts/common/io/net80211/net80211_node.c	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/uts/common/io/net80211/net80211_node.c	Fri Apr 27 09:21:03 2007 -0700
@@ -382,7 +382,7 @@
 	 */
 	ieee80211_setbasicrates(&in->in_rates, ic->ic_curmode);
 	IEEE80211_UNLOCK(ic);
-	ieee80211_sta_join(ic, in);
+	ieee80211_sta_join(ic, ieee80211_ref_node(in));
 	IEEE80211_LOCK(ic);
 }
 
@@ -539,6 +539,8 @@
 	ieee80211_node_t *selbs;
 
 	ieee80211_cancel_scan(ic);
+	/* notify SCAN done */
+	ieee80211_notify(ic, EVENT_SCAN_RESULTS);
 	IEEE80211_LOCK(ic);
 
 	/*
@@ -547,7 +549,7 @@
 	 */
 	/* NB: unlocked read should be ok */
 	in = list_head(&nt->nt_node);
-	if (in == NULL) {
+	if (in == NULL && (ic->ic_flags & IEEE80211_F_WPA) == 0) {
 		ieee80211_dbg(IEEE80211_MSG_SCAN, "ieee80211_end_scan: "
 			"no scan candidate\n");
 	notfound:
@@ -570,7 +572,8 @@
 		return;
 	}
 
-	if (ic->ic_flags & IEEE80211_F_SCANONLY) {	/* scan only */
+	if (ic->ic_flags & IEEE80211_F_SCANONLY ||
+	    ic->ic_flags & IEEE80211_F_WPA) {	/* scan only */
 		ic->ic_flags &= ~IEEE80211_F_SCANONLY;
 		IEEE80211_UNLOCK(ic);
 		ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
@@ -608,6 +611,8 @@
 		}
 		in = list_next(&nt->nt_node, in);
 	}
+	if (selbs != NULL)	/* grab ref while dropping lock */
+		(void) ieee80211_ref_node(selbs);
 	IEEE80211_NODE_UNLOCK(nt);
 	if (selbs == NULL)
 		goto notfound;
@@ -648,7 +653,7 @@
 		(ic->ic_flags & IEEE80211_F_SHPREAMBLE) ? "short" : "long",
 		(ic->ic_flags & IEEE80211_F_SHSLOT) ? "short" : "long",
 		(ic->ic_flags&IEEE80211_F_USEPROT) ? ", protection" : "");
-	ieee80211_sta_join(ic, in);
+	ieee80211_sta_join(ic, ieee80211_ref_node(in));
 	return (B_TRUE);
 }
 
@@ -685,7 +690,7 @@
 	 * Committed to selbs, setup state.
 	 */
 	obss = ic->ic_bss;
-	ic->ic_bss = ieee80211_ref_node(selbs);	/* Grab reference */
+	ic->ic_bss = selbs;	/* caller assumed to bump refcnt */
 	if (obss != NULL) {
 		ieee80211_copy_bss(selbs, obss);
 		ieee80211_free_node(obss);
@@ -763,6 +768,8 @@
 	ieee80211com_t *ic = in->in_ic;
 
 	ic->ic_node_cleanup(in);
+	if (in->in_wpa_ie != NULL)
+		ieee80211_free(in->in_wpa_ie);
 	kmem_free(in, sizeof (ieee80211_node_t));
 }
 
@@ -944,6 +951,35 @@
 }
 
 /*
+ * Like find but search based on the ssid too.
+ */
+ieee80211_node_t *
+ieee80211_find_node_with_ssid(ieee80211_node_table_t *nt,
+	const uint8_t *macaddr, uint32_t ssidlen, const uint8_t *ssid)
+{
+	ieee80211_node_t *in;
+	int hash;
+
+	IEEE80211_NODE_LOCK(nt);
+
+	hash = ieee80211_node_hash(macaddr);
+	in = list_head(&nt->nt_hash[hash]);
+	while (in != NULL) {
+		if (IEEE80211_ADDR_EQ(in->in_macaddr, macaddr) &&
+		    in->in_esslen == ssidlen &&
+		    memcmp(in->in_essid, ssid, ssidlen) == 0)
+			break;
+		in = list_next(&nt->nt_hash[hash], in);
+	}
+	if (in != NULL) {
+		(void) ieee80211_ref_node(in); /* mark referenced */
+	}
+	IEEE80211_NODE_UNLOCK(nt);
+
+	return (in);
+}
+
+/*
  * Fake up a node; this handles node discovery in adhoc mode.
  * Note that for the driver's benefit we treat this like an
  * association so the driver has an opportunity to setup it's
@@ -968,6 +1004,31 @@
 	return (in);
 }
 
+static void
+ieee80211_saveie(uint8_t **iep, const uint8_t *ie)
+{
+	uint_t ielen = ie[1]+2;
+	/*
+	 * Record information element for later use.
+	 */
+	if (*iep == NULL || (*iep)[1] != ie[1]) {
+		if (*iep != NULL)
+			ieee80211_free(*iep);
+		*iep = ieee80211_malloc(ielen);
+	}
+	if (*iep != NULL)
+		(void) memcpy(*iep, ie, ielen);
+}
+
+static void
+saveie(uint8_t **iep, const uint8_t *ie)
+{
+	if (ie == NULL)
+		*iep = NULL;
+	else
+		ieee80211_saveie(iep, ie);
+}
+
 /*
  * Process a beacon or probe response frame.
  */
@@ -1037,6 +1098,11 @@
 	 * processing of beacon frames.
 	 */
 	in->in_tim_off = sp->timoff;
+	/*
+	 * Record optional information elements that might be
+	 * used by applications or drivers.
+	 */
+	saveie(&in->in_wpa_ie, sp->wpa);
 
 	/* NB: must be after in_chan is setup */
 	(void) ieee80211_setup_rates(in, sp->rates, sp->xrates,
--- a/usr/src/uts/common/io/net80211/net80211_output.c	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/uts/common/io/net80211/net80211_output.c	Fri Apr 27 09:21:03 2007 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -175,6 +175,7 @@
 ieee80211_encap(ieee80211com_t *ic, mblk_t *mp, ieee80211_node_t *in)
 {
 	struct ieee80211_frame	*wh;
+	struct ieee80211_key *key;
 
 	ASSERT(mp != NULL && MBLKL(mp) >= sizeof (struct ieee80211_frame));
 	wh = (struct ieee80211_frame *)mp->b_rptr;
@@ -183,6 +184,22 @@
 		LE_16(in->in_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT);
 	in->in_txseqs[0]++;
 
+	if (ic->ic_flags & IEEE80211_F_PRIVACY)
+		key = ieee80211_crypto_getkey(ic);
+	else
+		key = NULL;
+
+	/*
+	 * IEEE 802.1X: send EAPOL frames always in the clear.
+	 * WPA/WPA2: encrypt EAPOL keys when pairwise keys are set.
+	 */
+	if (key != NULL && (ic->ic_flags & IEEE80211_F_WPA)) {
+		wh->i_fc[1] |= IEEE80211_FC1_WEP;
+		if (!ieee80211_crypto_enmic(isc, key, mp, 0)) {
+			ieee80211_err("ieee80211_crypto_enmic failed.\n");
+		}
+	}
+
 	return (mp);
 }
 
--- a/usr/src/uts/common/net/Makefile	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/uts/common/net/Makefile	Fri Apr 27 09:21:03 2007 -0700
@@ -21,7 +21,7 @@
 #
 # ident	"%Z%%M%	%I%	%E% SMI"
 #
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # uts/common/net/Makefile
@@ -30,7 +30,8 @@
 include ../../../Makefile.master
 
 HDRS=	af.h if.h if_arp.h if_dl.h if_types.h route.h pfkeyv2.h pfpolicy.h \
-	ppp-comp.h ppp_defs.h pppio.h vjcompress.h sppptun.h pppoe.h radix.h
+	ppp-comp.h ppp_defs.h pppio.h vjcompress.h sppptun.h pppoe.h radix.h \
+	wpa.h
 
 ROOTDIRS= $(ROOT)/usr/include/net
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/net/wpa.h	Fri Apr 27 09:21:03 2007 -0700
@@ -0,0 +1,195 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Macro and data structures defined for 802.11i.
+ */
+
+#ifndef	__WPA_H
+#define	__WPA_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/types.h>
+#include <inet/wifi_ioctl.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#define	SERVICE_NAME			"network/wpa"
+#define	WPA_DOOR 			"/var/run/wpa_door"
+#define	SVC_METHOD			"/usr/lib/inet/wpad"
+
+#define	IEEE80211_ADDR_LEN		6
+#define	IEEE80211_MAX_WPA_IE		40	/* IEEE802.11i */
+#define	WPA_STRSIZE			256
+/*
+ * Max size of optional information elements.  We artificially
+ * constrain this; it's limited only by the max frame size (and
+ * the max parameter size of the wireless extensions).
+ */
+#define	IEEE80211_MAX_OPT_IE		256
+
+/*
+ * Parameters.
+ * WL_WPA_BASE + 0x1, 5, 6 reserved to be compatible with FreeBSD.
+ */
+#define	WL_WPA_BASE			(WL_PARAMETERS_BASE + 0x500)
+#define	WL_SETOPTIE			(WL_WPA_BASE + 0x0)
+#define	WL_WPA				(WL_WPA_BASE + 0x2)
+#define	WL_KEY				(WL_WPA_BASE + 0x3)
+#define	WL_DELKEY			(WL_WPA_BASE + 0x4)
+#define	WL_SCANRESULTS			(WL_WPA_BASE + 0x7)
+#define	WL_MLME				(WL_WPA_BASE + 0x8)
+#define	WL_CAPABILITY			(WL_WPA_BASE + 0x9)
+
+typedef struct wl_wpa_ie {
+    uint32_t	wpa_ie_len;
+    char 	wpa_ie[1];	/* it's the head of wpa_ie */
+} wl_wpa_ie_t;
+
+typedef struct wl_wpa {
+    uint32_t	wpa_flag;
+} wl_wpa_t;
+
+typedef struct wl_capability {
+    uint32_t	caps;
+} wl_capability_t;
+
+#define	IEEE80211_KEYBUF_SIZE		16	/* 128-bit TKIP & CCMP key */
+#define	IEEE80211_MICBUF_SIZE		(8+8)	/* 8 byte tx, 8 byte rx */
+
+/*
+ * NB: these values are ordered carefully; there are lots of
+ * of implications in any reordering.  In particular beware
+ * that 4 is not used to avoid conflicting with IEEE80211_F_PRIVACY.
+ */
+#define	IEEE80211_CIPHER_WEP		0
+#define	IEEE80211_CIPHER_TKIP		1
+#define	IEEE80211_CIPHER_AES_OCB	2
+#define	IEEE80211_CIPHER_AES_CCM	3
+#define	IEEE80211_CIPHER_CKIP		4
+#define	IEEE80211_CIPHER_NONE		5	/* pseudo value */
+
+#define	IEEE80211_CIPHER_MAX		(IEEE80211_CIPHER_NONE+1)
+
+/* Key Flags */
+#define	IEEE80211_KEY_XMIT		0x01	/* key used for xmit */
+#define	IEEE80211_KEY_RECV		0x02	/* key used for recv */
+
+#define	IEEE80211_KEY_DEFAULT		0x80	/* default xmit key */
+
+/*
+ * WPA/RSN get/set key request.  Specify the key/cipher
+ * type and whether the key is to be used for sending and/or
+ * receiving.  The key index should be set only when working
+ * with global keys (use IEEE80211_KEYIX_NONE for ``no index'').
+ * Otherwise a unicast/pairwise key is specified by the bssid
+ * (on a station) or mac address (on an ap).  They key length
+ * must include any MIC key data; otherwise it should be no
+ * more than IEEE80211_KEYBUF_SIZE.
+ */
+#pragma pack(1)
+typedef struct wl_key {
+	uint8_t		ik_type;	/* key/cipher type */
+	uint8_t		ik_pad;
+
+	uint16_t	ik_keyix;	/* key index */
+	uint8_t		ik_keylen;	/* key length in bytes */
+	uint8_t		ik_flags;
+
+	uint8_t		ik_macaddr[IEEE80211_ADDR_LEN];
+	uint64_t	ik_keyrsc;	/* key receive sequence counter */
+	uint64_t	ik_keytsc;	/* key transmit sequence counter */
+
+	uint8_t ik_keydata[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE];
+} wl_key_t;
+#pragma pack()
+
+struct wpa_ess {
+	uint8_t		bssid[IEEE80211_ADDR_LEN];
+	uint8_t		ssid[MAX_ESSID_LENGTH];
+	uint32_t	ssid_len;
+
+	uint8_t		wpa_ie[IEEE80211_MAX_WPA_IE];
+	uint32_t	wpa_ie_len;
+	int		freq;
+};
+
+typedef struct wl_del_key {
+	uint8_t		idk_keyix;	/* key index */
+	uint8_t		idk_macaddr[IEEE80211_ADDR_LEN];
+}wl_del_key_t;
+
+typedef struct wl_countermeasures {
+	uint32_t	cm_flag;
+} wl_countermeasures_t;
+
+typedef struct wl_drop_unenc {
+	uint32_t	drop_flag;
+} wl_drop_unenc_t;
+
+typedef struct wl_wpa_ess {
+	uint32_t	count;
+	struct wpa_ess	ess[1];
+} wl_wpa_ess_t;
+
+#define	IEEE80211_MLME_ASSOC		1	/* associate station */
+#define	IEEE80211_MLME_DISASSOC		2	/* disassociate station */
+#define	IEEE80211_MLME_DEAUTH		3	/* deauthenticate station */
+#define	IEEE80211_MLME_AUTHORIZE	4	/* authorize station */
+#define	IEEE80211_MLME_UNAUTHORIZE	5	/* unauthorize station */
+
+/*
+ *  * MLME state manipulation request.  IEEE80211_MLME_ASSOC
+ *   * only makes sense when operating as a station.  The other
+ *    * requests can be used when operating as a station or an
+ *     * ap (to effect a station).
+ */
+typedef struct wl_mlme {
+	uint8_t		im_op;		/* operation to perform */
+	uint16_t	im_reason;	/* 802.11 reason code */
+	uint8_t		im_macaddr[IEEE80211_ADDR_LEN];
+} wl_mlme_t;
+
+/*
+ * State machine events
+ */
+typedef enum {
+	EVENT_ASSOC,
+	EVENT_DISASSOC,
+	EVENT_SCAN_RESULTS
+} wpa_event_type;
+
+typedef struct  wl_events {
+	wpa_event_type	event;
+} wl_events_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __WPA_H */
--- a/usr/src/uts/common/sys/dld.h	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/uts/common/sys/dld.h	Fri Apr 27 09:21:03 2007 -0700
@@ -123,7 +123,8 @@
  * Secure objects ioctls
  */
 typedef enum {
-	DLD_SECOBJ_CLASS_WEP = 1
+	DLD_SECOBJ_CLASS_WEP = 1,
+	DLD_SECOBJ_CLASS_WPA
 } dld_secobj_class_t;
 
 #define	DLD_SECOBJ_OPT_CREATE	0x00000001
--- a/usr/src/uts/common/sys/ethernet.h	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/uts/common/sys/ethernet.h	Fri Apr 27 09:21:03 2007 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -92,6 +92,8 @@
 #define	ETHERTYPE_SLOW		(0x8809)	/* Slow Protocol */
 #define	ETHERTYPE_PPPOED	(0x8863)	/* PPPoE Discovery Stage */
 #define	ETHERTYPE_PPPOES	(0x8864)	/* PPPoE Session Stage */
+#define	ETHERTYPE_EAPOL		(0x888e)	/* EAPOL protocol */
+#define	ETHERTYPE_RSN_PREAUTH	(0x88c7)	/* RSN PRE-Authentication */
 #define	ETHERTYPE_MAX		(0xffff)	/* Max valid ethernet type */
 
 /*
--- a/usr/src/uts/common/sys/mac_wifi.h	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/uts/common/sys/mac_wifi.h	Fri Apr 27 09:21:03 2007 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -48,7 +48,8 @@
  * May change in the future as new features are added.
  */
 #define	WIFI_HDRSIZE (sizeof (struct ieee80211_frame) + \
-    IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + sizeof (struct ieee80211_llc))
+    IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_EXTIVLEN + \
+    sizeof (struct ieee80211_llc))
 
 enum wifi_stat {
 	/* statistics described in ieee802.11(5) */
@@ -72,7 +73,8 @@
  */
 enum wifi_secmode {
 	WIFI_SEC_NONE,
-	WIFI_SEC_WEP
+	WIFI_SEC_WEP,
+	WIFI_SEC_WPA
 };
 
 /*
--- a/usr/src/uts/common/sys/net80211.h	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/uts/common/sys/net80211.h	Fri Apr 27 09:21:03 2007 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -44,6 +44,7 @@
 #include <sys/ethernet.h>
 #include <sys/net80211_proto.h>
 #include <sys/net80211_crypto.h>
+#include <net/wpa.h>
 
 /*
  * IEEE802.11 kernel support module
@@ -167,6 +168,9 @@
 #define	WME_AC_VI		2	/* video */
 #define	WME_AC_VO		3	/* voice */
 
+#define	MAX_EVENT		16
+#define	MAX_IEEE80211STR	256
+
 /*
  * Authentication mode.
  */
@@ -308,6 +312,7 @@
 
 	uint32_t		*in_challenge;	/* shared-key challenge */
 	struct ieee80211_key	in_ucastkey;	/* unicast key */
+	uint8_t			*in_wpa_ie;	/* captured WPA/RSN ie */
 
 	/* others */
 	int32_t			in_fails;	/* failure count to associate */
@@ -360,6 +365,12 @@
 	/* Cipher state/configuration. */
 	struct ieee80211_crypto_state	ic_crypto;
 
+	kmutex_t		ic_doorlock;
+	char			ic_wpadoor[MAX_IEEE80211STR];
+
+	wpa_event_type		ic_eventq[MAX_EVENT];
+	uint32_t		ic_evq_head, ic_evq_tail;
+
 	/* Runtime states */
 	uint32_t		ic_flags;	/* state/conf flags */
 	uint32_t		ic_flags_ext;	/* extended state flags */
@@ -493,6 +504,8 @@
 	void *);
 ieee80211_node_t *ieee80211_find_node(ieee80211_node_table_t *,
 	const uint8_t *);
+ieee80211_node_t *ieee80211_find_node_with_ssid(ieee80211_node_table_t *,
+	const uint8_t *, uint32_t, const uint8_t *);
 ieee80211_node_t *ieee80211_find_txnode(ieee80211com_t *,
 	const uint8_t daddr[IEEE80211_ADDR_LEN]);
 ieee80211_node_t *ieee80211_find_rxnode(ieee80211com_t *,
@@ -521,6 +534,9 @@
 void ieee80211_start_watchdog(ieee80211com_t *, uint32_t);
 void ieee80211_stop_watchdog(ieee80211com_t *);
 
+void *ieee80211_malloc(size_t);
+void ieee80211_free(void *);
+
 #ifdef	__cplusplus
 }
 #endif
--- a/usr/src/uts/common/sys/net80211_crypto.h	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/uts/common/sys/net80211_crypto.h	Fri Apr 27 09:21:03 2007 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -91,6 +91,17 @@
 #define	IEEE80211_WEP_CRCLEN		4	/* CRC-32 */
 #define	IEEE80211_WEP_NKID		4	/* number of key ids */
 
+/*
+ * 802.11i defines an extended IV for use with non-WEP ciphers.
+ * When the EXTIV bit is set in the key id byte an additional
+ * 4 bytes immediately follow the IV for TKIP.  For CCMP the
+ * EXTIV bit is likewise set but the 8 bytes represent the
+ * CCMP header rather than IV+extended-IV.
+ */
+#define	IEEE80211_WEP_EXTIV		0x20
+#define	IEEE80211_WEP_EXTIVLEN		4	/* extended IV length */
+#define	IEEE80211_WEP_MICLEN		8	/* trailing MIC */
+
 #define	IEEE80211_WEP_HDRLEN					\
 	(IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN)
 #define	IEEE80211_WEP_MINLEN					\
--- a/usr/src/uts/common/sys/net80211_proto.h	Fri Apr 27 08:49:43 2007 -0700
+++ b/usr/src/uts/common/sys/net80211_proto.h	Fri Apr 27 09:21:03 2007 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -385,6 +385,10 @@
 	IEEE80211_ELEMID_VENDOR			= 221	/* vendor private */
 };
 
+#define	WPA_OUI			0xf25000
+#define	WPA_OUI_TYPE		0x01
+#define	WPA_VERSION		1		/* current supported version */
+
 #define	IEEE80211_CHALLENGE_LEN			128
 
 #define	IEEE80211_RATE_BASIC			0x80
@@ -466,6 +470,17 @@
 #define	IEEE80211_WEP_CRCLEN		4	/* CRC-32 */
 #define	IEEE80211_WEP_NKID		4	/* number of key ids */
 
+/*
+ * 802.11i defines an extended IV for use with non-WEP ciphers.
+ * When the EXTIV bit is set in the key id byte an additional
+ * 4 bytes immediately follow the IV for TKIP.  For CCMP the
+ * EXTIV bit is likewise set but the 8 bytes represent the
+ * CCMP header rather than IV+extended-IV.
+ */
+#define	IEEE80211_WEP_EXTIV		0x20
+#define	IEEE80211_WEP_EXTIVLEN		4	/* extended IV length */
+#define	IEEE80211_WEP_MICLEN		8	/* trailing MIC */
+
 #define	IEEE80211_CRC_LEN		4
 
 /*