環境:
android 2.3.4
wpa_supplicant 0.8linux
一切從 main.c 開始: (=>表示第1級, 那麼==>表示下一級,類推)android
os_program_init => 參數解析& 獲取
=> wpa_supplicant_init(¶ms)
==> eap_register_methods /* 註冊EAP method ,好比EAP-PSK, EAP-TLS */
==> global->params獲取, 好比daemonize-是否在後臺運行wpa,
ctrl_interface-wpa_cli 命名socket
==> eloop_init , static struct eloop_data eloop; 結構清0
==> wpa_supplicant_global_ctrl_iface_init
(init.rc wpa socket file regist to eloop table)
在init.rc 中wpa service 啓動命令中聲明的socket以下:
service wpa_supplicant /system/bin/wpa_supplicant
socket wpa_wlan0:0
經過 android_get_control_socket 函數(android 特有的)和socket名
得到socket 句柄,放入struct wpa_global 的 ctrl_iface 變量中
==> wpas_notify_supplicant_initialized
其中進行 dbus init 但CONFIG_CTRL_IFACE_DBUS 沒有設置! 什麼也不作
==> wpa_drivers[i]->global_init();
目前wpa_drivers 包含wext,和nl80211 其中nl80211
是nl80211_global_init 分配生成
struct nl80211_global { struct dl_list interfaces; };
接口雙向list init ,而後給出pointer
==> 最後返回前面已經init 的struct wpa_global *global安全
=> wpa_supplicant_add_iface (如其名,添加接口,就是wlan0)
==> struct wpa_supplicant wpa_s = wpa_supplicant_alloc();
給struct wpa_supplicant *wpa_s 分配內存,並對下面這些變量init
/* scan_req =1,表示手動scan ,即便沒有網絡在conf文件中配置
除了0, 還能夠設置爲2,好象設置2時不作關聯請求(associate req)*/
wpa_s->scan_req = 1;
/* time in sec between scans to find suitable AP
可見這個參數決定了ap 是否在線的判斷,可是
要省電時能夠設置了長點*/
wpa_s->scan_interval = 5;
/* driver 參數,可見 kerneldoc 解釋以下:
http://linuxwireless.org/en/developers/Documentation/nl80211/kerneldoc
NL80211_ATTR_SCHED_SCAN_INTERVAL
Interval between scheduled scan cycles, in msecs.
這個參數看來必須小於上面的 scan_interval*/
wpa_s->sched_scan_interval = 3;
wpa_s->new_connection = 1;
wpa_s->parent = wpa_s;
wpa_s->sched_scanning = 0;
wpa_s->override_sched_scan = 0;網絡
scan_interval 就是設置driver 調度scan 的間隔時間
scan_req 就是wpa 定時去讀driver scan 到的結果
可是eloop 中scan 多播的事件,這個又如何解釋???app
==> wpa_supplicant_init_iface
===> struct wpa_config * wpa_config_read(wpa_s->confname);
讀/data/misc/wifi/wpa_supplicant.conf 並解析到wpa_config 結構
config.h 有default 設置,某些能夠經過wpa_cli 設置
所有讀到 struct wpa_supplicant結構的wpa_s->conf
(命令行-c /data/misc/wifi/wpa_supplicant.conf)
===> eapol_sm_notify_portEnabled, eapol_sm_notify_portValid
init eapol state machine ,可是目前sm==NULL,什麼也沒作less
===> wpa_supplicant_set_driver (肯定 wpa_s->driver = wpa_drivers[i];)
根據driver name = 'nl80211' 查到 wpa_drivers中的某1個作下面動做:
wpa_s->driver = wpa_drivers[i]; /* 指向某個struct wpa_driver_ops */
/*drv_priv 是前面wpa_drivers[i]->global_init()時得到的
struct nl80211_global {struct dl_list interfaces;};
之後driver init 時將init 好的driver data掛到該雙向list 上
可參考 wpa_drv_init wpa_driver_nl80211_init,
其實不用之後,下面立刻開始wpa_drv_init
*/
wpa_s->global_drv_priv = wpa_s->global->drv_priv[i];socket
===> wpa_drv_init ====> init2 (wpa_driver_nl80211_init)
分配內存給struct wpa_driver_nl80211_data * drv
而後對各結構成員init :
drv->global = global_priv; 還記得前面那個dl list 掛接口用的
有了這個參數傳入,driver 就能夠訪問該list ,作接口init 後掛錶動做了
drv->ctx = ctx; /* ctx 就是struct wpa_supplicant *wpa_s
這樣driver opt function 中也能訪問到 此結構*/
====> wpa_driver_nl80211_init_nl
這部分主要是生成兩個結構指針:
struct nl_handle * nl_hanlde nl_handle和nl_handle_event
nl_handle用於向nl80211 netlink 寫,
nl_handle_event表示接收,scan,mlme,regulatory等多播包,並把
nl_handle_event 註冊到eloop reads table
====> /sys/class/net/wlan0/phy80211/name 去讀drv->phyname
====> drv->ioctl_sock 建立用於 訪問網絡接口kernel ioctl socket
看下下面這段註釋:(好比up,down 接口使用該socket 去作)
/*The usual method in Unix to set and get parameters from a network device is through ioctl.
Ioctl are usually operations performed on a file descriptor, but they also apply on network sockets.
The ioctl is a kernel system call. The arguments of the ioctl define the operations to be done,
the parameters of these operations and the device they applies to.
*/
====> drv->netlink = netlink_init(cfg)
進行driver data部分的 netlink config 設置
netlink_init 註冊NETLINK_ROUTE socket 到
eloop reads table,其中handle cb func 爲 netlink_receive
netlink->sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
建立用於NETLINK_ROUTE socket
netlink目前使用最普遍的是經過NETLINK_ROUTE 這個選項來獲取
網絡設備或者網址的一些信息,好比得到接口狀態: IFF_RUNNINGide
接收newlink, dellink 兩個event ,而後使用cfg
設置的 callback 函數:
* wpa_driver_nl80211_event_rtm_newlink :
若是是if disable 到 enable 就執行wpa_supplicant_event
(drv->ctx, EVENT_INTERFACE_ENABLED, NULL);
該事件發生,引起 wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
還有就是請求 scan (wpa_supplicant_req_scan), 要等待iface up時
* wpa_driver_nl80211_event_rtm_dellink,
處理iface down 的event,並更新wpa_s 的狀態
====> rfkill_init /dev/rfkill 不存在,什麼也沒作
====> wpa_driver_nl80211_finish_drv_init
wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION)
經過 NL80211_CMD_SET_INTERFACE 設置compat driver 接口爲station,
若是wlan0 沒有up 就linux_set_iface_flags 讓它up)
netlink_send_oper_ifla 設置接口鏈接mode=1, operstate =5
(operstate 參考 RFC 2863 operational status)
能夠經過/sys/class/net/wlan0(也與多是其餘名字)/operstate 查看
operstate的可能取值以下:
IF_OPER_UNKNOWN,
IF_OPER_NOTPRESENT,
IF_OPER_DOWN,
IF_OPER_LOWERLAYERDOWN,
IF_OPER_TESTING,
IF_OPER_DORMANT, =5 表示休眠的操做狀態
IF_OPER_UP,
對應到RFC2863兼容狀態的策略link_mode
IF_LINK_MODE_DEFAULT,
IF_LINK_MODE_DORMANT, =1 對應上面的5 -> IF_OPER_DORMANT函數
wpa_driver_nl80211_capa :
設置 key manager 能力,好比有WAP2,WPA_PSK,WAP2_PSK
設置 加密方法 : WEP40, TKIP,CCMP
設置 設置802.11 認證方式,好比open,(注意不是802.1X認證)
接下來經過 wpa_driver_nl80211_get_info 獲取driver的
max_scan_ssids, max_sched_scan_ssids
sched_scan_supported等等 ,
還有其餘WPA_DRIVER_FLAGS_SME, WPA_DRIVER_FLAGS_P2P_CAPABLE
經過SIOCGIFHWADDR 得到 mac addr (記住在這裏得到mac地址)
因此你能夠不改driver ,在這裏修改mac addr
nl80211_register_action_frame :
NL80211_CMD_REGISTER_ACTION
NL80211_CMD_REGISTER_FRAME 看下面註釋就知道該函數作了什麼
Register for receiving certain mgmt frames (via NL80211_CMD_FRAME) for processing in userspace.
This command requires an interface index, a frame type attribute (optional for backward compatibility reasons,
if not given assumes action frames) and a match attribute containing
the first few bytes of the frame that should match, e.g.
a single byte for only a category match or four bytes for vendor frames including the OUI.
The registration cannot be dropped, but is removed automatically when the netlink socket is closed. Multiple registrations can be made.oop
註冊user層接收的一些管理幀,註冊後由user 層處理
主要有P2P 的 Generic Advertisement Services frame , P2P Action
802.11W 部分相關的 SA Query Response
FT Action frames ,Fast BSS Transition 相關(802.11r),用於VOIP的多點
AP ,快速漫遊,小於50ms VOIP設備不會掉線
====> driver init 完成了,經過 struct wpa_driver_nl80211_data 中的
成員list,將driver data 掛到前面所講的global->drv_priv
在wpa_supplicant_set_driver 函數中
最後返回1個struct i802_bss *bss 主要包含struct wpa_driver_nl80211_data
===> wpa_drv_set_param -> nl80211_set_param
能夠經過命令行-p ,或者conf文件來設置
Driver interface parameters:
This field can be used to configure arbitrary driver interace parameters. The
format is specific to the selected driver interface. This field is not used
in most cases. driver_param="field=value"
若是p2p 支持 就處理 use_p2p_group_interface=1,
其餘狀況什麼也不作,代碼註釋以下:
/*When this is added(use_p2p_group_interface=1), start the supplicant normally on wlan0 like above.
Then, when P2P negotiation finishes, it will create a new interface for the group (called "p2p-wlan0-0")
and put it into the appropriate mode (GO or P2P client). */
===> wpa_supplicant_init_wpa
設置struct wpa_sm_ctx 相關function (處理wpa狀態機變化的func !!!)
====>wpa_sm_init(ctx); wpa state machine 初始化
=====> pmksa_cache_init ( 專門有show PMKSA cache的命令 ???)
下面聊下PMKSA
The Pairwise Master Key Security Association (PMKSA)
成對主蜜鑰安全關聯
在802.11i 中成對主蜜鑰是工做站和AP 成功認證產生的結果.
PMK的生命週期和惟一標識被稱爲PMKID。 這些信息的集合被稱爲成對主蜜鑰安全關聯
工做站判斷是不是1個有效的目標AP的PMK,經過檢查是否PMKSA 和目標AP 的mac 地址匹配,若是這樣的PMK不存在,就和AP 使用EAP 進行認證
若是存在這樣1個目標AP的PMK,那麼工做站就試圖使用PMK,經過將PMKID放入到關聯請求消息的RSN IE中,當AP接收到關聯請求中包含PMKID,AP就檢查是不是1個有效的PMKSA,其中有
相同的PMKID,若是有,開始的4次握手就使用已經協商的PMKSA.
根據上面解釋下面這3個pmksa_cache_init參數,應就能夠理解了
sm->dot11RSNAConfigPMKLifetime = 43200; /*生命週期*/
/* 超過生命週期的%70,須要進行re auth */
sm->dot11RSNAConfigPMKReauthThreshold = 70;
/*Security association timeout
就是進行上面說的安全關聯的創建應該在60s內完成*/
sm->dot11RSNAConfigSATimeout = 60;
===> wpa_sm_set_ifname, wpa_sm_set_fast_reauth
===> wpa_sm_set_param
1.RSNA_PMK_LIFETIME 2. RSNA_PMK_REAUTH_THRESHOLD 3. RSNA_SA_TIMEOUT
這3個參數前面已經解釋過了,一樣設置和上面wpa_drv_set_param
不一樣處是如今只是將這些參數保存到wpa_sm 結構中
===> wpa_drv_get_capa 取 driver 的 capability 而後設置到
struct wpa_supplicant 的 max_scan_ssids 等member
===> wpa_supplicant_driver_init
====> l2_packet_init (ifname,macaddr,ETH_P_PAE,
wpa_supplicant_rx_eapol(rx cb)
直接看代碼:
/* create l2 socket fd!!!
l2 就是鏈路層,因此上面init 傳了mac 地址
*/
l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM,htons(protocol));
ll.sll_family = PF_PACKET;
ll.sll_ifindex = ifr.ifr_ifindex;
ll.sll_protocol = htons(protocol);
/* l2 socket addr !!! */
if (bind(l2->fd, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
/* 註冊eloop 讀入 l2 packet 事件,callback l2_packet_receive!!! */
eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL);
====> wpa_clear_keys
=====> wpa_drv_set_key
======> wpa_driver_nl80211_set_key
清除key key-index 0-3 ??? why 4key
====> wpa_drv_set_countermeasures /* TKIP countermeasures*/
====> wpa_drv_flush_pmkid -> nl80211 driver 沒有該接口???
====> wpa_supplicant_delayed_sched_scan 對conf 文件中的network設置
進行掃描調度,若是不支持(sched_scan_supported==0)
100ms 後wpa_supplicant_req_scan,若是conf沒有 network ,
那麼須要設置 inactive state以下:
====> /* Inactive state (wpa_supplicant disabled)*/
wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
/* 上面函數中 後臺scan 進行的判斷標準是:4-Way Handshake
結束後就是wpa_complete ,這以前中止後臺scan !!! */
=====> netlink_send_oper_ifla (??????)
=====> wpas_notify_state_changed
===> wpa_drv_set_country(wpa_s, wpa_s->conf->country)
====>wpa_driver_nl80211_set_country
if (wpa_s->conf->country[0] && wpa_s->conf->country[1]
/* 上面這句的意思 country已經經過CRDA得到了才設置 */
===> wpa_sm_set_own_addr
===> wpa_supplicant_init_eapol(struct wpa_supplicant *wps)
和wpa_supplicant_init_wpa 相似
設置struct eapol_ctx 相關function (處理eapol 狀態機變化的func !!!)
====> eapol_sm_init Initialize EAPOL state machine
(RFC4137,)
====> eap_peer_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx, &conf);
=====> tls_init (tls openssl 的init)
====> eloop_register_timeout(1, 0, eapol_port_timers_tick
/* PAE 端口認證明體,EAP 端口超時處理 */, NULL, sm);
===> wpa_sm_set_eapol (/* wpa-sm 與 eapol -sm 關聯起來*/)
===> wpa_s->ctrl_iface = wpa_supplicant_ctrl_iface_init(wpa_s);
(wpa_cli)
1. 根據conf ctrl_iface=wlan0:0設置 在/data/misc/wifi/wlan0:0
(android init.rc 中已經作了)
2. eloop_register_read_sock(priv->sock,
wpa_supplicant_ctrl_iface_receive, wpa_s, priv);
3. wpa_msg_register_cb (wpa_supplicant_ctrl_iface_msg_cb);
/* 註冊msg debug call back func*/
由 wpa_msg_ctrl 回調 wpa_supplicant_ctrl_iface_msg_cb
-> wpa_supplicant_ctrl_iface_send
( Send a control interface packet to monitors)
發給V/WifiMonitor( 1172): Event [CTRL-EVENT-BSS-ADDED 3 04:21:b0:e0:20:20]
sendmsg(priv->sock, &msg, 0) ,priv 就是init.rc 中
priv->sock = android_get_control_socket(addr.sun_path);
socket wpa_wlan0:0 dgram 660 wifi wifi
===> wpas_p2p_init (struct wpa_global * (wpa_s->global),
struct wpa_supplicant * wpa_s)
p2p 相關,之後再看,已經不行了
===> wpa_bss_init
eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0,
wpa_bss_timeout, wpa_s, NULL);
關於上面看下面這段話,可能和bss(網絡或AP)的過時與按期更新有關
Currently, this is very simple: check every WPA_BSS_EXPIRATION_PERIOD
(10 sec by default) whether there are entries that are over
WPA_BSS_EXPIRATION_AGE (180 seconds) old and expire them if they are ot
in use (BSS entry age is the time from the last update in it, e.g.,
based on scan results). The other rule for expiring entries is based on
new scan results: expire an entry if it has not been seen in last
WPA_BSS_EXPIRATION_SCAN_COUNT (2). The latter one will eventually be
improved to handle partial scans (i.e., only some channels/SSIDs being
scanned; those should only expire matching BSS entries). In addition,
there is a maximum limit on the BSS entries (200) and new BSS entries
added above that will end up getting the oldest entry getting removed.
These values will likely end up being configurable at some point and I
have also considered providing options for controlling the BSS list
updating "mode". For example, wpa_supplicant could be requested to keep
the BSS table more frequently up-to-date for all BSSes or for specific
ESSes. This could have some connections with the bgscan mechanism. In
addition, this could get more input from things like Microsoft Wireless
Provisioning Services (multi-SSID/hidden SSID) and IEEE 802.11k neighbor
reports, etc.
==> wpas_notify_iface_added /* 不支持dbus,什麼也不作*/
==> 讀init.rc 設置supplicant_scan_interval
wifi無線局域網掃描間隔時間,單位爲秒。調大這個值可節約耗電。
若是不設置,就用 wpa_supplicant_alloc 設置的5 秒 (wpa_s->scan_interval = 5;)
==> wpas_notify_network_addedstruct wpa_supplicant *wpa_s,struct wpa_ssid *ssid)
把conf 文件中的 network {中的
=> wpa_supplicant_run -> eloop_run(); 開始epool 大循環 ,進行event 處理(callback)
下面簡但列下 eloop_run 相關handle
eloop_register_read_sock , eloop_register_timeout
1. nl_handle_event :
註冊nl_handle_event 的sock 到eloop reads table 而nl_handle_event
加入多播scan ,mlme,regulator組
eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle_event),
wpa_driver_nl80211_event_receive, drv, drv->nl_handle_event);
和compat driver 通信 接收scan,mlme,regulator 多播
2.netlink->sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
netlink目前使用最普遍的是經過NETLINK_ROUTE 這個選項來獲取網絡設備或者網址的一些信息
註冊到NETLINK_ROUTE socket 到eloop reads table
eloop_register_read_sock(netlink->sock, netlink_receive, netlink, NULL);
3. l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM, htons(protocol)); /* l2 socket !!! */
eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL); eloop 讀l2 packet 進入 !!!
4. 每隔1s 執行eapol_port_timers_tick
/ * This statemachine is implemented as a function that will be called
* once a second as a registered event loop timeout.
*/
eloop_register_timeout(1, 0, eapol_port_timers_tick/*PAE 端口認證明體,EAP 端口超時*/, NULL, sm);
5. scan 結果,過時處理?
eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0, wpa_bss_timeout, wpa_s, NULL);
6.wpa ctrl 命令處理:
eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive, wpa_s, priv);
7. scan timeout when driver
eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout,drv, drv->ctx);
wpa_driver_nl80211_scan_timeout:
/* This function can be used as registered timeout when starting a scan to
* generate a scan completed event if the driver does not report this.
*/
8. P2P: 主要有下面這些
P2p.c (src\p2p): eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0,
P2p.c (src\p2p): eloop_register_timeout(sec, usec, p2p_state_timeout, p2p, NULL);
P2p.c (src\p2p): eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,
P2p.c (src\p2p): eloop_register_timeout(timeout, 0, p2p_find_timeout,
P2p.c (src\p2p): eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,
P2p.c (src\p2p): eloop_register_timeout(0, 0, p2p_go_neg_start, p2p, NULL);
P2p.c (src\p2p): eloop_register_timeout(0, 0, p2p_invite_start, p2p, NULL);
P2p.c (src\p2p): eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0,
P2p.c (src\p2p): eloop_register_timeout(p2p->ext_listen_interval_sec,
P2p.c (src\p2p): eloop_register_timeout(p2p->ext_listen_interval_sec,
P2p_supplicant.c: eloop_register_timeout(0, 0, wpas_send_action_cb, wpa_s, NULL);
P2p_supplicant.c: eloop_register_timeout(15 + res->peer_config_timeout / 100,
P2p_supplicant.c: eloop_register_timeout(1, 0, wpas_p2p_join_scan, wpa_s, NULL);
P2p_supplicant.c: eloop_register_timeout(1, 0, wpas_p2p_join_scan, wpa_s, NULL);
P2p_supplicant.c: eloop_register_timeout(60, 0, wpas_p2p_group_formation_timeout,
P2p_supplicant.c: eloop_register_timeout(timeout, 0,
P2p_supplicant.c: eloop_register_timeout(wpa_s->conf->p2p_group_idle, 0,