wpa_supplicant_init代码如下所示。
**wpa_supplicant.c::wpa_supplicant_init**
~~~
struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
{
struct wpa_global *global;
int ret, i;
......
#ifdef CONFIG_DRIVER_NDIS
......// windows driver支持
#endif
#ifndef CONFIG_NO_WPA_MSG
// 设置全局回调函数,详情见下文解释
wpa_msg_register_ifname_cb(wpa_supplicant_msg_ifname_cb);
#endif /* CONFIG_NO_WPA_MSG */
// 输出日志文件设置,本例未设置该文件
wpa_debug_open_file(params->wpa_debug_file_path);
......
ret = eap_register_methods();// ①注册EAP方法
......
global = os_zalloc(sizeof(*global)); // 创建一个wpa_global对象
...... // 初始化global中的其他参数
wpa_printf(MSG_DEBUG, "wpa_supplicant v" VERSION_STR);
// ②初始化事件循环机制
if (eloop_init()) {......}
// 初始化随机数相关资源,用于提升后续随机数生成的随机性
// 这部分内容不是本书的重点,感兴趣的读者请自行研究
random_init(params->entropy_file);
// 初始化全局控制接口对象。由于本例中未设置全局控制接口,故该函数的处理非常简单,请读者自行阅读该函数
global->ctrl_iface = wpa_supplicant_global_ctrl_iface_init(global);
......
// 初始化通知机制相关资源,它和dbus有关。本例没有包括dbus相关内容,略
if (wpas_notify_supplicant_initialized(global)) {......}
// ③wpa_driver是一个全局变量,其作用见下文解释
for (i = 0; wpa_drivers[i]; i++)
global->drv_count++;
......
// 分配全局driver wrapper上下文信息数组
global->drv_priv = os_zalloc(global->drv_count * sizeof(void *));
......
return global;
}
~~~
wpa_supplicant_init函数的主要功能是初始化wpa_global以及一些与整个程序相关的资源,包括随机数资源、eloop事件循环机制以及设置消息全局回调函数。
此处先简单介绍消息全局回调函数,一共有两个。
* **wpa_msg_get_ifname_func**:有些输出信息中需要打印出网卡接口名。该回调函数用于获取网卡接口名。
* **wpa_msg_cb_func**:除了打印输出信息外,还可通过该回调函数进行一些特殊处理,如把输出信息发送给客户端进行处理。
上述两个回调函数相关的代码如下所示。
**wpa_debug.c**
~~~
// wpa_msg_ifname_cb用于获取无线网卡接口名
// WPAS为其设置的实现函数为wpa_supplicant_msg_ifname_cb
// 读者可自行阅读此函数
static wpa_msg_get_ifname_func wpa_msg_ifname_cb = NULL;
void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func){
wpa_msg_ifname_cb = func;
}
// WPAS中,wpa_msg_cb的实现函数是wpa_supplicant_ctrl_iface_msg_cb,它将输出信息发送给客户端
// 图4-2最后两行的信息就是由此函数发送给客户端的。而且前面的"<3>"也是由它添加的
static wpa_msg_cb_func wpa_msg_cb = NULL;
void wpa_msg_register_cb(wpa_msg_cb_func func){
wpa_msg_cb = func;
}
~~~
现在来看wpa_supplicant_init中列出的三个关键点,首先是eap_register_method函数。
1. eap_register_methods函数
该函数本身非常简单,它主要根据编译时的配置项来初始化不同的eap方法。其代码如下所示。
**eap_register.c::eap_register_methods**
~~~
int eap_register_methods(void)
{
int ret = 0;
#ifdef EAP_MD5 // 作为supplicant端,编译时将定义EAP_MD5
if (ret == 0)
ret = eap_peer_md5_register();
#endif /* EAP_MD5 */
......
#ifdef EAP_SERVER_MD5 // 作为Authenticator端,编译时将定义EAP_SERVER_MD5
if (ret == 0)
ret = eap_server_md5_register();
#endif /* EAP_SERVER_MD5 */
......
return ret;
}
~~~
如上述代码所示,eap_register_methods函数将根据编译配置项来注册所需的eap method。例如,MD5身份验证方法对应的注册函数是eap_peer_md5_register,该函数内部将填充一个名为eap_method的数据结构,其定义如图4-8所示。
图4-8所示的struct eap_method结构体声明于eap_i.h中,其内部一些变量及函数指针的定义和RFC4137有较大关系。此处,我们暂时列出其中一些简单的成员变量。4.4节将详细介绍RFC4137相关的知识。
来看第二个关键函数eloop_init,它和图4-1所示WPAS软件架构中的event loop模块有关。
2. eloop_init函数及event loop模块
eloop_init函数本身特别简单,它仅初始化了WPAS中事件驱动的核心数据结构体eloop_data。WPAS事件驱动机制的实现非常简单,它就是利用epoll(如果编译时设置了CONFIG_ELOOP_POLL选项)或select实现了I/O复用。
**提醒** select(或epoll)是I/O复用的重要函数,属于基础知识范畴。请不熟悉的读者自行学习相关内容。
从事件角度来看,WPAS的事件驱动机制支持5种类型的event。
* read event:读事件,例如来自socket的可读事件。
* write event:写事件,例如socket的可写事件。
* exception event:异常事件,如果socket操作发生错误,则由错误事件处理。
* timeout event:定时事件,通过select的等待超时机制来实现定时事件。
* signal:信号事件,信号事件来源于Kernel。WPAS允许为一些特定信号设置处理函数。
以上这些事件相关的信息都保存在eloop_data结构体中,如图4-9所示。
:-: ![](https://box.kancloud.cn/104717da8e12a7982fe107be7c532729_991x526.jpg)
图4-8 eap_method数据结构
:-: ![](https://box.kancloud.cn/9c60fec0a04f1c1418fd3c3e9e700b11_741x363.jpg)
图4-9 eloop_data结构体
简单介绍一下eloop提供的事件注册API及eloop事件循环核心处理函数eloop_run。首先是事件注册API函数,相关代码如下所示。
**eloop.h**
~~~
// 注册socket读事件处理函数,参数sock代表一个socket句柄。一旦该句柄上有读事件发生,则handler函数
// 将被事件处理循环(见下文eloop_run函数)调用
int eloop_register_read_sock(int sock, eloop_sock_handler handler,
void *eloop_data, void *user_data);
// 注册socket事件处理函数,具体是哪种事件(只能是读、写或异常)由type参数决定
int eloop_register_sock(int sock, eloop_event_type type,
eloop_sock_handler handler,void *eloop_data, void *user_data);
// 注册超时事件处理函数
int eloop_register_timeout(unsigned int secs, unsigned int usecs,
eloop_timeout_handler handler, void *eloop_data, void *user_data);
// 注册信号事件处理函数,具体要处理的信号由sig参数指定
int eloop_register_signal(int sig, eloop_signal_handler handler, void *user_data);
~~~
最后,向读者展示一下WPAS事件驱动机制的运行原理,其代码在eloop_run函数中,如下所示。
**eloop.c::eloop_run**
~~~
void eloop_run(void)
{
fd_set *rfds, *wfds, *efds; // fd_set是select中用到的一种参数类型
struct timeval _tv;
int res;
struct os_time tv, now;
// 事件驱动循环
while (!eloop.terminate &&
(!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 ||
eloop.writers.count > 0 || eloop.exceptions.count > 0)) {
struct eloop_timeout *timeout;
// 判断是否有超时事件需要等待
timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,list);
if (timeout) {
os_get_time(&now);
if (os_time_before(&now, &timeout->time))
os_time_sub(&timeout->time, &now, &tv);
else
tv.sec = tv.usec = 0;
_tv.tv_sec = tv.sec;
_tv.tv_usec = tv.usec;
}
// 将外界设置的读事件添加到对应的fd_set中
eloop_sock_table_set_fds(&eloop.readers, rfds);
......// 设置写、异常事件到fd_set中
// 调用select函数
res = select(eloop.max_sock + 1, rfds, wfds, efds,timeout ? &_tv : NULL);
if(res < 0) {
......// 错误处理
}
// 先处理信号事件
eloop_process_pending_signals();
// 判断是否有超时事件发生
timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,list);
if (timeout) {
os_get_time(&now);
if (!os_time_before(&now, &timeout->time)) {
void *eloop_data = timeout->eloop_data;
void *user_data = timeout->user_data;
eloop_timeout_handler handler = timeout->handler;
eloop_remove_timeout(timeout); // 注意,超时事件只执行一次
handler(eloop_data, user_data); // 处理超时事件
}
}
......// 处理读/写/异常事件。方法和下面这个函数类似
eloop_sock_table_dispatch(&eloop.readers, rfds);
......// 处理wfds和efds
}
out:
return;
}
~~~
eloop_run中的while循环是WPAS进程的运行中枢。不过其难度也不大。
下面来看wpa_supplicant_init代码中的第三个关键点,即wpa_drivers变量。
3. wpa_drivers数组和driver i/f模块
wpa_drivers是一个全局数组变量,它通过extern方式声明于main.c中,其定义却在drivers.c中,如下所示。
**drivers.c::wpa_drivers定义**
~~~
struct wpa_driver_ops *wpa_drivers[] =
{
#ifdef CONFIG_DRIVER_WEXT
&wpa_driver_wext_ops,
#endif /* CONFIG_DRIVER_WEXT */
#ifdef CONFIG_DRIVER_NL80211
&wpa_driver_nl80211_ops,
#endif /* CONFIG_DRIVER_NL80211 */
......// 其他driver接口
}
~~~
wpa_drivers数组成员指向一个wpa_driver_ops类型的对象。wpa_driver_ops是driver i/f模块的核心数据结构,其内部定义了很多函数指针。而正是通过定义函数指针的方法,WPAS能够隔离上层使用者和具体的driver。
**注意** 此处的driver并非通常意义所指的那些运行于Kernel层的驱动。读者可认为它们是Kernel层wlan驱动在用户空间的代理模块。上层使用者通过它们来和Kernel层的驱动交互。为了避免混淆,本书后续将用driver wrapper一词来表示WPAS中的driver。而driver一词将专指Kernel里对应的wlan驱动。
**另外**,wpa_drivers数组包含多少个driver wrapper对象也由编译选项来控制(如代码中所示的CONFIG_DRIVER_WEXT宏,它们可在android.cfg中被修改)。
此处先列出wpa_driver_nl80211_ops的定义。
**driver_nl80211.c::wpa_driver_nl80211_ops**
~~~
const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.name = "nl80211", // driver wrapper的名称
.desc = "Linux nl80211/cfg80211", // 描述信息
.get_bssid = wpa_driver_nl80211_get_bssid, // 用于获取bssid
......
.scan2 = wpa_driver_nl80211_scan, // 扫描函数
......
.get_scan_results2 = wpa_driver_nl80211_get_scan_results,
// 获取扫描结果
......
.disassociate = wpa_driver_nl80211_disassociate, // 触发disassociation操作
.authenticate = wpa_driver_nl80211_authenticate, // 触发authentication操作
.associate = wpa_driver_nl80211_associate, // 触发association操作
// driver wrapper全局初始化函数,该函数的返回值保存在wpa_global成员变量drv_pri数组中
.global_init = nl80211_global_init,
......
.init2 = wpa_driver_nl80211_init, // driver wrapper初始化函数
......
#ifdef ANDROID // Android平台定义了该宏
.driver_cmd = wpa_driver_nl80211_driver_cmd,// 该函数用于处理和具体驱动相关的命令
#endif
};
~~~
本节介绍了main函数中第一个的关键点wpa_supplicant_init,其中涉及的知识有:几个重要数据结构,如wpa_global、wpa_interface、eap_method、wpa_driver_ops等;event loop的工作原理;消息全局回调函数和wpa_drivers等内容。
下面来分析main中第二个关键函数wpa_supplicant_add_iface。
- 前言
- 第1章 准备工作
- 1.1 Android系统架构
- 1.2 工具使用
- 1.2.1 Source Insight的使用
- 1.2.2 Eclipse的使用
- 1.2.3 BusyBox的使用
- 1.3 本书资源下载说明
- 第2章 深入理解Netd
- 2.1 概述
- 2.2 Netd工作流程
- 2.2.1 main函数分析
- 2.2.2 NetlinkManager分析
- 2.2.3 CommandListener分析
- 2.2.4 DnsProxyListener分析
- 2.2.5 MDnsSdListener分析
- 2.3 CommandListener中的命令
- 2.3.1 iptables、tc和ip命令
- 2.3.2 CommandListener构造函数和测试工具ndc
- 2.3.3 InterfaceCmd命令
- 2.3.4 IpFwd和FirewallCmd命令
- 2.3.5 ListTtysCmd和PppdCmd命令
- 2.3.6 BandwidthControlCmd和IdletimerControlCmd命令
- 2.3.7 NatCmd命令
- 2.3.8 TetherCmd和SoftapCmd命令
- 2.3.9 ResolverCmd命令
- 2.4 NetworkManagementService介绍
- 2.4.1 create函数详解
- 2.4.2 systemReady函数详解
- 2.5 本章总结和参考资料说明
- 2.5.1 本章总结
- 2.5.2 参考资料说明
- 第3章 Wi-Fi基础知识
- 3.1 概述
- 3.2 无线电频谱和802.11协议的发展历程
- 3.2.1 无线电频谱知识
- 3.2.2 IEEE 802.11发展历程
- 3.3 802.11无线网络技术
- 3.3.1 OSI基本参考模型及相关基本概念
- 3.3.2 802.11知识点导读
- 3.3.3 802.11组件
- 3.3.4 802.11 Service介绍
- 3.3.5 802.11 MAC服务和帧
- 3.3.6 802.11 MAC管理实体
- 3.3.7 无线网络安全技术知识点
- 3.4 Linux Wi-Fi编程API介绍
- 3.4.1 Linux Wireless Extensions介绍
- 3.4.2 nl80211介绍
- 3.5 本章总结和参考资料说明
- 3.5.1 本章总结
- 3.5.2 参考资料说明
- 第4章 深入理解wpa_supplicant
- 4.1 概述
- 4.2 初识wpa_supplicant
- 4.2.1 wpa_supplicant架构
- 4.2.2 wpa_supplicant编译配置
- 4.2.3 wpa_supplicant命令和控制API
- 4.2.4 git的使用
- 4.3 wpa_supplicant初始化流程
- 4.3.1 main函数分析
- 4.3.2 wpa_supplicant_init函数分析
- 4.3.3 wpa_supplicant_add_iface函数分析
- 4.3.4 wpa_supplicant_init_iface函数分析
- 4.4 EAP和EAPOL模块
- 4.4.1 EAP模块分析
- 4.4.2 EAPOL模块分析
- 4.5 wpa_supplicant连接无线网络分析
- 4.5.1 ADD_NETWORK命令处理
- 4.5.2 SET_NETWORK命令处理
- 4.5.3 ENABLE_NETWORK命令处理
- 4.6 本章总结和参考资料说明
- 4.6.1 本章总结
- 4.6.2 参考资料说明
- 第5章 深入理解WifiService
- 5.1 概述
- 5.2 WifiService的创建及初始化
- 5.2.1 HSM和AsyncChannel介绍
- 5.2.2 WifiService构造函数分析
- 5.2.3 WifiStateMachine介绍
- 5.3 加入无线网络分析
- 5.3.1 Settings操作Wi-Fi分析
- 5.3.2 WifiService操作Wi-Fi分析
- 5.4 WifiWatchdogStateMachine介绍
- 5.5 Captive Portal Check介绍
- 5.6 本章总结和参考资料说明
- 5.6.1 本章总结
- 5.6.2 参考资料说明
- 第6章 深入理解Wi-Fi Simple Configuration
- 6.1 概述
- 6.2 WSC基础知识
- 6.2.1 WSC应用场景
- 6.2.2 WSC核心组件及接口
- 6.3 Registration Protocol详解
- 6.3.1 WSC IE和Attribute介绍
- 6.3.2 802.11管理帧WSC IE设置
- 6.3.3 EAP-WSC介绍
- 6.4 WSC代码分析
- 6.4.1 Settings中的WSC处理
- 6.4.2 WifiStateMachine的处理
- 6.4.3 wpa_supplicant中的WSC处理
- 6.4.4 EAP-WSC处理流程分析
- 6.5 本章总结和参考资料说明
- 6.5.1 本章总结
- 6.5.2 参考资料说明
- 第7章 深入理解Wi-Fi P2P
- 7.1 概述
- 7.2 P2P基础知识
- 7.2.1 P2P架构
- 7.2.2 P2P Discovery技术
- 7.2.3 P2P工作流程
- 7.3 WifiP2pSettings和WifiP2pService介绍
- 7.3.1 WifiP2pSettings工作流程
- 7.3.2 WifiP2pService工作流程
- 7.4 wpa_supplicant中的P2P
- 7.4.1 P2P模块初始化
- 7.4.2 P2P Device Discovery流程分析
- 7.4.3 Provision Discovery流程分析
- 7.4.4 GO Negotiation流程分析
- 7.5 本章总结和参考资料说明
- 7.5.1 本章总结
- 7.5.2 参考资料说明
- 第8章 深入理解NFC
- 8.1 概述
- 8.2 NFC基础知识
- 8.2.1 NFC概述
- 8.2.2 NFC R/W运行模式
- 8.2.3 NFC P2P运行模式
- 8.2.4 NFC CE运行模式
- 8.2.5 NCI原理
- 8.2.6 NFC相关规范
- 8.3 Android中的NFC
- 8.3.1 NFC应用示例
- 8.3.2 NFC系统模块
- 8.4 NFC HAL层讨论
- 8.5 本章总结和参考资料说明
- 8.5.1 本章总结
- 8.5.2 参考资料说明
- 第9章 深入理解GPS
- 9.1 概述
- 9.2 GPS基础知识
- 9.2.1 卫星导航基本原理
- 9.2.2 GPS系统组成及原理
- 9.2.3 OMA-SUPL协议
- 9.3 Android中的位置管理
- 9.3.1 LocationManager架构
- 9.3.2 LocationManager应用示例
- 9.3.3 LocationManager系统模块
- 9.4 本章总结和参考资料说明
- 9.4.1 本章总结
- 9.4.2 参考资料说明
- 附录