Process EAPOL 3/4 frame and plumb PTK and per-link GTK/IGTK/BIGTK keys to driver. Signed-off-by: Veerendranath Jakkam <quic_vjakkam@xxxxxxxxxxx> --- src/rsn_supp/wpa.c | 474 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/rsn_supp/wpa_i.h | 6 + 2 files changed, 478 insertions(+), 2 deletions(-) diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index 5611da3..5d1ff43 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -1220,6 +1220,64 @@ static int wpa_supplicant_install_gtk(struct wpa_sm *sm, } +static int wpa_supplicant_install_mlo_gtk(struct wpa_sm *sm, u8 link_id, + const struct wpa_gtk_data *gd, + const u8 *key_rsc, int wnm_sleep) +{ + const u8 *gtk = gd->gtk; + u8 gtk_buf[32]; + char title[50]; + int ret; + + /* Detect possible key reinstallation */ + if ((sm->mlo.links[link_id].gtk.gtk_len == (size_t) gd->gtk_len && + os_memcmp(sm->mlo.links[link_id].gtk.gtk, gd->gtk, + sm->mlo.links[link_id].gtk.gtk_len) == 0) || + (sm->mlo.links[link_id].gtk_wnm_sleep.gtk_len == + (size_t) gd->gtk_len && + os_memcmp(sm->mlo.links[link_id].gtk_wnm_sleep.gtk, gd->gtk, + sm->mlo.links[link_id].gtk_wnm_sleep.gtk_len) == 0)) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Not reinstalling already in-use GTK to the driver (link_id=%d keyidx=%d tx=%d len=%d)", + link_id, gd->keyidx, gd->tx, gd->gtk_len); + return 0; + } + + ret = os_snprintf(title, sizeof(title), "RSN: Link %u Group Key", + link_id); + if (!os_snprintf_error(sizeof(title), ret)) + wpa_hexdump_key(MSG_DEBUG, title, gd->gtk, gd->gtk_len); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Installing GTK to the driver (link_id=%d keyidx=%d tx=%d len=%d)", + link_id, gd->keyidx, gd->tx, gd->gtk_len); + ret = os_snprintf(title, sizeof(title), "RSN: Link %u RSC", link_id); + if (!os_snprintf_error(sizeof(title), ret)) + wpa_hexdump(MSG_DEBUG, title, key_rsc, gd->key_rsc_len); + if (wpa_sm_set_key(sm, link_id, gd->alg, broadcast_ether_addr, + gd->keyidx, gd->tx, key_rsc, gd->key_rsc_len, gtk, + gd->gtk_len, KEY_FLAG_GROUP_RX) < 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "RSN: Failed to set GTK to the driver (link_id=%d alg=%d keylen=%d keyidx=%d)", + link_id, gd->alg, gd->gtk_len, gd->keyidx); + forced_memzero(gtk_buf, sizeof(gtk_buf)); + return -1; + } + forced_memzero(gtk_buf, sizeof(gtk_buf)); + + if (wnm_sleep) { + sm->mlo.links[link_id].gtk_wnm_sleep.gtk_len = gd->gtk_len; + os_memcpy(sm->mlo.links[link_id].gtk_wnm_sleep.gtk, gd->gtk, + sm->mlo.links[link_id].gtk_wnm_sleep.gtk_len); + } else { + sm->mlo.links[link_id].gtk.gtk_len = gd->gtk_len; + os_memcpy(sm->mlo.links[link_id].gtk.gtk, gd->gtk, + sm->mlo.links[link_id].gtk.gtk_len); + } + + return 0; +} + + static int wpa_supplicant_gtk_tx_bit_workaround(const struct wpa_sm *sm, int tx) { @@ -1268,6 +1326,87 @@ static int wpa_supplicant_rsc_relaxation(const struct wpa_sm *sm, } +static int wpa_supplicant_mlo_gtk(struct wpa_sm *sm, u8 link_id, const u8 *gtk, + size_t gtk_len, int key_info) +{ + struct wpa_gtk_data gd; + const u8 *key_rsc; + char title[100]; + int ret; + + /* + * MLO GTK KDE format: + * KeyID[bits 0-1], Tx [bit 2], Reserved [bit 3], link id [4-7] + * PN + * GTK + */ + os_memset(&gd, 0, sizeof(gd)); + ret = os_snprintf(title, sizeof(title), + "RSN: received link %u GTK in pairwise handshake", + link_id); + if (!os_snprintf_error(sizeof(title), ret)) + wpa_hexdump_key(MSG_DEBUG, title, gtk, gtk_len); + + if (gtk_len < RSN_MLO_GTK_KDE_PREFIX_LENGTH || + gtk_len - RSN_MLO_GTK_KDE_PREFIX_LENGTH > sizeof(gd.gtk)) + return -1; + + gd.keyidx = gtk[0] & 0x3; + gtk += 1; + gtk_len -= 1; + + key_rsc = gtk; + + gtk += 6; + gtk_len -= 6; + + os_memcpy(gd.gtk, gtk, gtk_len); + gd.gtk_len = gtk_len; + + ret = 0; + if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gtk_len, + gtk_len, &gd.key_rsc_len, + &gd.alg) || + wpa_supplicant_install_mlo_gtk(sm, link_id, &gd, key_rsc, 0)) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Failed to install GTK for MLO Link ID %u", + link_id); + ret = -1; + goto out; + } + +out: + forced_memzero(&gd, sizeof(gd)); + return ret; +} + + +static int wpa_supplicant_pairwise_mlo_gtk(struct wpa_sm *sm, + const struct wpa_eapol_key *key, + struct wpa_eapol_ie_parse *ie, + int key_info) +{ + u8 i; + + for (i = 0; i < MAX_NUM_MLO_LINKS; i++) { + if (!(sm->mlo.setup_links & BIT(i))) + continue; + + if (!ie->mlo_gtk[i]) { + wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, + "MLO RSN: GTK not found for link ID %u", i); + return -1; + } + + if (wpa_supplicant_mlo_gtk(sm, i, ie->mlo_gtk[i], + ie->mlo_gtk_len[i], key_info)) + return -1; + } + + return 0; +} + + static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, const struct wpa_eapol_key *key, const u8 *gtk, size_t gtk_len, @@ -1438,6 +1577,185 @@ static int wpa_supplicant_install_bigtk(struct wpa_sm *sm, return 0; } +static int wpa_supplicant_install_mlo_igtk(struct wpa_sm *sm, u8 link_id, + const struct rsn_mlo_igtk_kde *igtk, + int wnm_sleep) +{ + size_t len = wpa_cipher_key_len(sm->mgmt_group_cipher); + u16 keyidx = WPA_GET_LE16(igtk->keyid); + char title[100]; + int ret; + + /* Detect possible key reinstallation */ + if ((sm->mlo.links[link_id].igtk.igtk_len == len && + os_memcmp(sm->mlo.links[link_id].igtk.igtk, igtk->igtk, + sm->mlo.links[link_id].igtk.igtk_len) == 0) || + (sm->mlo.links[link_id].igtk_wnm_sleep.igtk_len == len && + os_memcmp(sm->mlo.links[link_id].igtk_wnm_sleep.igtk, igtk->igtk, + sm->mlo.links[link_id].igtk_wnm_sleep.igtk_len) == 0)) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Not reinstalling already in-use IGTK to the driver (link_id=%d keyidx=%d)", + link_id, keyidx); + return 0; + } + + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: MLO Link %u IGTK keyid %d pn " COMPACT_MACSTR, + link_id, keyidx, MAC2STR(igtk->pn)); + ret = os_snprintf(title, sizeof(title), "RSN: MLO Link %u IGTK", + link_id); + if (!os_snprintf_error(sizeof(title), ret)) + wpa_hexdump_key(MSG_DEBUG, title, igtk->igtk, len); + if (keyidx > 4095) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "RSN: Invalid MLO Link %d IGTK KeyID %d", link_id, + keyidx); + return -1; + } + if (wpa_sm_set_key(sm, link_id, + wpa_cipher_to_alg(sm->mgmt_group_cipher), + broadcast_ether_addr, keyidx, 0, igtk->pn, + sizeof(igtk->pn), igtk->igtk, len, + KEY_FLAG_GROUP_RX) < 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "RSN: Failed to configure MLO Link %d IGTK to the driver", + link_id); + return -1; + } + + if (wnm_sleep) { + sm->mlo.links[link_id].igtk_wnm_sleep.igtk_len = len; + os_memcpy(sm->mlo.links[link_id].igtk_wnm_sleep.igtk, + igtk->igtk, + sm->mlo.links[link_id].igtk_wnm_sleep.igtk_len); + } else { + sm->mlo.links[link_id].igtk.igtk_len = len; + os_memcpy(sm->mlo.links[link_id].igtk.igtk, igtk->igtk, + sm->mlo.links[link_id].igtk.igtk_len); + } + + return 0; +} + + +static int +wpa_supplicant_install_mlo_bigtk(struct wpa_sm *sm, u8 link_id, + const struct rsn_mlo_bigtk_kde *bigtk, + int wnm_sleep) +{ + size_t len = wpa_cipher_key_len(sm->mgmt_group_cipher); + u16 keyidx = WPA_GET_LE16(bigtk->keyid); + char title[100]; + int ret; + + /* Detect possible key reinstallation */ + if ((sm->mlo.links[link_id].bigtk.bigtk_len == len && + os_memcmp(sm->mlo.links[link_id].bigtk.bigtk, bigtk->bigtk, + sm->mlo.links[link_id].bigtk.bigtk_len) == 0) || + (sm->mlo.links[link_id].bigtk_wnm_sleep.bigtk_len == len && + os_memcmp(sm->mlo.links[link_id].bigtk_wnm_sleep.bigtk, + bigtk->bigtk, + sm->mlo.links[link_id].bigtk_wnm_sleep.bigtk_len) + == 0)) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Not reinstalling already in-use BIGTK to the driver (link_id=%d keyidx=%d)", + link_id, keyidx); + return 0; + } + + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: MLO Link %u BIGTK keyid %d pn " COMPACT_MACSTR, + link_id, keyidx, MAC2STR(bigtk->pn)); + ret = os_snprintf(title, sizeof(title), "RSN: MLO Link %u BIGTK", + link_id); + if (!os_snprintf_error(sizeof(title), ret)) + wpa_hexdump_key(MSG_DEBUG, title, bigtk->bigtk, len); + if (keyidx < 6 || keyidx > 7) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid MLO Link %d BIGTK KeyID %d", link_id, + keyidx); + return -1; + } + if (wpa_sm_set_key(sm, link_id, + wpa_cipher_to_alg(sm->mgmt_group_cipher), + broadcast_ether_addr, keyidx, 0, bigtk->pn, + sizeof(bigtk->pn), bigtk->bigtk, len, + KEY_FLAG_GROUP_RX) < 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "RSN: Failed to configure MLO Link %d BIGTK to the driver", + link_id); + return -1; + } + + if (wnm_sleep) { + sm->mlo.links[link_id].bigtk_wnm_sleep.bigtk_len = len; + os_memcpy(sm->mlo.links[link_id].bigtk_wnm_sleep.bigtk, + bigtk->bigtk, + sm->mlo.links[link_id].bigtk_wnm_sleep.bigtk_len); + } else { + sm->mlo.links[link_id].bigtk.bigtk_len = len; + os_memcpy(sm->mlo.links[link_id].bigtk.bigtk, bigtk->bigtk, + sm->mlo.links[link_id].bigtk.bigtk_len); + } + + return 0; +} + + +static int _mlo_ieee80211w_set_keys(struct wpa_sm *sm, u8 link_id, + struct wpa_eapol_ie_parse *ie) +{ + size_t len; + + if (!wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher)) + return 0; + + if (ie->mlo_igtk[link_id]) { + const struct rsn_mlo_igtk_kde *igtk; + + len = wpa_cipher_key_len(sm->mgmt_group_cipher); + if (ie->mlo_igtk_len[link_id] != + (RSN_MLO_IGTK_KDE_PREFIX_LENGTH + len)) + return -1; + + igtk = (const struct rsn_mlo_igtk_kde *) ie->mlo_igtk[link_id]; + if (wpa_supplicant_install_mlo_igtk(sm, link_id, igtk, 0) < 0) + return -1; + } + + if (ie->mlo_bigtk[link_id] && sm->beacon_prot) { + const struct rsn_mlo_bigtk_kde *bigtk; + + len = wpa_cipher_key_len(sm->mgmt_group_cipher); + if (ie->mlo_bigtk_len[link_id] != + (RSN_MLO_BIGTK_KDE_PREFIX_LENGTH + len)) + return -1; + + bigtk = (const struct rsn_mlo_bigtk_kde *) ie->mlo_bigtk[link_id]; + if (wpa_supplicant_install_mlo_bigtk(sm, link_id, bigtk, 0) < 0) + return -1; + } + + return 0; +} + + +static int mlo_ieee80211w_set_keys(struct wpa_sm *sm, + struct wpa_eapol_ie_parse *ie) +{ + u8 i; + + for (i = 0; i < MAX_NUM_MLO_LINKS; i++) { + if (!(sm->mlo.setup_links & BIT(i))) + continue; + + if (_mlo_ieee80211w_set_keys(sm, i, ie)) + return -1; + } + + return 0; +} + static int ieee80211w_set_keys(struct wpa_sm *sm, struct wpa_eapol_ie_parse *ie) @@ -1799,6 +2117,130 @@ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, } +static void wpa_supplicant_process_mlo_3_of_4(struct wpa_sm *sm, + const struct wpa_eapol_key *key, + u16 ver, const u8 *key_data, + size_t key_data_len) +{ + u16 key_info, keylen; + struct wpa_eapol_ie_parse ie; + int res; + + wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE); + wpa_dbg(sm->ctx->msg_ctx, MSG_INFO, "RSN MLO: RX message 3 of 4-Way " + "Handshake from " MACSTR " (ver=%d)", MAC2STR(sm->bssid), ver); + + key_info = WPA_GET_BE16(key->key_info); + + wpa_hexdump(MSG_DEBUG, "RSN MLO: IE KeyData", key_data, key_data_len); + if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0) + goto failed; + + if (!ie.valid_mlo_gtks) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "MLO RSN: No GTK KDE included in EAPOL-Key msg 3/4"); + goto failed; + } + if ((key_info & + (WPA_KEY_INFO_ENCR_KEY_DATA | WPA_KEY_INFO_INSTALL | + WPA_KEY_INFO_SECURE)) != + (WPA_KEY_INFO_ENCR_KEY_DATA | WPA_KEY_INFO_INSTALL | + WPA_KEY_INFO_SECURE)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "RSN MLO: Invalid key info in EAPOL-Key msg 3/4"); + goto failed; + } + +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->key_mgmt) && + wpa_supplicant_validate_ie_ft(sm, sm->bssid, &ie) < 0) + goto failed; +#endif /* CONFIG_IEEE80211R */ + + if (os_memcmp(sm->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "RSN MLO: ANonce from message 1 of 4-Way Handshake " + "differs from 3 of 4-Way Handshake - drop packet (src=" + MACSTR ")", MAC2STR(sm->bssid)); + goto failed; + } + + keylen = WPA_GET_BE16(key->key_length); + if (keylen != wpa_cipher_key_len(sm->pairwise_cipher)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "RSN MLO: Invalid %s key length %d (src=" MACSTR")", + wpa_cipher_txt(sm->pairwise_cipher), keylen, + MAC2STR(sm->bssid)); + goto failed; + } + + if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info, + &sm->ptk) < 0) + goto failed; + + /* 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. */ + sm->renew_snonce = 1; + + + if (sm->use_ext_key_id) + res = wpa_supplicant_activate_ptk(sm); + else + res = wpa_supplicant_install_ptk(sm, key, KEY_FLAG_RX_TX); + if (res) + goto failed; + + wpa_sm_mlme_setprotection(sm, sm->bssid, + MLME_SETPROTECTION_PROTECT_TYPE_RX, + MLME_SETPROTECTION_KEY_TYPE_PAIRWISE); + eapol_sm_notify_portValid(sm->eapol, true); + + wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE); + + if (wpa_supplicant_pairwise_mlo_gtk(sm, key, &ie, key_info) < 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "MLO RSN: Failed to configure MLO GTKs"); + goto failed; + } + + if (mlo_ieee80211w_set_keys(sm, &ie) < 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "MLO RSN: Failed to configure IGTK"); + goto failed; + } + + wpa_supplicant_key_neg_complete(sm, sm->bssid, + key_info & WPA_KEY_INFO_SECURE); + wpa_sm_set_rekey_offload(sm); + + /* Add PMKSA cache entry for Suite B AKMs here since PMKID can be + * calculated only after KCK has been derived. Though, do not replace an + * existing PMKSA entry after each 4-way handshake (i.e., new KCK/PMKID) + * to avoid unnecessary changes of PMKID while continuing to use the + * same PMK. */ + if (sm->proto == WPA_PROTO_RSN && wpa_key_mgmt_suite_b(sm->key_mgmt) && + !sm->cur_pmksa) { + struct rsn_pmksa_cache_entry *sa; + + sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, NULL, + sm->ptk.kck, sm->ptk.kck_len, + sm->bssid, sm->own_addr, + sm->network_ctx, sm->key_mgmt, NULL); + if (!sm->cur_pmksa) + sm->cur_pmksa = sa; + } + + if (ie.transition_disable) + wpa_sm_transition_disable(sm, ie.transition_disable[0]); + sm->msg_3_of_4_ok = 1; + return; + +failed: + wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); +} + + static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, const struct wpa_eapol_key *key, u16 ver, const u8 *key_data, @@ -2861,8 +3303,13 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, if (key_info & (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ENCR_KEY_DATA)) { /* 3/4 4-Way Handshake */ - wpa_supplicant_process_3_of_4(sm, key, ver, key_data, - key_data_len); + if (sm->mlo.setup_links) + wpa_supplicant_process_mlo_3_of_4( + sm, key, ver, key_data, key_data_len); + else + wpa_supplicant_process_3_of_4(sm, key, ver, + key_data, + key_data_len); } else { /* 1/4 4-Way Handshake */ wpa_supplicant_process_1_of_4(sm, src_addr, key, @@ -3159,6 +3606,7 @@ void wpa_sm_deinit(struct wpa_sm *sm) void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) { int clear_keys = 1; + int i; if (sm == NULL) return; @@ -3216,6 +3664,16 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) os_memset(&sm->igtk, 0, sizeof(sm->igtk)); os_memset(&sm->igtk_wnm_sleep, 0, sizeof(sm->igtk_wnm_sleep)); sm->tk_set = false; + for (i = 0; i < MAX_NUM_MLD_LINKS; i++) { + os_memset(&sm->mlo.links[i].gtk, 0, + sizeof(sm->mlo.links[i].gtk)); + os_memset(&sm->mlo.links[i].gtk_wnm_sleep, 0, + sizeof(sm->mlo.links[i].gtk_wnm_sleep)); + os_memset(&sm->mlo.links[i].igtk, 0, + sizeof(sm->mlo.links[i].igtk)); + os_memset(&sm->mlo.links[i].igtk_wnm_sleep, 0, + sizeof(sm->mlo.links[i].igtk_wnm_sleep)); + } } #ifdef CONFIG_TDLS @@ -4063,6 +4521,8 @@ struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_get(struct wpa_sm *sm, void wpa_sm_drop_sa(struct wpa_sm *sm) { + int i; + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PMK and PTK"); sm->ptk_set = 0; sm->tptk_set = 0; @@ -4075,6 +4535,16 @@ void wpa_sm_drop_sa(struct wpa_sm *sm) os_memset(&sm->gtk_wnm_sleep, 0, sizeof(sm->gtk_wnm_sleep)); os_memset(&sm->igtk, 0, sizeof(sm->igtk)); os_memset(&sm->igtk_wnm_sleep, 0, sizeof(sm->igtk_wnm_sleep)); + for (i = 0; i < MAX_NUM_MLD_LINKS; i++) { + os_memset(&sm->mlo.links[i].gtk, 0, + sizeof(sm->mlo.links[i].gtk)); + os_memset(&sm->mlo.links[i].gtk_wnm_sleep, 0, + sizeof(sm->mlo.links[i].gtk_wnm_sleep)); + os_memset(&sm->mlo.links[i].igtk, 0, + sizeof(sm->mlo.links[i].igtk)); + os_memset(&sm->mlo.links[i].igtk_wnm_sleep, 0, + sizeof(sm->mlo.links[i].igtk_wnm_sleep)); + } #ifdef CONFIG_IEEE80211R os_memset(sm->xxkey, 0, sizeof(sm->xxkey)); sm->xxkey_len = 0; diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index 28a2509..45717f8 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -24,6 +24,12 @@ struct wpa_sm_link { u8 bssid[ETH_ALEN]; u8 *ap_rsne, *ap_rsnxe; size_t ap_rsne_len, ap_rsnxe_len; + struct wpa_gtk gtk; + struct wpa_gtk gtk_wnm_sleep; + struct wpa_igtk igtk; + struct wpa_igtk igtk_wnm_sleep; + struct wpa_bigtk bigtk; + struct wpa_bigtk bigtk_wnm_sleep; } links[MAX_NUM_MLD_LINKS]; struct wpa_sm_mlo { -- 2.7.4 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap