关联事件处理流程分析
WPAS中的driver wrapper将收到来自wlan driver的 NL80211_CMD_CONNECT 命令
case NL80211_CMD_CONNECT:
case NL80211_CMD_ROAM:
/*
调用mlme_event_connect进行处理
NL80211_ATTR_STATUS_CODE 保存AP的处理结果
NL80211_ATTR_REQ_IE 保存Association Request请求时包含的IE
NL80211_ATTR_RESP_IE 保存Association Response请求时包含的IE
*/
mlme_event_connect(drv, cmd,
tb[NL80211_ATTR_STATUS_CODE],
tb[NL80211_ATTR_MAC],
tb[NL80211_ATTR_REQ_IE],
tb[NL80211_ATTR_RESP_IE],
tb[NL80211_ATTR_TIMED_OUT],
tb[NL80211_ATTR_TIMEOUT_REASON],
NULL, NULL, NULL,
tb[NL80211_ATTR_FILS_KEK],
NULL,
tb[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM],
tb[NL80211_ATTR_PMK],
tb[NL80211_ATTR_PMKID]);
break;
static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
enum nl80211_commands cmd, struct nlattr *status,
struct nlattr *addr, struct nlattr *req_ie,
struct nlattr *resp_ie,
struct nlattr *timed_out,
struct nlattr *timeout_reason,
struct nlattr *authorized,
struct nlattr *key_replay_ctr,
struct nlattr *ptk_kck,
struct nlattr *ptk_kek,
struct nlattr *subnet_status,
struct nlattr *fils_erp_next_seq_num,
struct nlattr *fils_pmk,
struct nlattr *fils_pmkid)
{
union wpa_event_data event;
const u8 *ssid = NULL;
u16 status_code;
int ssid_len;
//wlan driver支持SME时的处理
if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
/*
* Avoid reporting two association events that would confuse
* the core code.
*/
wpa_printf(MSG_DEBUG, "nl80211: Ignore connect event (cmd=%d) "
"when using userspace SME", cmd);
return;
}
drv->connect_reassoc = 0;
status_code = status ? nla_get_u16(status) : WLAN_STATUS_SUCCESS;
if (cmd == NL80211_CMD_CONNECT) {
wpa_printf(MSG_DEBUG,
"nl80211: Connect event (status=%u ignore_next_local_disconnect=%d)",
status_code, drv->ignore_next_local_disconnect);
} else if (cmd == NL80211_CMD_ROAM) {
wpa_printf(MSG_DEBUG, "nl80211: Roam event");
}
os_memset(&event, 0, sizeof(event));
if (cmd == NL80211_CMD_CONNECT && status_code != WLAN_STATUS_SUCCESS) {
//关联AP失败时的处理
if (addr)
event.assoc_reject.bssid = nla_data(addr);
if (drv->ignore_next_local_disconnect) {
drv->ignore_next_local_disconnect = 0;
if (!event.assoc_reject.bssid ||
(os_memcmp(event.assoc_reject.bssid,
drv->auth_attempt_bssid,
ETH_ALEN) != 0)) {
/*
* Ignore the event that came without a BSSID or
* for the old connection since this is likely
* not relevant to the new Connect command.
*/
wpa_printf(MSG_DEBUG,
"nl80211: Ignore connection failure event triggered during reassociation");
return;
}
}
if (resp_ie) {
event.assoc_reject.resp_ies = nla_data(resp_ie);
event.assoc_reject.resp_ies_len = nla_len(resp_ie);
}
event.assoc_reject.status_code = status_code;
event.assoc_reject.timed_out = timed_out != NULL;
if (timed_out && timeout_reason) {
enum nl80211_timeout_reason reason;
reason = nla_get_u32(timeout_reason);
switch (reason) {
case NL80211_TIMEOUT_SCAN:
event.assoc_reject.timeout_reason = "scan";
break;
case NL80211_TIMEOUT_AUTH:
event.assoc_reject.timeout_reason = "auth";
break;
case NL80211_TIMEOUT_ASSOC:
event.assoc_reject.timeout_reason = "assoc";
break;
default:
break;
}
}
if (fils_erp_next_seq_num)
event.assoc_reject.fils_erp_next_seq_num =
nla_get_u16(fils_erp_next_seq_num);
wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event);
return;
}
//关联成功
drv->associated = 1;
//保存bssid信息
if (addr) {
os_memcpy(drv->bssid, nla_data(addr), ETH_ALEN);
os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
}
//保存Association Request ie信息
if (req_ie) {
event.assoc_info.req_ies = nla_data(req_ie);
event.assoc_info.req_ies_len = nla_len(req_ie);
if (cmd == NL80211_CMD_ROAM) {
ssid = get_ie(event.assoc_info.req_ies,
event.assoc_info.req_ies_len,
WLAN_EID_SSID);
if (ssid && ssid[1] > 0 && ssid[1] <= 32) {
drv->ssid_len = ssid[1];
os_memcpy(drv->ssid, ssid + 2, ssid[1]);
wpa_printf(MSG_DEBUG,
"nl80211: Set drv->ssid based on req_ie to '%s'",
wpa_ssid_txt(drv->ssid,
drv->ssid_len));
}
}
}
//保存Association Response ie信息
if (resp_ie) {
event.assoc_info.resp_ies = nla_data(resp_ie);
event.assoc_info.resp_ies_len = nla_len(resp_ie);
}
//通过发送 NL80211_GET_SCAN 命令获取STA的工作频率
event.assoc_info.freq = nl80211_get_assoc_freq(drv);
drv->first_bss->freq = drv->assoc_freq;
if ((!ssid || ssid[1] == 0 || ssid[1] > 32) &&
(ssid_len = nl80211_get_assoc_ssid(drv, drv->ssid)) > 0) {
/* When this connection was initiated outside of wpa_supplicant,
* drv->ssid needs to be set here to satisfy later checking. */
drv->ssid_len = ssid_len;
wpa_printf(MSG_DEBUG,
"nl80211: Set drv->ssid based on scan res info to '%s'",
wpa_ssid_txt(drv->ssid, drv->ssid_len));
}
if (authorized && nla_get_u8(authorized)) {
event.assoc_info.authorized = 1;
wpa_printf(MSG_DEBUG, "nl80211: connection authorized");
}
if (key_replay_ctr) {
event.assoc_info.key_replay_ctr = nla_data(key_replay_ctr);
event.assoc_info.key_replay_ctr_len = nla_len(key_replay_ctr);
}
if (ptk_kck) {
event.assoc_info.ptk_kck = nla_data(ptk_kck);
event.assoc_info.ptk_kck_len = nla_len(ptk_kck);
}
if (ptk_kek) {
event.assoc_info.ptk_kek = nla_data(ptk_kek);
event.assoc_info.ptk_kek_len = nla_len(ptk_kek);
}
if (subnet_status) {
/*
* At least for now, this is only available from
* QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS and that
* attribute has the same values 0, 1, 2 as are used in the
* variable here, so no mapping between different values are
* needed.
*/
event.assoc_info.subnet_status = nla_get_u8(subnet_status);
}
if (fils_erp_next_seq_num)
event.assoc_info.fils_erp_next_seq_num =
nla_get_u16(fils_erp_next_seq_num);
if (fils_pmk) {
event.assoc_info.fils_pmk = nla_data(fils_pmk);
event.assoc_info.fils_pmk_len = nla_len(fils_pmk);
}
if (fils_pmkid)
event.assoc_info.fils_pmkid = nla_data(fils_pmkid);
//重要函数
wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
}
wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); 实际上是 wpa_supplicant_event_assoc(wpa_s, data);
static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
u8 bssid[ETH_ALEN];
int ft_completed, already_authorized;
int new_bss = 0;
#if defined(CONFIG_FILS) || defined(CONFIG_MBO)
struct wpa_bss *bss;
#endif /* CONFIG_FILS || CONFIG_MBO */
#ifdef CONFIG_AP
if (wpa_s->ap_iface) {
if (!data)
return;
hostapd_notif_assoc(wpa_s->ap_iface->bss[0],
data->assoc_info.addr,
data->assoc_info.req_ies,
data->assoc_info.req_ies_len,
data->assoc_info.reassoc);
return;
}
#endif /* CONFIG_AP */
eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
wpa_s->own_reconnect_req = 0;
//判断Fast Tratition是否完成
ft_completed = wpa_ft_is_completed(wpa_s->wpa);
//更新 RSN/WPA IE 信息
if (data && wpa_supplicant_event_associnfo(wpa_s, data) < 0)
return;
if (!ft_completed)
ft_completed = wpa_fils_is_completed(wpa_s->wpa);
if (wpa_drv_get_bssid(wpa_s, bssid) < 0) {
wpa_dbg(wpa_s, MSG_ERROR, "Failed to get BSSID");
wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
return;
}
#ifdef CONFIG_WAPI_SUPPORT
if (wpa_s->wpa_proto != WPA_PROTO_WAPI)
#endif
wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATED);
if (os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) {
if (os_reltime_initialized(&wpa_s->session_start)) {
os_reltime_age(&wpa_s->session_start,
&wpa_s->session_length);
wpa_s->session_start.sec = 0;
wpa_s->session_start.usec = 0;
wpas_notify_session_length(wpa_s);
} else {
wpas_notify_auth_changed(wpa_s);
os_get_reltime(&wpa_s->session_start);
}
wpa_dbg(wpa_s, MSG_DEBUG, "Associated to a new BSS: BSSID="
MACSTR, MAC2STR(bssid));
new_bss = 1;
random_add_randomness(bssid, ETH_ALEN);
os_memcpy(wpa_s->bssid, bssid, ETH_ALEN);
os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
wpas_notify_bssid_changed(wpa_s);
if (wpa_supplicant_dynamic_keys(wpa_s) && !ft_completed) {
wpa_clear_keys(wpa_s, bssid);
}
if (wpa_supplicant_select_config(wpa_s) < 0) {
wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
return;
}
}
multi_ap_set_4addr_mode(wpa_s);
if (wpa_s->conf->ap_scan == 1 &&
wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION) {
if (wpa_supplicant_assoc_update_ie(wpa_s) < 0 && new_bss)
wpa_msg(wpa_s, MSG_WARNING,
"WPA/RSN IEs not updated");
}
wpas_fst_update_mb_assoc(wpa_s, data);
#ifdef CONFIG_SME
os_memcpy(wpa_s->sme.prev_bssid, bssid, ETH_ALEN);
wpa_s->sme.prev_bssid_set = 1;
wpa_s->sme.last_unprot_disconnect.sec = 0;
#endif /* CONFIG_SME */
wpa_msg(wpa_s, MSG_INFO, "Associated with " MACSTR, MAC2STR(bssid));
#ifdef CONFIG_WAPI_SUPPORT
/**
* migration notice: please pay attention to if there's any changes under these code
* in new wpa_supplicant.
*/
if (wpa_s->wpa_proto == WPA_PROTO_WAPI) {
wapi_event_assoc(wpa_s);
return;
}
#endif
if (wpa_s->current_ssid) {
/* When using scanning (ap_scan=1), SIM PC/SC interface can be
* initialized before association, but for other modes,
* initialize PC/SC here, if the current configuration needs
* smartcard or SIM/USIM. */
wpa_supplicant_scard_init(wpa_s, wpa_s->current_ssid);
}
wpa_sm_notify_assoc(wpa_s->wpa, bssid);
if (wpa_s->l2)
l2_packet_notify_auth_start(wpa_s->l2);
already_authorized = data && data->assoc_info.authorized;
/*
* Set portEnabled first to false in order to get EAP state machine out
* of the SUCCESS state and eapSuccess cleared. Without this, EAPOL PAE
* state machine may transit to AUTHENTICATING state based on obsolete
* eapSuccess and then trigger BE_AUTH to SUCCESS and PAE to
* AUTHENTICATED without ever giving chance to EAP state machine to
* reset the state.
*/
if (!ft_completed && !already_authorized) {
eapol_sm_notify_portEnabled(wpa_s->eapol, false);
eapol_sm_notify_portValid(wpa_s->eapol, false);
}
if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
wpa_s->key_mgmt == WPA_KEY_MGMT_DPP ||
wpa_s->key_mgmt == WPA_KEY_MGMT_OWE || ft_completed ||
already_authorized || wpa_s->drv_authorized_port)
eapol_sm_notify_eap_success(wpa_s->eapol, false);
/* 802.1X::portControl = Auto */
eapol_sm_notify_portEnabled(wpa_s->eapol, true);
wpa_s->eapol_received = 0;
if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE ||
(wpa_s->current_ssid &&
wpa_s->current_ssid->mode == WPAS_MODE_IBSS)) {
if (wpa_s->current_ssid &&
wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE &&
(wpa_s->drv_flags &
WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE)) {
/*
* Set the key after having received joined-IBSS event
* from the driver.
*/
wpa_supplicant_set_wpa_none_key(wpa_s,
wpa_s->current_ssid);
}
wpa_supplicant_cancel_auth_timeout(wpa_s);
wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
} else if (!ft_completed) {
/* Timeout for receiving the first EAPOL packet */
wpa_supplicant_req_auth_timeout(wpa_s, 10, 0);
}
wpa_supplicant_cancel_scan(wpa_s);
if (ft_completed) {
/*
* FT protocol completed - make sure EAPOL state machine ends
* up in authenticated.
*/
wpa_supplicant_cancel_auth_timeout(wpa_s);
wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
eapol_sm_notify_portValid(wpa_s->eapol, true);
eapol_sm_notify_eap_success(wpa_s->eapol, true);
} else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK) &&
wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) {
/*
* We are done; the driver will take care of RSN 4-way
* handshake.
*/
wpa_supplicant_cancel_auth_timeout(wpa_s);
wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
eapol_sm_notify_portValid(wpa_s->eapol, true);
eapol_sm_notify_eap_success(wpa_s->eapol, true);
} else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X) &&
wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
/*
* The driver will take care of RSN 4-way handshake, so we need
* to allow EAPOL supplicant to complete its work without
* waiting for WPA supplicant.
*/
eapol_sm_notify_portValid(wpa_s->eapol, true);
}
wpa_s->last_eapol_matches_bssid = 0;
#ifdef CONFIG_TESTING_OPTIONS
if (wpa_s->rsne_override_eapol) {
wpa_printf(MSG_DEBUG,
"TESTING: RSNE EAPOL-Key msg 2/4 override");
wpa_sm_set_assoc_wpa_ie(wpa_s->wpa,
wpabuf_head(wpa_s->rsne_override_eapol),
wpabuf_len(wpa_s->rsne_override_eapol));
}
if (wpa_s->rsnxe_override_eapol) {
wpa_printf(MSG_DEBUG,
"TESTING: RSNXE EAPOL-Key msg 2/4 override");
wpa_sm_set_assoc_rsnxe(wpa_s->wpa,
wpabuf_head(wpa_s->rsnxe_override_eapol),
wpabuf_len(wpa_s->rsnxe_override_eapol));
}
#endif /* CONFIG_TESTING_OPTIONS */
if (wpa_s->pending_eapol_rx) {
struct os_reltime now, age;
os_get_reltime(&now);
os_reltime_sub(&now, &wpa_s->pending_eapol_rx_time, &age);
if (age.sec == 0 && age.usec < 200000 &&
os_memcmp(wpa_s->pending_eapol_rx_src, bssid, ETH_ALEN) ==
0) {
wpa_dbg(wpa_s, MSG_DEBUG, "Process pending EAPOL "
"frame that was received just before "
"association notification");
wpa_supplicant_rx_eapol(
wpa_s, wpa_s->pending_eapol_rx_src,
wpabuf_head(wpa_s->pending_eapol_rx),
wpabuf_len(wpa_s->pending_eapol_rx));
}
wpabuf_free(wpa_s->pending_eapol_rx);
wpa_s->pending_eapol_rx = NULL;
}
#ifdef CONFIG_WEP
if ((wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) &&
wpa_s->current_ssid &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE)) {
/* Set static WEP keys again */
wpa_set_wep_keys(wpa_s, wpa_s->current_ssid);
}
#endif /* CONFIG_WEP */
#ifdef CONFIG_IBSS_RSN
if (wpa_s->current_ssid &&
wpa_s->current_ssid->mode == WPAS_MODE_IBSS &&
wpa_s->key_mgmt != WPA_KEY_MGMT_NONE &&
wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE &&
wpa_s->ibss_rsn == NULL) {
wpa_s->ibss_rsn = ibss_rsn_init(wpa_s, wpa_s->current_ssid);
if (!wpa_s->ibss_rsn) {
wpa_msg(wpa_s, MSG_INFO, "Failed to init IBSS RSN");
wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
return;
}
ibss_rsn_set_psk(wpa_s->ibss_rsn, wpa_s->current_ssid->psk);
}
#endif /* CONFIG_IBSS_RSN */
wpas_wps_notify_assoc(wpa_s, bssid);
if (data) {
wmm_ac_notify_assoc(wpa_s, data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len,
&data->assoc_info.wmm_params);
if (wpa_s->reassoc_same_bss)
wmm_ac_restore_tspecs(wpa_s);
}
#if defined(CONFIG_FILS) || defined(CONFIG_MBO)
bss = wpa_bss_get_bssid(wpa_s, bssid);
#endif /* CONFIG_FILS || CONFIG_MBO */
#ifdef CONFIG_FILS
if (wpa_key_mgmt_fils(wpa_s->key_mgmt)) {
const u8 *fils_cache_id = wpa_bss_get_fils_cache_id(bss);
if (fils_cache_id)
wpa_sm_set_fils_cache_id(wpa_s->wpa, fils_cache_id);
}
#endif /* CONFIG_FILS */
#ifdef CONFIG_MBO
wpas_mbo_check_pmf(wpa_s, bss, wpa_s->current_ssid);
#endif /* CONFIG_MBO */
#ifdef CONFIG_DPP2
wpa_s->dpp_pfs_fallback = 0;
#endif /* CONFIG_DPP2 */
}
至此,WPAS已经和目标AP完成了Association操作,接来下将进入802.1X身份认证
流程。接下来的工作将是4-Way Handshake和Group Key Handshake
的处理。
这两个模块先不解读。