应用层roam过程分析
在应用层使用下面的命令进行roam的时候,应用层干了什么事情?本文重点分析应用层wpa程序roam过程。
wpa_cli -p/var/run/wpa_supplicant-ath0 -iath0 roam 00:0f:ff:01:40:12
wpa_supplicant 漫游过程分析
当我们主动发送roam命令的时候,wpa_s收到wpa_cli命令时候进入wpa_supplicant_ctrl_iface_process函数
char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
char *buf, size_t *resp_len)
{
if (os_strncmp(buf, "ROAM ", 5) == 0) {
if (wpa_supplicant_ctrl_iface_roam(wpa_s, buf + 5))
reply_len = -1;
}
}
接着调用wpa_supplicant_ctrl_iface_roam函数
wpa_supplicant_ctrl_iface_roam
static int wpa_supplicant_ctrl_iface_roam(struct wpa_supplicant *wpa_s,
char *addr)
{
u8 bssid[ETH_ALEN];
struct wpa_bss *bss;
struct wpa_ssid *ssid = wpa_s->current_ssid;
// 漫游目标addr转换为bssid
if (hwaddr_aton(addr, bssid)) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: invalid "
"address '%s'", addr);
return -1;
}
// 如果当前连接为空,则直接返回,不允许roam
if (!ssid) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: No network "
"configuration known for the target AP");
return -1;
}
//根据传入的bssid 得到bss
bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len);
/*
* TODO: Find best network configuration block from configuration to
* allow roaming to other networks
*/
wpa_s->reassociate = 1;
wpa_supplicant_connect(wpa_s, bss, ssid);// 继续往下分析
return 0;
}
接下来分析wpa_supplicant_connect函数
wpa_supplicant_connect
int wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
struct wpa_bss *selected,
struct wpa_ssid *ssid)
{
// 一段调试信息
wpa_msg(wpa_s, MSG_DEBUG,
"Considering connect request: reassociate: %d selected: "
MACSTR " bssid: " MACSTR " pending: " MACSTR
" wpa_state: %s ssid=%p current_ssid=%p",
wpa_s->reassociate, MAC2STR(selected->bssid),
MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid),
wpa_supplicant_state_txt(wpa_s->wpa_state),
ssid, wpa_s->current_ssid);
/*
* Do not trigger new association unless the BSSID has changed or if
* reassociation is requested. If we are in process of associating with
* the selected BSSID, do not trigger new attempt.
*/
// 除非BSSID已更改或请求重新关联,否则不要触发新的关联。
// 如果我们正在与所选BSSID关联,请不要触发新的尝试。
if (wpa_s->reassociate ||
(os_memcmp(selected->bssid, wpa_s->bssid, ETH_ALEN) != 0 &&
((wpa_s->wpa_state != WPA_ASSOCIATING &&
wpa_s->wpa_state != WPA_AUTHENTICATING) ||
(!is_zero_ether_addr(wpa_s->pending_bssid) &&
os_memcmp(selected->bssid, wpa_s->pending_bssid, ETH_ALEN) !=
0) ||
(is_zero_ether_addr(wpa_s->pending_bssid) &&
ssid != wpa_s->current_ssid)))) {
if (wpa_supplicant_scard_init(wpa_s, ssid)) {
wpa_supplicant_req_new_scan(wpa_s, 10, 0);
return 0;
}
wpa_msg(wpa_s, MSG_DEBUG, "Request association with " MACSTR,
MAC2STR(selected->bssid));
// 重点 开始关联
wpa_supplicant_associate(wpa_s, selected, ssid);
} else {
wpa_dbg(wpa_s, MSG_DEBUG, "Already associated or trying to "
"connect with the selected AP");
}
return 0;
}
接下来分析wpa_supplicant_associate
wpa_supplicant_associate
void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, struct wpa_ssid *ssid)
{
struct wpa_connect_work *cwork;
int rand_style;
wpa_s->own_disconnect_req = 0;
wpa_s->own_reconnect_req = 0;
/*
* If we are starting a new connection, any previously pending EAPOL
* RX cannot be valid anymore.
*/
wpabuf_free(wpa_s->pending_eapol_rx);
wpa_s->pending_eapol_rx = NULL;
if (ssid->mac_addr == -1)
rand_style = wpa_s->conf->mac_addr;
else
rand_style = ssid->mac_addr;
wpa_s->multi_ap_ie = 0;
wmm_ac_clear_saved_tspecs(wpa_s);
wpa_s->reassoc_same_bss = 0;
wpa_s->reassoc_same_ess = 0;
#ifdef CONFIG_TESTING_OPTIONS
wpa_s->testing_resend_assoc = 0;
#endif /* CONFIG_TESTING_OPTIONS */
/*
* Get wideband support for the interface.
* It is disabled by default unless explicitly enabled.
*/
wpa_s->wideband_support = 0;
wpa_drv_get_wideband_support(wpa_s);
if (wpa_s->last_ssid == ssid) {
wpa_dbg(wpa_s, MSG_DEBUG, "Re-association to the same ESS");
wpa_s->reassoc_same_ess = 1;
if (wpa_s->current_bss && wpa_s->current_bss == bss) {
wmm_ac_save_tspecs(wpa_s);
wpa_s->reassoc_same_bss = 1;
} else if (wpa_s->current_bss && wpa_s->current_bss != bss) {
os_get_reltime(&wpa_s->roam_start);
}
} else {
#ifdef CONFIG_SAE
wpa_s_clear_sae_rejected(wpa_s);
wpa_s_setup_sae_pt(wpa_s->conf, ssid);
#endif /* CONFIG_SAE */
}
if (rand_style > 0 && !wpa_s->reassoc_same_ess) {
if (wpas_update_random_addr(wpa_s, rand_style) < 0)
return;
wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
} else if (rand_style == 0 && wpa_s->mac_addr_changed) {
if (wpa_drv_set_mac_addr(wpa_s, NULL) < 0) {
wpa_msg(wpa_s, MSG_INFO,
"Could not restore permanent MAC address");
return;
}
wpa_s->mac_addr_changed = 0;
if (wpa_supplicant_update_mac_addr(wpa_s) < 0) {
wpa_msg(wpa_s, MSG_INFO,
"Could not update MAC address information");
return;
}
wpa_msg(wpa_s, MSG_DEBUG, "Using permanent MAC address");
}
wpa_s->last_ssid = ssid;
#ifdef CONFIG_IBSS_RSN
ibss_rsn_deinit(wpa_s->ibss_rsn);
wpa_s->ibss_rsn = NULL;
#else /* CONFIG_IBSS_RSN */
if (ssid->mode == WPAS_MODE_IBSS &&
!(ssid->key_mgmt & (WPA_KEY_MGMT_NONE | WPA_KEY_MGMT_WPA_NONE))) {
wpa_msg(wpa_s, MSG_INFO,
"IBSS RSN not supported in the build");
return;
}
#endif /* CONFIG_IBSS_RSN */
if (ssid->mode == WPAS_MODE_AP || ssid->mode == WPAS_MODE_P2P_GO ||
ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) {
#ifdef CONFIG_AP
if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP)) {
wpa_msg(wpa_s, MSG_INFO, "Driver does not support AP "
"mode");
return;
}
if (wpa_supplicant_create_ap(wpa_s, ssid) < 0) {
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)
wpas_p2p_ap_setup_failed(wpa_s);
return;
}
wpa_s->current_bss = bss;
#else /* CONFIG_AP */
wpa_msg(wpa_s, MSG_ERROR, "AP mode support not included in "
"the build");
#endif /* CONFIG_AP */
return;
}
if (ssid->mode == WPAS_MODE_MESH) {
#ifdef CONFIG_MESH
if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_MESH)) {
wpa_msg(wpa_s, MSG_INFO,
"Driver does not support mesh mode");
return;
}
if (bss)
ssid->frequency = bss->freq;
if (wpa_supplicant_join_mesh(wpa_s, ssid) < 0) {
wpa_msg(wpa_s, MSG_ERROR, "Could not join mesh");
return;
}
wpa_s->current_bss = bss;
wpa_msg(wpa_s, MSG_INFO, MESH_GROUP_STARTED "ssid=\"%s\" id=%d",
wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
ssid->id);
wpas_notify_mesh_group_started(wpa_s, ssid);
#else /* CONFIG_MESH */
wpa_msg(wpa_s, MSG_ERROR,
"mesh mode support not included in the build");
#endif /* CONFIG_MESH */
return;
}
/*
* Set WPA state machine configuration to match the selected network now
* so that the information is available before wpas_start_assoc_cb()
* gets called. This is needed at least for RSN pre-authentication where
* candidate APs are added to a list based on scan result processing
* before completion of the first association.
*/
wpa_supplicant_rsn_supp_set_config(wpa_s, ssid);
#ifdef CONFIG_DPP
if (wpas_dpp_check_connect(wpa_s, ssid, bss) != 0)
return;
#endif /* CONFIG_DPP */
#ifdef CONFIG_TDLS
if (bss)
wpa_tdls_ap_ies(wpa_s->wpa, (const u8 *) (bss + 1),
bss->ie_len);
#endif /* CONFIG_TDLS */
#ifdef CONFIG_MBO
wpas_mbo_check_pmf(wpa_s, bss, ssid);
#endif /* CONFIG_MBO */
if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
ssid->mode == WPAS_MODE_INFRA) {
sme_authenticate(wpa_s, bss, ssid);
return;
}
if (wpa_s->connect_work) {
wpa_dbg(wpa_s, MSG_DEBUG, "Reject wpa_supplicant_associate() call since connect_work exist");
return;
}
if (radio_work_pending(wpa_s, "connect")) {
wpa_dbg(wpa_s, MSG_DEBUG, "Reject wpa_supplicant_associate() call since pending work exist");
return;
}
#ifdef CONFIG_SME
if (ssid->mode == WPAS_MODE_IBSS || ssid->mode == WPAS_MODE_MESH) {
/* Clear possibly set auth_alg, if any, from last attempt. */
wpa_s->sme.auth_alg = WPA_AUTH_ALG_OPEN;
}
#endif /* CONFIG_SME */
wpas_abort_ongoing_scan(wpa_s);
cwork = os_zalloc(sizeof(*cwork));
if (cwork == NULL)
return;
cwork->bss = bss;
cwork->ssid = ssid;
// 开启工作队列
if (radio_add_work(wpa_s, bss ? bss->freq : 0, "connect", 1,
wpas_start_assoc_cb, cwork) < 0) {
os_free(cwork);
}
}
接下来分析wpas_start_assoc_cb函数
static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
{
ret = wpa_drv_associate(wpa_s, ¶ms);
}
/*
1. struct wpa_driver_associate_params params;
2. wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING);
3. 填充 params
4. ret = wpa_drv_associate(wpa_s, ¶ms);
5. wpa_supplicant_req_auth_timeout(wpa_s, timeout, 0);
6. wpa_supplicant_initiate_eapol(wpa_s);
*/
//5.6步目前还不确定走不走,这个函数 囊括了 assoc、auth、EAPOL动作
接下来分析wpa_drv_associate函数
wpa_drv_associate
static inline int wpa_drv_associate(struct wpa_supplicant *wpa_s,
struct wpa_driver_associate_params *params)
{
if (wpa_s->driver->associate) {
// 实际就是调用wpa_driver_nl80211_ops 中的 wpa_driver_nl80211_associate
return wpa_s->driver->associate(wpa_s->drv_priv, params);
}
return -1;
}
接下来分析wpa_driver_nl80211_ops
wpa_driver_nl80211_associate
static int wpa_driver_nl80211_associate( void *priv, struct wpa_driver_associate_params *params) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; int ret = -1; struct nl_msg *msg; nl80211_unmask_11b_rates(bss); if (params->mode == IEEE80211_MODE_AP) return wpa_driver_nl80211_ap(drv, params); if (params->mode == IEEE80211_MODE_IBSS) return wpa_driver_nl80211_ibss(drv, params); if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) { enum nl80211_iftype nlmode = params->p2p ? NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION; if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0) return -1; if (params->key_mgmt_suite == WPA_KEY_MGMT_SAE || params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE) bss->use_nl_connect = 1; else bss->use_nl_connect = 0; // 连接 return wpa_driver_nl80211_connect(drv, params, get_connect_handle(bss)); } nl80211_mark_disconnected(drv); wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)", drv->ifindex); //先创建一个基础的 msg NL80211_CMD_ASSOCIATE msg = nl80211_drv_msg(drv, 0, NL80211_CMD_ASSOCIATE); if (!msg) return -1; //将 parmas 参数塞进 msg ret = nl80211_connect_common(drv, params, msg); if (ret) goto fail; if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED && nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED)) goto fail; if (params->fils_kek) { wpa_printf(MSG_DEBUG, " * FILS KEK (len=%u)", (unsigned int) params->fils_kek_len); if (nla_put(msg, NL80211_ATTR_FILS_KEK, params->fils_kek_len, params->fils_kek)) goto fail; } if (params->fils_nonces) { wpa_hexdump(MSG_DEBUG, " * FILS nonces (for AAD)", params->fils_nonces, params->fils_nonces_len); if (nla_put(msg, NL80211_ATTR_FILS_NONCES, params->fils_nonces_len, params->fils_nonces)) goto fail; } //将 msg 发出 /* - 放入 NL80211_CMD_ASSOCIATE - 把参数通过nl80211_connect_common函数在它里面put各种nl消息 - 调用send_and_recv_msgs_owner发送出去msg - */ ret = send_and_recv_msgs_owner(drv, msg, get_connect_handle(drv->first_bss), 1, NULL, NULL, NULL, NULL); msg = NULL; if (ret) { wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: MLME command failed (assoc): ret=%d (%s)", ret, strerror(-ret)); nl80211_dump_scan(drv); } else { wpa_printf(MSG_DEBUG, "nl80211: Association request send successfully"); } fail: nlmsg_free(msg); return ret; }
wpa_driver_nl80211_connect
static int wpa_driver_nl80211_connect( struct wpa_driver_nl80211_data *drv, struct wpa_driver_associate_params *params, struct nl_sock *nl_connect) { int ret; // 尝试连接 下面分析该函数 ret = wpa_driver_nl80211_try_connect(drv, params, nl_connect); if (ret == -EALREADY) { /* * cfg80211 does not currently accept new connections if * we are already connected. As a workaround, force * disconnection and try again. * 如果已经连接上,就断开,从新连接 */ if (wpa_driver_nl80211_disconnect( drv, WLAN_REASON_PREV_AUTH_NOT_VALID, nl_connect)) return -1; ret = wpa_driver_nl80211_try_connect(drv, params, nl_connect); } return ret; }
wpa_driver_nl80211_try_connect
static int wpa_driver_nl80211_try_connect( struct wpa_driver_nl80211_data *drv, struct wpa_driver_associate_params *params, struct nl_sock *nl_connect) { struct nl_msg *msg; enum nl80211_auth_type type; int ret; int algs; #ifdef CONFIG_DRIVER_NL80211_QCA if (params->req_key_mgmt_offload && params->psk && (params->key_mgmt_suite == WPA_KEY_MGMT_PSK || params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 || params->key_mgmt_suite == WPA_KEY_MGMT_FT_PSK)) { wpa_printf(MSG_DEBUG, "nl80211: Key management set PSK"); ret = issue_key_mgmt_set_key(drv, params->psk, 32); if (ret) return ret; } #endif /* CONFIG_DRIVER_NL80211_QCA */ wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex); //第一步:创建消息nl消息 命令问为 NL80211_CMD_CONNECT msg = nl80211_drv_msg(drv, 0, NL80211_CMD_CONNECT); if (!msg) return -1; //第二步:传入参数放入nl attr中 ret = nl80211_connect_common(drv, params, msg); //这中间删除掉一部分,具体看代码 ret = nl80211_set_conn_keys(params, msg); // 发送消息 ret = send_and_recv_msgs_owner(drv, msg, nl_connect, 1, NULL,(void *) -1, NULL, NULL); msg = NULL; fail: nl80211_nlmsg_clear(msg); nlmsg_free(msg); return ret; }
至此应用层完成工作,当驱动收到 NL80211_CMD_CONNECT 该消息后,会调用驱动的
nl80211_connect函数进行处理qca/src/linux-4.4/net/wireless/nl80211.c文件下nl80211_ops里面定义的nl80211_connect函数,static const struct genl_ops nl80211_ops[] = { { .cmd = NL80211_CMD_CONNECT, .doit = nl80211_connect, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, } }
下面就是驱动要干的事情了
驱动层我们这里不做分析,当驱动处理后,同样会发送NL80211_CMD_CONNECT到应用层wpa,wpa
process_global_event do_process_drv_event { case NL80211_CMD_CONNECT: case NL80211_CMD_ROAM: mlme_event_connect(drv, cmd,xx) { 处理驱动上传的事件 } }