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所示。
:-: 
圖4-8 eap_method數據結構
:-: 
圖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 參考資料說明
- 附錄