判定当前wifi驱动是否走external_auth流程,主要是看wpa_s->drv_flags 里WPA_DRIVER_FLAGS_SME比特位是否置1。置1,会在wpa_supplicant_associate内中间一步调用sme_authenticate后直接return。未置1则在wpa_supplicant_associate函数里继续走下去。
external auth的流程始于wpa_supplicant_associate里最后一段:
if (radio_add_work(wpa_s, bss ? bss->freq : 0, "connect", 1,
wpas_start_assoc_cb, cwork) < 0) {
os_free(cwork);
}
这段在wpa_s的链表wpa_s->radio上添加了一个radio_work,这个作业的处理函数是wpas_start_assoc_cb。当执行到这个radio_work的时候,wpas_start_assoc_cb会被调到。
wpas_start_assoc_cb内部设置各类参数,例如key_mgmt,he_caps,vht_caps等等。
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;
... ... ... ...//创建n多下文要用的变量
... ... ... ...//一些异常判断
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;
if (bss &&
(!wpas_driver_bss_selection(wpa_s) || wpas_wps_searching(wpa_s))) {//判断当前的bssid是否可连接,一般走这个if条件
#ifdef CONFIG_IEEE80211R
const u8 *ie, *md = NULL;
#endif /* CONFIG_IEEE80211R */
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);
#ifdef CONFIG_IEEE80211R
ie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN);
if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN)
md = ie + 2;
wpa_sm_set_ft_params(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0);
if (md) {
/* Prepare for the next transition */
wpa_ft_prepare_auth_request(wpa_s->wpa, ie);
}
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_WPS
} else if ((ssid->ssid == NULL || ssid->ssid_len == 0) &&
wpa_s->conf->ap_scan == 2 &&
(ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {//异常情况
/* Use ap_scan==1 style network selection to find the network*/
wpas_connect_work_done(wpa_s);
wpa_s->scan_req = MANUAL_SCAN_REQ;
wpa_s->reassociate = 1;
wpa_supplicant_req_scan(wpa_s, 0, 0);
return;
#endif /* CONFIG_WPS */
} else {//其他情况
wpa_msg(wpa_s, MSG_INFO, "Trying to associate with SSID '%s'",
wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
... ... ... ...//一些处理
}
if (!wpa_s->pno)
wpa_supplicant_cancel_sched_scan(wpa_s);
wpa_supplicant_cancel_scan(wpa_s);//确认可以开始连接了就不需要再扫描了
/* Starting new association, so clear the possibly used WPA IE from the
* previous association. */
wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
wpa_ie = wpas_populate_assoc_ies(wpa_s, bss, ssid, ¶ms, NULL);//调的这个函数里会组成关联过程要用到的ie
if (!wpa_ie) {
wpas_connect_work_done(wpa_s);
return;
}
... ... ... ...//配置参数,如key_mgmt
wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING);//将wpa_supplicant状态改为关联中
... ... ... ...//配置参数,如bss相关,key_mgmt_suite等等
ret = wpa_drv_associate(wpa_s, ¶ms);//真正开始进行关联过程
os_free(wpa_ie);
if (ret < 0) {//异常处理
wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
"failed");
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SANE_ERROR_CODES) {
/*
* The driver is known to mean what is saying, so we
* can stop right here; the association will not
* succeed.
*/
wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
return;
}
/* try to continue anyway; new association will be tried again
* after timeout */
assoc_failed = 1;
}
... ... ... ...//多种情况判断处理
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);
}
wpas_start_assoc_cb里会调用wpa_drv_associate。
wpa_drv_associate -> wpa_driver_nl80211_associate -> wpa_driver_nl80211_connect -> wpa_driver_nl80211_try_connect
wpa_driver_nl80211_try_connect里,部分代码如下:
wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex);
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_CONNECT);//先创建NL80211_CMD_CONNECT的msg
if (!msg)
return -1;
ret = nl80211_connect_common(drv, params, msg);//根据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;//利用nla_put系列函数往msg中填参
... ... ... ...//继续向msg中填参
if (nl_connect)//最终将msg发给net层
ret = send_and_recv(drv->global, nl_connect, msg, NULL, (void *) -1);
else
ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1);
NL80211_CMD_CONNECT最终会调用驱动注册的cfg80211_connect处理函数。在驱动接下来的内部处理流程里,需要通过cfg80211_external_auth_request通知上层开启external auth流程。cfg80211_external_auth_request里创建事件NL80211_CMD_EXTERNAL_AUTH向上发送,process_bss_event会收到并调用nl80211_external_auth处理本事件。
static void nl80211_external_auth(struct wpa_driver_nl80211_data *drv,
struct nlattr **tb)
{
union wpa_event_data event;
enum nl80211_external_auth_action act;
if (!tb[NL80211_ATTR_AKM_SUITES] || !tb[NL80211_ATTR_EXTERNAL_AUTH_ACTION] ||
!tb[NL80211_ATTR_BSSID] || !tb[NL80211_ATTR_SSID])
return;
os_memset(&event, 0, sizeof(event));
act = nla_get_u32(tb[NL80211_ATTR_EXTERNAL_AUTH_ACTION]);
switch (act) {//判断是刚开始的external auth事件还是异常结束的external auth事件
case NL80211_EXTERNAL_AUTH_START: //正常进入external auth流程
event.external_auth.action = EXT_AUTH_START;
break;
case NL80211_EXTERNAL_AUTH_ABORT:
event.external_auth.action = EXT_AUTH_ABORT;
break;
default:
return;
}
event.external_auth.key_mgmt_suite = nla_get_u32(tb[NL80211_ATTR_AKM_SUITES]);//通过nla_get等函数从tb中获取信息存进event里
event.external_auth.ssid_len = nla_len(tb[NL80211_ATTR_SSID]);
if (event.external_auth.ssid_len > SSID_MAX_LEN)
return;
event.external_auth.ssid = nla_data(tb[NL80211_ATTR_SSID]);
event.external_auth.bssid = nla_data(tb[NL80211_ATTR_BSSID]);
wpa_printf(MSG_DEBUG, "nl80211: External auth action: %u, AKM: 0x%x", event.external_auth.action, event.external_auth.key_mgmt_suite);
wpa_supplicant_event(drv->ctx, EVENT_EXTERNAL_AUTH, &event);//将event传给wpa_supplicant的状态机里
}
wpa_supplicant_event收到EVENT_EXTERNAL_AUTH事件,会调用sme_external_auth_trigger处理。
void sme_external_auth_trigger(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
{
if (RSN_SELECTOR_GET(&data->external_auth.key_mgmt_suite) != RSN_AUTH_KEY_MGMT_SAE)
return;
if (data->external_auth.action == EXT_AUTH_START) {
if (!data->external_auth.bssid || !data->external_auth.ssid)
return;
os_memcpy(wpa_s->sme.ext_auth_bssid, data->external_auth.bssid, ETH_ALEN);
os_memcpy(wpa_s->sme.ext_auth_ssid, data->external_auth.ssid, data->external_auth.ssid_len);
wpa_s->sme.ext_auth_ssid_len = data->external_auth.ssid_len;
wpa_s->sme.seq_num = 0;
wpa_s->sme.sae.state = SAE_NOTHING;
wpa_s->sme.sae.send_confirm = 0;
wpa_s->sme.sae_group_index = 0;//初始化好wpa_s的sme结构
if (sme_handle_external_auth_start(wpa_s, data) < 0)//调用sme_handle_external_auth_start开始external_auth
sme_send_external_auth_status(wpa_s, WLAN_STATUS_UNSPECIFIED_FAILURE);
} else if (data->external_auth.action == EXT_AUTH_ABORT) {//异常情况需改变状态通知其他模块
/* Report failure to driver for the wrong trigger */
sme_send_external_auth_status(wpa_s, WLAN_STATUS_UNSPECIFIED_FAILURE);
}
}
sme_handle_external_auth_start里检查待连接wifi状态是否正常,若正常则调用sme_external_auth_send_sae_commit。
static int sme_external_auth_send_sae_commit(struct wpa_supplicant *wpa_s,
const u8 *bssid,
struct wpa_ssid *ssid)
{
struct wpabuf *resp, *buf;
resp = sme_auth_build_sae_commit(wpa_s, ssid, bssid, 1, 0);//创建sae-auth的第一帧--sta向ap发的commit
if (!resp) {
wpa_printf(MSG_DEBUG, "SAE: Failed to build SAE commit");
return -1;
}
wpa_s->sme.sae.state = SAE_COMMITTED;
buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + wpabuf_len(resp));
if (!buf) {
wpabuf_free(resp);
return -1;
}
wpa_s->sme.seq_num++;
sme_external_auth_build_buf(buf, resp, wpa_s->own_addr, bssid, 1, wpa_s->sme.seq_num);
wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0);//发给下层这个组好的auth包
wpabuf_free(resp);
wpabuf_free(buf);
return 0;
}
这之后,驱动注册的cfg80211_mgmt_tx处理函数会被调用,发出这第一个auth包。驱动收到AP回复的sae-auth 第二帧commit包后,需调接口函数上传给上层。
我了解的是可以通过函数cfg80211_rx_mgmt上传。cfg80211_rx_mgmt里会调用nl80211_send_mgmt,nl80211_send_mgmt中hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
,以NL80211_CMD_FRAME事件类型上传。
process_bss_event会收到NL80211_CMD_FRAME事件,并调用mlme_event处理本事件。
mlme_event中:
case NL80211_CMD_FRAME://对于NL80211_CMD_FRAME会调mlme_event_mgmt
mlme_event_mgmt(bss, freq, sig, nla_data(frame), nla_len(frame));
break;
mlme_event_mgmt代码如下:
static void mlme_event_mgmt(struct i802_bss *bss,
struct nlattr *freq, struct nlattr *sig,
const u8 *frame, size_t len)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
const struct ieee80211_mgmt *mgmt;
union wpa_event_data event;
u16 fc, stype;
int ssi_signal = 0;
int rx_freq = 0;
wpa_printf(MSG_MSGDUMP, "nl80211: Frame event");
mgmt = (const struct ieee80211_mgmt *) frame;
if (len < 24) {
wpa_printf(MSG_DEBUG, "nl80211: Too short management frame");
return;
}
fc = le_to_host16(mgmt->frame_control);
stype = WLAN_FC_GET_STYPE(fc);
if (sig)
ssi_signal = (s32) nla_get_u32(sig);
os_memset(&event, 0, sizeof(event));//创建wpa_supplicant中的event
if (freq) {//填充event结构中的值
event.rx_mgmt.freq = nla_get_u32(freq);
rx_freq = drv->last_mgmt_freq = event.rx_mgmt.freq;
}
wpa_printf(MSG_DEBUG, "nl80211: RX frame da=" MACSTR " sa=" MACSTR " bssid=" MACSTR
" freq=%d ssi_signal=%d fc=0x%x seq_ctrl=0x%x stype=%u (%s) len=%u",
MAC2STR(mgmt->da), MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid), rx_freq, ssi_signal, fc,
le_to_host16(mgmt->seq_ctrl), stype, fc2str(fc), (unsigned int) len);
event.rx_mgmt.frame = frame;//给event变量赋值
event.rx_mgmt.frame_len = len;
event.rx_mgmt.ssi_signal = ssi_signal;
event.rx_mgmt.drv_priv = bss;
wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);//传给wpa_s的事件状态机
}
wpa_supplicant_event收到事件EVENT_RX_MGMT后,会调用sme_external_auth_mgmt_rx函数处理。
case EVENT_RX_MGMT: {
u16 fc, stype;
const struct ieee80211_mgmt *mgmt;
#ifdef CONFIG_TESTING_OPTIONS
... ... ... ...//CONFIG_TESTING_OPTIONS的一些操作
#endif /* CONFIG_TESTING_OPTIONS */
mgmt = (const struct ieee80211_mgmt *) data->rx_mgmt.frame;
fc = le_to_host16(mgmt->frame_control);
stype = WLAN_FC_GET_STYPE(fc);
#ifdef CONFIG_AP
if (wpa_s->ap_iface == NULL) {
#endif /* CONFIG_AP */
#ifdef CONFIG_P2P
... ... ... ...//CONFIG_P2P的一些操作
#endif /* CONFIG_P2P */
#ifdef CONFIG_IBSS_RSN
... ... ... ...//CONFIG_IBSS_RSN的一些操作
#endif /* CONFIG_IBSS_RSN */
if (stype == WLAN_FC_STYPE_ACTION) {
wpas_event_rx_mgmt_action( wpa_s, data->rx_mgmt.frame, data->rx_mgmt.frame_len, data->rx_mgmt.freq, data->rx_mgmt.ssi_signal);
break;
}
if (wpa_s->ifmsh) {
mesh_mpm_mgmt_rx(wpa_s, &data->rx_mgmt);
break;
}
#ifdef CONFIG_SAE
if (stype == WLAN_FC_STYPE_AUTH && !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE)) {//如果subtype是AUTH并且支持SAE不支持SME
sme_external_auth_mgmt_rx( wpa_s, data->rx_mgmt.frame, data->rx_mgmt.frame_len);//调用sme_external_auth_mgmt_rx处理这个auth包
break;
}
#endif /* CONFIG_SAE */
wpa_dbg(wpa_s, MSG_DEBUG, "AP: ignore received management frame in non-AP mode");
break;
#ifdef CONFIG_AP
}
... ... ... ...//CONFIG_AP的一些处理
ap_mgmt_rx(wpa_s, &data->rx_mgmt);
#endif /* CONFIG_AP */
break;
}
void sme_external_auth_mgmt_rx(struct wpa_supplicant *wpa_s,
const u8 *auth_frame, size_t len)
{
const struct ieee80211_mgmt *header;
size_t auth_length;
header = (const struct ieee80211_mgmt *) auth_frame;
auth_length = IEEE80211_HDRLEN + sizeof(header->u.auth);
if (len < auth_length) {
/* Notify failure to the driver */
sme_send_external_auth_status(wpa_s, WLAN_STATUS_UNSPECIFIED_FAILURE);
return;
}
if (le_to_host16(header->u.auth.auth_alg) == WLAN_AUTH_SAE) {
int res;//调sme_sae_auth
res = sme_sae_auth( wpa_s, le_to_host16(header->u.auth.auth_transaction), le_to_host16(header->u.auth.status_code), header->u.auth.variable, len - auth_length, 1, header->sa);
if (res < 0) {
/* Notify failure to the driver */
sme_send_external_auth_status( wpa_s, WLAN_STATUS_UNSPECIFIED_FAILURE);
return;
}
if (res != 1)//如果是confirm阶段成功,返回的是1;commit阶段成功,返回的是0
return;//若commit阶段成功,直接return
wpa_printf(MSG_DEBUG, "SME: SAE completed - setting PMK for 4-way handshake");//confirm阶段成功设置pmk
wpa_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, PMK_LEN, wpa_s->sme.sae.pmkid, wpa_s->pending_bssid);
}
}
这是external auth流程里第一次进入sme_sae_auth里,auth_transaction传进来的值为1,external传进来的值也为1。
static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
u16 status_code, const u8 *data, size_t len, int external, const u8 *sa)
{
int *groups;
wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE authentication transaction %u "
"status code %u", auth_transaction, status_code);
... ... ... ...
if (auth_transaction == 1) {//auth_transaction值为1或2,代表sae的auth握手阶段
u16 res;//commit阶段
groups = wpa_s->conf->sae_groups;
wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE commit");
... ... ... ...//处理一些参数
wpabuf_free(wpa_s->sme.sae_token);
wpa_s->sme.sae_token = NULL;
if (!external)
sme_send_authentication(wpa_s, wpa_s->current_bss, wpa_s->current_ssid, 0);
else//使用external auth,本文走这条路径
sme_external_auth_send_sae_confirm(wpa_s, sa);
return 0;//commit阶段返回0
} else if (auth_transaction == 2) {//confirm阶段
wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE confirm");
if (wpa_s->sme.sae.state != SAE_CONFIRMED)
return -1;
if (sae_check_confirm(&wpa_s->sme.sae, data, len) < 0)
return -1;
wpa_s->sme.sae.state = SAE_ACCEPTED;
sae_clear_temp_data(&wpa_s->sme.sae);
if (external) {//如果当前使用external auth
/* Report success to driver */
sme_send_external_auth_status(wpa_s, WLAN_STATUS_SUCCESS);
}
return 1;//confirm阶段返回1
}
return -1;
}
static void sme_external_auth_send_sae_confirm(struct wpa_supplicant *wpa_s,
const u8 *da)
{
struct wpabuf *resp, *buf;
resp = sme_auth_build_sae_confirm(wpa_s, 1);//创建sae-auth的第三帧--sta向ap发的confirm
if (!resp) {
wpa_printf(MSG_DEBUG, "SAE: Confirm message buf alloc failure");
return;
}
wpa_s->sme.sae.state = SAE_CONFIRMED;
buf = wpabuf_alloc(4 + SAE_CONFIRM_MAX_LEN + wpabuf_len(resp));
if (!buf) {
wpa_printf(MSG_DEBUG, "SAE: Auth Confirm buf alloc failure");
wpabuf_free(resp);
return;
}
wpa_s->sme.seq_num++;
sme_external_auth_build_buf(buf, resp, wpa_s->own_addr, da, 2, wpa_s->sme.seq_num);
wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0);//向下层发这个组好的sae-auth confirm包
wpabuf_free(resp);
wpabuf_free(buf);
}
接下来是相同的流程,驱动通过注册的cfg80211_mgmt_tx处理函数,发出这第三步confirm-auth包。驱动收到AP回复的第四个confirm-auth包后,调cfg80211_rx_mgmt函数上传给上层。之后同样的,这个ap发来的auth会以NL80211_CMD_FRAME事件类型上传。
到wpa_supplicant里的函数过程都一样,直到在sme_sae_auth里,这次是external auth流程中第二次进入sme_sae_auth,auth_transaction传进来的值为2,external传进来的值为1。这一次,sme_sae_auth里将进入另一个if条件,检查完状态后,返回1到sme_external_auth_mgmt_rx里,正式结束auth阶段,开始设置PMK,准备进入associate阶段。
至此,WPAv2.9的SAE之支持external auth的auth四次握手阶段结束,进入associate流程。