一 wpa_supplicant 进程
我们首先来看看 wpa_supplicant 进程的初始化。
int main(int argc, char *argv[])
{
int c, i;
struct wpa_interface *ifaces, *iface;
int iface_count, exitcode = -1;
struct wpa_params params;
struct wpa_global *global;
//...
//处理进程启动参数
exitcode = 0;
global = wpa_supplicant_init(¶ms);
//创建了 wpa_global 对象,内部有一个 wpa_supplicant 队列。
//创建了进程的epoll 文件描述符
//...
for (i = 0; exitcode == 0 && i < iface_count; i++) {
struct wpa_supplicant *wpa_s;
if ((ifaces[i].confname == NULL &&
ifaces[i].ctrl_interface == NULL) ||
ifaces[i].ifname == NULL) {
if (iface_count == 1 && (params.ctrl_interface ||
params.dbus_ctrl_interface))
break;
usage();
exitcode = -1;
break;
}
wpa_s = wpa_supplicant_add_iface(global, &ifaces[i], NULL);
if (wpa_s == NULL) {
exitcode = -1;
break;
}
}
if (exitcode == 0)
//开始事件循环监听
exitcode = wpa_supplicant_run(global);
//...
return exitcode;
}
在 wpa_supplicant 进程的 main 函数中,初始化了 wpa_global 对象供整个进程使用。wpa_supplicant 进程主要是向上接受上层关于 WiFi 的请求,然后向下层内核驱动发送通信。所以这里通过 IO 多路复用来轮询监听相应的事件。
wpa_supplicant_add_iface ->
wpa_supplicant_init_iface ->
wpas_init_driver ->
wpa_supplicant_set_driver ->
//external/wpa_supplicant_8/src/drivers/drivers.c
const struct wpa_driver_ops *const wpa_drivers[] =
{
#ifdef CONFIG_DRIVER_NL80211
&wpa_driver_nl80211_ops,
#endif /* CONFIG_DRIVER_NL80211 */
#ifdef CONFIG_DRIVER_WEXT
&wpa_driver_wext_ops,
#endif /* CONFIG_DRIVER_WEXT */
#ifdef CONFIG_DRIVER_HOSTAP
&wpa_driver_hostap_ops,
#endif /* CONFIG_DRIVER_HOSTAP */
#ifdef CONFIG_DRIVER_BSD
&wpa_driver_bsd_ops,
#endif /* CONFIG_DRIVER_BSD */
#ifdef CONFIG_DRIVER_OPENBSD
&wpa_driver_openbsd_ops,
#endif /* CONFIG_DRIVER_OPENBSD */
#ifdef CONFIG_DRIVER_NDIS
&wpa_driver_ndis_ops,
#endif /* CONFIG_DRIVER_NDIS */
#ifdef CONFIG_DRIVER_WIRED
&wpa_driver_wired_ops,
#endif /* CONFIG_DRIVER_WIRED */
#ifdef CONFIG_DRIVER_MACSEC_LINUX
&wpa_driver_macsec_linux_ops,
#endif /* CONFIG_DRIVER_MACSEC_LINUX */
#ifdef CONFIG_DRIVER_MACSEC_QCA
&wpa_driver_macsec_qca_ops,
#endif /* CONFIG_DRIVER_MACSEC_QCA */
#ifdef CONFIG_DRIVER_ROBOSWITCH
&wpa_driver_roboswitch_ops,
#endif /* CONFIG_DRIVER_ROBOSWITCH */
#ifdef CONFIG_DRIVER_ATHEROS
&wpa_driver_atheros_ops,
#endif /* CONFIG_DRIVER_ATHEROS */
#ifdef CONFIG_DRIVER_NONE
&wpa_driver_none_ops,
#endif /* CONFIG_DRIVER_NONE */
NULL
};
wpa_global 中的 每一个 wpa_supplicant 对象都是一个 wpa_supplicant 接口,其中都配置了对应的驱动接口。可选驱动如上图所示
二 Framework->HAL
//external/wpa_supplicant_8/wap_supplicant/hidl/1.4/sta_iface.cpp
SupplicantStatus StaIface::reassociateInternal()
{
//从 wpa_global 中获取到 wpa_supplicant
struct wpa_supplicant *wpa_s = retrieveIfacePtr();
if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
return {SupplicantStatusCode::FAILURE_IFACE_DISABLED, ""};
}
wpas_request_connection(wpa_s);
return {SupplicantStatusCode::SUCCESS, ""};
}
通过 HIDL 调用到 HAL 层中。在 wpa_suplicant 中请求连接到指定网络。
//external/wpa_supplicant_8/wap_supplicant/wap_supplicant.c
void wpas_request_connection(struct wpa_supplicant *wpa_s)
{
//指定连接类型为 reassociate
wpa_s->normal_scans = 0;
wpa_s->scan_req = NORMAL_SCAN_REQ;
wpa_supplicant_reinit_autoscan(wpa_s);
wpa_s->disconnected = 0;
wpa_s->reassociate = 1;
wpa_s->last_owe_group = 0;
//尝试关联到特定 AP,失败则请求重新扫描
if (wpa_supplicant_fast_associate(wpa_s) != 1)
wpa_supplicant_req_scan(wpa_s, 0, 0);
else
wpa_s->reattach = 0;
}
//external/wpa_supplicant_8/wap_supplicant/event.c
int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s)
{
struct os_reltime now;
wpa_s->ignore_post_flush_scan_res = 0;
if (wpa_s->last_scan_res_used == 0)
return -1;
os_get_reltime(&now);
//当前时间和上一次扫描时间是否超过了指定的时间阈值scan_res_valid_for_connect
if (os_reltime_expired(&now, &wpa_s->last_scan,
wpa_s->conf->scan_res_valid_for_connect)) {
wpa_printf(MSG_DEBUG, "Fast associate: Old scan results");
return -1;
}
return wpas_select_network_from_last_scan(wpa_s, 0, 1);
}
//external/wpa_supplicant_8/wap_supplicant/events.c
static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s,
int new_scan, int own_request)
{
struct wpa_bss *selected;
struct wpa_ssid *ssid = NULL;
int time_to_reenable = wpas_reenabled_network_time(wpa_s);
//...
wpa_s->owe_transition_search = 0;
//从最新的扫描结果中选择一个网络,并更新selected和ssid
selected = wpa_supplicant_pick_network(wpa_s, &ssid);
if (selected) {
int skip;
skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid);
if (skip) {
if (new_scan)
wpa_supplicant_rsn_preauth_scan_results(wpa_s);
return 0;
}
wpa_s->suitable_network++;
//如果当前连接的SSID与选定的SSID不同
//并且WPA状态为WPA_AUTHENTICATING或更高级别
//设置wpa_s->own_disconnect_req标志为1,向当前网络发送断开请求
if (ssid != wpa_s->current_ssid &&
wpa_s->wpa_state >= WPA_AUTHENTICATING) {
wpa_s->own_disconnect_req = 1;
wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
}
if (wpa_supplicant_connect(wpa_s, selected, ssid) < 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "Connect failed");
return -1;
}
//...
return 1;
} else {
//...
//network not found
}
return 0;
}
- wpas_request_connection函数用于请求建立连接,它重置一些相关的标志和状态,设置关联和扫描请求,并调用wpa_supplicant_fast_associate()函数。
- wpa_supplicant_fast_associate函数检查上次扫描结果的有效性,如果结果过期或不存在,则返回-1。否则,它调用wpas_select_network_from_last_scan()函数选择一个网络进行关联。
- wpas_select_network_from_last_scan()函数从上次扫描结果中选择一个网络,并判断是否需要进行漫游。如果不需要漫游,则返回0;如果需要漫游,则调用wpa_supplicant_connect()函数与选定的网络进行连接。
//external/wpa_supplicant_8/wap_supplicant/events.c
int wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
struct wpa_bss *selected,
struct wpa_ssid *ssid)
{
//...
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)))) {
//...
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;
}
//external/wpa_supplicant_8/wap_supplicant/wap_supplicant.c
void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, struct wpa_ssid *ssid)
{
//...
//如果上一次连接的SSID与当前SSID相同,则标记进行ESS的重新关联
//如果当前连接的BSS与之前相同,还会标记进行相同BSS的重新关联
//如果当前连接的BSS与之前不同,记录漫游开始的时间。
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 {
}
//如果rand_style大于0且不是进行ESS的重新关联
//则使用wpas_update_random_addr函数根据给定的rand_style生成随机MAC地址
//并且刷新WPA状态机的PMKSA缓存。
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);
//如果rand_style等于0且之前发生了MAC地址的更改
//则使用wpas_restore_permanent_mac_addr函数恢复永久MAC地址。
} else if (rand_style == 0 && wpa_s->mac_addr_changed) {
if (wpas_restore_permanent_mac_addr(wpa_s) < 0)
return;
}
wpa_s->last_ssid = ssid;
//...
wpa_supplicant_rsn_supp_set_config(wpa_s, ssid);
//...
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);
}
}
- 重置相关标志和状态,准备进行新的关联过程。
- 生成随机的MAC地址或恢复永久MAC地址。
- 标记是否进行相同ESS或相同BSS的重新关联。
- 设置WPA状态机的配置。
- 终止正在进行的扫描操作。
- 创建连接工作对象,并将其添加到设备的工作队列中,以便在适当的时间执行关联操作。
//external/wpa_supplicant_8/wap_supplicant/wap_supplicant.c
static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
{
struct wpa_connect_work *cwork = work->ctx;
struct wpa_bss *bss = cwork->bss;
struct wpa_ssid *ssid = cwork->ssid;
struct wpa_supplicant *wpa_s = work->wpa_s;
u8 *wpa_ie;
const u8 *edmg_ie_oper;
int use_crypt, ret, bssid_changed;
unsigned int cipher_pairwise, cipher_group, cipher_group_mgmt;
struct wpa_driver_associate_params params;
int assoc_failed = 0;
struct wpa_ssid *old_ssid;
u8 prev_bssid[ETH_ALEN];
//...
wpa_s->connect_work = work;
//...
os_memcpy(prev_bssid, wpa_s->bssid, ETH_ALEN);
os_memset(¶ms, 0, sizeof(params));
wpa_s->reassociate = 0;
wpa_s->eap_expected_failure = 0;
//...
//根据当前的 BSS 和 SSID 信息,构建关联所需的 IEs(Information Elements)
wpa_ie = wpas_populate_assoc_ies(wpa_s, bss, ssid, ¶ms, NULL);
if (!wpa_ie) {
wpas_connect_work_done(wpa_s);
return;
}
//如果存在有效的 BSS,并且驱动程序允许选择 BSS(如非手动指定 BSSID 或正在进行 WPS 搜索),则尝试关联该 BSS;
//否则,尝试关联指定的 SSID。
if (bss &&
(!wpas_driver_bss_selection(wpa_s) || wpas_wps_searching(wpa_s))) {
wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR
" (SSID='%s' freq=%d MHz)", MAC2STR(bss->bssid),
wpa_ssid_txt(bss->ssid, bss->ssid_len), bss->freq);
bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
os_memset(wpa_s->bssid, 0, ETH_ALEN);
os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN);
if (bssid_changed)
wpas_notify_bssid_changed(wpa_s);
} else {
wpa_msg(wpa_s, MSG_INFO, "Trying to associate with SSID '%s'",
wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
if (bss)
os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN);
else
os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
}
//取消可能存在的扫描和计划扫描操作。清除之前的关联过程中的密钥。
if (!wpa_s->pno)
wpa_supplicant_cancel_sched_scan(wpa_s);
wpa_supplicant_cancel_scan(wpa_s);
wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL);
use_crypt = 1;
cipher_pairwise = wpa_s->pairwise_cipher;
cipher_group = wpa_s->group_cipher;
cipher_group_mgmt = wpa_s->mgmt_group_cipher;
if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE)
use_crypt = 0;
}
if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS)
use_crypt = 0;
//如果key管理方式是WPA_NONE,表示不使用任何加密,设置一个无加密的key
if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
/* Set the key before (and later after) association */
wpa_supplicant_set_wpa_none_key(wpa_s, ssid);
}
wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING);
//...
//配置params
ret = wpa_drv_associate(wpa_s, ¶ms);
os_free(wpa_ie);
//...
//设置相关密钥信息
if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set) {
wpa_s->current_bss = bss;
}
wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
wpa_supplicant_initiate_eapol(wpa_s);
if (old_ssid != wpa_s->current_ssid)
wpas_notify_network_changed(wpa_s);
}
- 准备关联所需的信息,例如关联帧需要携带的信息元素。
- 根据要关联的网络和BSS的情况,设置关联的目标BSSID和SSID。
- 取消已有的扫描操作,并清除相关的密钥信息。
- 根据网络的安全配置和认证方式,确定是否需要加密。
- 根据不同的认证方式,设置相关的密钥信息。
- 将驱动状态设置为关联中,调用驱动程序进行关联操作。
- 清理关联所用的临时变量和内存。
- 更新当前的BSS和SSID信息。
- 配置RSN(Robust Secure Network)相关参数。
- 初始化EAPOL(EAP over LAN)过程,以进行后续的认证和密钥协商。
- 如果当前网络与之前连接的网络不同,通知网络发生了变化。
//external/wpa_supplicant_8/wap_supplicant/driver_i.h
static inline int wpa_drv_associate(struct wpa_supplicant *wpa_s,
struct wpa_driver_associate_params *params)
{
if (wpa_s->driver->associate) {
return wpa_s->driver->associate(wpa_s->drv_priv, params);
}
return -1;
}
//external/wpa_supplicant_8/src/drivers/driver.h
struct wpa_driver_ops {
/**
* associate - Request driver to associate
* @priv: private driver interface data
* @params: association parameters
*
* Returns: 0 on success, -1 on failure
*/
int (*associate)(void *priv,
struct wpa_driver_associate_params *params);
/**
* roaming - Set roaming policy for driver-based BSS selection
* @priv: Private driver interface data
* @allowed: Whether roaming within ESS is allowed
* @bssid: Forced BSSID if roaming is disabled or %NULL if not set
* Returns: Length of written status information or -1 on failure
*
* This optional callback can be used to update roaming policy from the
* associate() command (bssid being set there indicates that the driver
* should not roam before getting this roaming() call to allow roaming.
* If the driver does not indicate WPA_DRIVER_FLAGS_BSS_SELECTION
* capability, roaming policy is handled within wpa_supplicant and there
* is no need to implement or react to this callback.
*/
int (*roaming)(void *priv, int allowed, const u8 *bssid);
};
wpa_driver_ops 中定义了一些驱动接口 API。在 wpa_s 初始化的过程中,配置了一些驱动程序,调用驱动中的 associate 方法。
假定这里的驱动应该是wpa_driver_nl80211_ops
//external/wpa_supplicant_8/src/drivers/driver_nl80211.c
const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.name = "nl80211",
.desc = "Linux nl80211/cfg80211",
//...
.associate = wpa_driver_nl80211_associate,
//...
#ifdef CONFIG_DRIVER_NL80211_QCA
.roaming = nl80211_roaming,
//...
#endif /* CONFIG_DRIVER_NL80211_QCA */
//...
};
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);
//如果驱动程序不支持SME特性则调用wpa_driver_nl80211_connect 执行关联操作
//并传递get_connect_handle(bss)的值作为参数。
//最终也是向 Netlink 发送消息。
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_drv_msg(drv, 0, NL80211_CMD_ASSOCIATE);
if (!msg)
return -1;
//处理关联过程的共同部分 msg 部分
//传入驱动程序数据结构、关联参数和Netlink消息作为参数
ret = nl80211_connect_common(drv, params, msg);
if (ret)
goto fail;
//...
//发送Netlink消息,并等待关联过程完成。
//传入驱动程序数据结构、消息、关联操作的句柄、以及一些回调函数指针作为参数。
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;
}
这段代码根据驱动程序的支持情况和关联参数的配置,执行Wi-Fi网络的关联过程。它包括了根据不同的情况调用不同的处理函数,配置相应参数,构建即将发送的 msg,向Netlink 发送关联请求、处理关联结果等步骤,并提供了相应的错误处理和调试输出。
成功调用 wpa_supplicant_associate 后,将等待 NL80211_CMD_CONNECT 命令的处理结果。该结果由 wlan driver通过 NL80211_CMD_CONNECT 类型的消息返回给 wpa_supplicant driver。
static void do_process_drv_event(struct i802_bss *bss, int cmd,
struct nlattr **tb)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
int external_scan_event = 0;
struct nlattr *frame = tb[NL80211_ATTR_FRAME];
if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED &&
(cmd == NL80211_CMD_NEW_SCAN_RESULTS ||
cmd == NL80211_CMD_SCAN_ABORTED))
nl80211_restore_ap_mode(bss);
switch (cmd) {
//...
case NL80211_CMD_CONNECT:
case NL80211_CMD_ROAM:
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;
//...
default:
wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event "
"(cmd=%d)", cmd);
break;
}
}