环境:
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,