Chapter 4 Part 8 关联处理源码走读

关联事件处理流程分析

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
的处理。

这两个模块先不解读。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值