WPA-PSK连接
从packages\apps\Settings\src\com\android\settings\wifi\WifiSettings.java 和 WifiDialog.java 开始
1.
如果你点中某个AP
=> onClick执行 (WifiSettings.java)
代码如下:
- button == WifiDialog.BUTTON_SUBMIT
- WifiConfiguration config = mDialog.getConfig();(WifiDialog.java中)
- WifiDialog getConfig =>( 因为才你自己选的某个AP 有网络ID,
- 所以network id !=-1,mSelected!=null)
- if (config.networkId!= -1) {
- if (mSelected!= null) {/* 走到这里 */
- /*下面函数 到2.分支继续分析 */
- mWifiManager.updateNetwork(config);
- /*下面函数 到4.分支继续分析 */
- saveNetworks();
- }
- } else{
- int networkId = mWifiManager.addNetwork(config);
- if (networkId!= -1) {
- mWifiManager.enableNetwork(networkId, false);
- config.networkId= networkId;
- if (mDialog.edit || requireKeyStore(config)){
- saveNetworks();
- } else {
- connect(networkId);
- }
- }
- }
2. 接上面分支1.的 mWifiManager.updateNetwork(config);
继续分析
=> addOrUpdateNetwork (wifiManager.java)
==> mService.addOrUpdateNetwork(config);
(wifi manager 对wifi service function 的访问)
===> mWifiStateTracker.addNetwork()
====> WifiNative.addNetworkCommand();
(上面这句执行完成到 setVariables: 部分 ,分支3.继续)
=====> JNI 执行 doIntCommand("ADD_NETWORK");
上面是andorid ,下面就执行 wpa_cli的命令
---------------------------------------------------------
=>
wpa_supplicant_ctrl_iface_add_network
==> wpa_config_add_network
===> wpa_config_update_prio_list
更新 wpa prioty list! 就是conf文件设置的priority 组
看下解释:
# priority: Priority group
# By default, all networks and credentials get the same priority group
# (0). This field can be used to give higher priority for credentials
# (and similarly in struct wpa_ssid for network blocks) to change the
# Interworking automatic networking selection behavior. The matching
# network (based on either an enabled network block or a credential)
# with the highest priority value will be selected.
==> wpas_notify_network_added(wpa_s,ssid); (dbus 部分,没有用)
==> wpa_config_set_network_defaults ,设置network(ssid) 如下default 值:
#define DEFAULT_EAP_WORKAROUND ((unsigned int) -1)
#define DEFAULT_EAPOL_FLAGS (EAPOL_FLAG_REQUIRE_KEY_UNICAST | \
EAPOL_FLAG_REQUIRE_KEY_BROADCAST)
#define DEFAULT_PROTO (WPA_PROTO_WPA | WPA_PROTO_RSN)
#define DEFAULT_KEY_MGMT (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X)
#define DEFAULT_PAIRWISE (WPA_CIPHER_CCMP | WPA_CIPHER_TKIP)
#define DEFAULT_GROUP (WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | \
WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40)
#define DEFAULT_FRAGMENT_SIZE 1398
就是加密算法,密钥管理,及EAPOL等等,struct wpa_ssid结构中注释很全
android 发起的一条ADD_NETWORK命令完成了!
3. 又回到java,刚才2.分支中mService.addOrUpdateNetwork部分
=> mService.addOrUpdateNetwork(config);
继续往下执行代码如下:
setVariables: { 设置网络变量如下
==> mWifiStateTracker.setNetworkVariable(netId,
WifiConfiguration.ssidVarName, config.SSID)
debugmessage: CTRL_IFACE: SET_NETWORK id=0 name='ssid'
===> WifiNative.setNetworkVariableCommand(netId, name, value);
(这中间还JNI部分,最后总是到 wpa_cli 执行set network命令)
====>到 wpa_supplicant wpa_supplicant_ctrl_iface_set_network
=====> wpa_config_set(ssid, name, value, 0) 更新ssid_fields
=====> wpa_config_update_psk 更新 wpa-psk的密码
/* 密码由SHA1 生成psk */
=====> wpa_config_update_prio_list 更新prioty
根据命令set network 命令参数内容:
<network id> <variable name> <value>
如果variable name 还会再设置prioty,
就是说,除了conf文件1开始设置好,后面根据命令也还可以变
你可以在java 中自己临时改变network 部分的 priority
wpa_cli完成后,下面又返回java
===> mWifiStateTracker.setNetworkVariable(netId,
WifiConfiguration.KeyMgmt.varName, allowedKeyManagementString)
debugmessage: CTRL_IFACE: SET_NETWORK id=0 name='key_mgmt'
一看就知道设置id=0 的netword 的key manager 变量
===> mWifiStateTracker.setNetworkVariable( netId,
WifiConfiguration.priorityVarName,
Integer.toString(config.priority)
会跑到刚才说的wpa_config_update_prio_list,
==> mWifiManager.enableNetwork(networkId, false);
==> mWifiStateTracker.enableNetwork(netId, false);
===> enableNetworkCommand(netId, false);
====> 到 wpa_cli 的 ENABLE_NETWORK 处理部分
wpa_supplicant_ctrl_iface_enable_network
(dbgmessage: CTRL_IFACE: ENABLE_NETWORK id=0)
=====> wpa_supplicant_enable_network(wpa_s, ssid);
第1次,if (wpa_s->current_ssid == NULL) 满足
1. wpa_supplicant_req_sched_scan
请求dirver 进行周期scan
(wpa_driver_nl80211_sched_scan NL80211_CMD_START_SCHED_SCAN)
scan paramater 比如ssids,filter_ssids
基本来自于将conf文件读到内存的wpa_s->conf
带着参数调用 wpa_supplicant_start_sched_scan
去执行nl80211 driver 的 sched_scan
2. wpa_supplicant_req_scan(wpa_s, 2, 0);
到eloop_register_timeout(sec, usec,
wpa_supplicant_scan, wpa_s, NULL);
2秒后
wpa_driver_nl80211_scan通过NL80211_CMD_TRIGGER_SCAN命令
以及给定参数触发1个新的scan
wpa_supplicant_trigger_scan
wpa_drv_scan 到nl80211 driver 的 scan2
kerneldoc :
http://linuxwireless.org/en/developers/Documentation/nl80211/kerneldoc
解释如下:
NL80211_CMD_START_SCHED_SCAN:
start a scheduled scan at certain intervals, as specified by NL80211_ATTR_SCHED_SCAN_INTERVAL. Like with normal scans,
if SSIDs (NL80211_ATTR_SCAN_SSIDS) are passed, they are used in the probe requests. For broadcast,
a broadcast SSID must be passed (ie. an empty string). If no SSID is passed,
no probe requests are sent and a passive scan is performed. NL80211_ATTR_SCAN_FREQUENCIES,
if passed, define which channels should be scanned; if not passed, all channels allowed for the current regulatory domain are used.
Extra IEs can also be passed from the userspace by using the NL80211_ATTR_IE attribute
上面的意思就是让STA driver开始1个在sched scan interval 时间间隔的scan,
如果有已经设置的网络,就发probe request,如果没有那么执行
被动扫描(接收AP的beacon), 并根据设置的通道梳洗来判断,scan 所有channel
还是某些
NL80211_CMD_TRIGGER_SCAN
trigger a new scan with the given parameters NL80211_ATTR_TX_NO_CCK_RATE is
used to decide whether to send the probe requests at CCK rate or not.
wpa_supplicant_req_sched_scan wpa_supplicant_req_scan 区别:
就是 sched_scan 和scan2的区别 请看代码中的注释
/**
* scan2 - Request the driver to initiate scan
* @priv: private driver interface data
* @params: Scan parameters
*
* Returns: 0 on success, -1 on failure
*
* Once the scan results are ready, the driver should report scan
* results event for wpa_supplicant which will eventually request the
* results with wpa_driver_get_scan_results2().
*/
/**
* sched_scan - Request the driver to initiate scheduled scan
* @priv: private driver interface data
* @params: Scan parameters
* @interval: interval between scan cycles
*
* Returns: 0 on success, -1 on failure
*
* This operation should be used for scheduled scan offload to
* the hardware. Every time scan results are available, the
* driver should report scan results event for wpa_supplicant
* which will eventually request the results with
* wpa_driver_get_scan_results2(). This operation is optional
* and if not provided or if it returns -1, we fall back to
* normal host-scheduled scans.
*/
sched scan 就是所启动一个定时扫描循环,然后scan2 触发一个scan,
这样driver 就在scan2 后按时间间隔循环scan
接下来到4.分支继续
到这里已经是连接上AP后的处理部分了
=> saveNetworks(); (wifisettings.java)
==> enableNetworks(); (loop mAccessPoints.getPreferenceCount )
==>
mWifiManager.saveConfiguration();
能看到如下message 表示在保存建立连接的network设置
Writing configuration file '/data/misc/wifi/wpa_supplicant.conf'
===>mService.saveConfiguration
====> mWifiStateTracker.saveConfig
=====> wpa_cli AP_SCAN 1 + wpa_cli SAVE_CONFIG
====> mWifiStateTracker.reloadConfig
------------------------------------------
wpa_cli RECONFIGURE 命令
=====> wpa_supplicant_reload_configuration(), message 如下:
- D/wpa_supplicant( 2129): Reading configuration file'/data/misc/wifi/wpa_supplicant.conf'
- D/wpa_supplicant( 2129): ctrl_interface='wlan0:0'
- wpa_config_read, wpa_config_process_global
- D/wpa_supplicant( 2129): update_config=1
- wpa_config_read, wpa_config_process_global
- D/wpa_supplicant( 2129): Line: 5- start of a new network block
- wpa_config_read ,wpa_config_read_network
- D/wpa_supplicant( 2129): key_mgmt: 0x2
- D/wpa_supplicant( 2129): priority=1(0x1)
- D/wpa_supplicant( 2129): Priority group 1
- wpa_config_read ,wpa_config_debug_dump_networks
- D/wpa_supplicant( 2129): id=0 ssid='RD-Test
wpa_config_read ,wpa_config_debug_dump_networks
=====> wpa_sm_set_config(wpa_s->wpa, NULL);
wpa 状态机 init,并将config 中相关内容设置到wpa状态机
=====> wpa_supplicant_update_config 的 message :
D/wpa_supplicant( 2129): WPS: Set UUID for interface wlan0
对应 wpa_supplicant_update_config ,wpas_wps_set_uuid
D/wpa_supplicant( 2129): wlan0: P2P: Intra BSS distribution enabled
V/WifiMonitor( 1363): Event [CTRL-EVENT-STATE-CHANGE id=-1 state=0 BSSID=00:00:00:00:00:00]
V/WifiStateTracker( 1363): Changing supplicant state: SCANNING ==> DISCONNECTED
D/wpa_supplicant( 2129): Setting scan request: 2 sec 0 usec
D/wpa_supplicant( 2129): Reconfiguration completed
=====> wpa_supplicant_clear_status !
----------------------------------------------
又回到java
====> boardcast NETWORK_IDS_CHANGED_ACTION
==> updateAccessPoints();
上面 mWifiManager.enableNetwork(networkId, false);
=> WifiNative.enableNetworkCommand 发 ENABLE_NETWORK命令
==> wpa_supplicant_ctrl_iface_enable_network
===> wpa_supplicant_enable_network
===> 继续scan
4. 接上面3.分支继续
这时你会看到driver的消息:
Association completed. (bss_info_changed)
字面上看关联成功? 还没发requet associate ?
那么在那里发的request associate ?
前面3.分支,开始我们SET_NETWORK,之后我们又ENABLE_NETWORK
最后发起对我们选择的AP,进行scan2,scan之后就需要等待接收结果
在前面wap_supplicant init 部分
曾经做过这个动作 wpa_driver_nl80211_init_nl
该函数最后调用:
eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle_event),
wpa_driver_nl80211_event_receive, drv,
drv->nl_handle_event);
前面解释过是用来接收scan result,mlme,regulator 相关的多播包
其实前面wifi eanalbe 时这部分应该也做,并且如果conf中有Network
它也会去pick network,然后开始连接过程,
现在你是第1次手动连接,同样也是从scan result 中
找到你选择的那个AP,进行连接,看下面:
=>wpa_driver_nl80211_event_receive
通过process_event callback 来继续处理 NL80211_CMD_NEW_SCAN_RESULTS 如下:
这时先会 eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout,
取消前面的10s scan timeout eloop
==> send_scan_event
===> wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event);
====> wpa_supplicant_event_scan_results
=====> _wpa_supplicant_event_scan_results
======>wpas_notify_scan_results(wpa_s);
=======> wpas_wps_notify_scan_results
"Event [WPS-AP-AVAILABLE]"
======> wpa_supplicant_pick_network
从scan results 中选择AP,
根据scan 到的new ap 去匹配 (有prori等)
其中执行 wpa_supplicant_select_bss
可以看到: Selecting BSS from priority group 1:
wpa_supplicant_pick_network最后返回一个
struct wpa_bss 结构的pointer,包含该AP的info
======> wpa_supplicant_rsn_preauth_scan_results
对scan results 启动预身份验证
注意 WPA 不支持预先身份验证,WPA2才支持
======> wpa_supplicant_connect 开始连接AP
终于要开始连接了,这是应该会看到下面的msg:
wlan0: Request association: reassociate: 1
selected: 00:1f:33:b9:5d:e0
bssid: 00:00:00:00:00:00
pending: 00:00:00:00:00:00
wpa_state: SCANNING
wpa_supplicant_connect 去调用
=======> wpa_supplicant_associate(wpa_s, selected, ssid);
========> sme_authenticate(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, struct wpa_ssid *ssid)
开始设置一堆参数为driver层auth 调用做准备
就提下params.auth_alg = WPA_AUTH_ALG_OPEN;
其他看代码
/* WPA 802.11 author 使用OPEN !!! */
=========>wpa_supplicant_set_suites
(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, struct wpa_ssid *ssid,
u8 *wpa_ie, size_t *wpa_ie_len)
它设置认证和加密的参数,这些参数从
这个AP scan result 发过的被处理后挂在
struct wpa_bss 的最后的IE部分获得
获得 WPA IE ,RSN IE 后就知道了,从message看:
WPA: Selected cipher suites: group WPA_CIPHER_TKIP,
pairwise WPA_CIPHER_CCMP,
key_mgmt WPA_KEY_MGMT_PSK
proto WPA_PROTO_RSN
表示:
group 使用TKIP, 成对密钥 使用CCMP, key manager wpa_psk,
协议为RSN ,就是强健安全网络(RSN)的标准
调用两次wpa_sm_set_param
设置proto,proto enanble 到 wpa state machine结构
struct wpa_sm 的,proto 和 rsn_enabled
还在wpa_supplicant_set_suites中,接下来通过
wpa_sm_set_assoc_wpa_ie_default去生成关联需要
的WPA/RSN IE,放到struct wpa_sm->assoc_wpa_ie
最后 wpa_sm_set_pmk 将预共享密钥PSK,copy到
struct wpa_sm->pmk,后面要用
=========> wpa_supplicant_cancel_sched_scan
wpa_supplicant_cancel_scan
开始认证时要取消sched scan ,并canle 当前scan
=========> wpa_msg(wpa_s, MSG_INFO,
"SME: Trying to authenticate with "
MACSTR (SSID='%s' freq=%d MHz)",
MAC2STR(params.bssid),
wpa_ssid_txt(params.ssid, params.ssid_len),
params.freq);
对应可以看到WifiMonitor msg:
Event [SME: Trying to authenticate with 00:1f:33:b9:5d:e0
(SSID='RD-Test' freq=2462 MHz)]
由wpa_supplicant_ctrl_iface_send发到
wifi monitor 被 parse 为 UNKNOWN,
monitor 不关心
=========> wpa_supplicant_set_state(wpa_s,
WPA_AUTHENTICATING);
=========> wpa_s->new_connection = 1;
=========> wpa_drv_set_operstate(wpa_s, 0);
/* 设置到IF_OPER_DORMANT */
=========> wpa_supplicant_stop_bgscan 停止back groud scan
=========> wpas_notify_state_changed
==========> wpa_msg_ctrl(wpa_s, MSG_INFO,
WPA_EVENT_STATE_CHANGE 给wifi monitor 发:
Event [SME: Trying to authenticate with 00:1f:33:b9:5d:e0 (SSID='RD-Test'
freq=2462 MHz)]
=========> wpa_supplicant_rsn_supp_set_config
注释如下:
Notify WPA state machine that configuration has changed
配置的网络上下文就是
struct wpa_ssid - Network configuration data
还有 PTK 生命周期
=========> wpa_supplicant_initiate_eapol
配置EAPOL 状态机器, 启动 EAPOL,EAP 状态机器
调用 eapol_sm_notify_eap_success
清除EAP success
调用 eapol_sm_notify_eap_fail
清除 EAP failure
调用eapol_sm_notify_portControl(Auto)
设置prot control 为自动,其他还有
ForceUnauthorized, ForceAuthorized
每个类似的eapol sm notify 函数中都
eapol_sm_step(sm); 这句很重要!
虽然简单,但却是状态机的驱动中枢!
=========>wpas_notify_network_changed
=========> wpa_drv_authenticate
前面一大堆,为的就是这句,这数执行nl80211 driver
authenticate,发出后等driver 响应
到分支5.继续
=========>eloop_register_timeout(callback=>sme_auth_timer)
认证超时失败处理
=========> eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
注释如下:
/*
* 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.
*/