android_wifi讀書筆記之5-WPA_SUPPLICANT分析
本文為讀書筆記,整理自網路文獻和原始碼
5 WPA_SUPPLICANT分析
5.1、WPA_SUPPLICANT 分析1:
參考文獻:
http://www.cnblogs.com/chenbin7/p/3266032.html
http://blog.chinaunix.net/uid-26585427-id-4051479.html
wpa_supplicant軟體架構分析http://blog.csdn.net/fxfzz/article/details/6176414
wpa_supplicant本是開源專案原始碼,被谷歌修改後加入android移動平臺,它主要是用來支援WEP,WPA/WPA2和WAPI無線協議和加密認證的,而實際上的工作內容是通過socket(不管是wpa_supplicant與上層還是wpa_supplicant與驅動都採用socket通訊)與驅動互動上報資料給使用者,而使用者可以通過socket傳送命令給wpa_supplicant調動驅動來對WiFi晶片操作。簡單的說,wpa_supplicant就是WiFi驅動和使用者的中轉站外加對協議和加密認證的支援。
wpa_supplicant.c
首先定義一個驅動運算元組externstructwpa_driver_ops*wpa_supplicant_drivers[],然後是系列wpa_supplicant_XXX()方法,很多方法裡面呼叫 wpa_drv_XXX()方法,這些方法是wpa_supplicant_i.h中實現的方法。幾乎每個方法都需要一個wpa_supplicant結構,對其進行所有的控制和通訊操作。
Wpa_supplicant_i.h
其中定義了一個重要資料結構wpa_supplicant,其中有一個重要的driver成 員,它是wpa_driver_ops型別,可以被用來呼叫抽象層的介面。接下來是系列方法宣告,這些方法宣告在wpa_supplicant.c中實現,然後就是wpa_drv_XXX方法,這些方法就是在 wpa_supplicant.c中被wpa_supplicant_xxx方法呼叫的,而這些wpa_drv_xxx方法也都有一個wpa_supplicant結構的變數指標,用來呼叫封裝的抽象介面,而這些抽象介面的實現在driver_wext.c中(如果使用的漢斯WEXT驅動)。
這裡要注意的是:在wpa_suppliant.c檔案中定義的很多方法是在該標頭檔案中宣告的,而不是在wpa_supplicant.h中宣告的。
上行介面:
wpa_supplicant提供兩種方式的上行介面。一種基於傳統dbus機制實現與其他程序間的IPC通訊;另一種通過Unix domain socket機制實現程序間的IPC通訊。
(1) Dbus
(2) Socket
該介面主要在檔案”wpa_ctrl.h”,“wpa_ctrl.c”,“ctrl_iface_unix.c”,“ctrl_iface.h”和“ctrl_iface.c”實現。
1. “wpa_ctrl.h”,“wpa_ctrl.c”完成對controlinterface的封裝,對外提供統一的介面。其主要的工作是通過Unix domainsocket建立一個controlinterface 的client結點,與作為server的wpa_supplicant結點通訊。
wpa_supplicant 提供兩種由外部模組獲取資訊的方式:一種是外部模組通過傳送request 命令然後獲取response的問答模式,另一種是wpa_supplicant主動向外部發送event事件,由外部模組監聽接收。
一般的常用做法是外部模組通過呼叫wpa_ctrl_open()兩次,分別建立兩個controlinterface介面。一個為ctrl interface,用於傳送命令,獲取資訊。然後,將另外一個介面作為引數,呼叫wpa_ctrl_attach,成為 monitor interface,用於監聽接收來自於wpa_supplicant的event事件。此舉可以降低通訊的耦合性,避免response和event的相互干擾。
2. “ctrl_iface_unix.c”實現wpa_supplicant的Unix domainsocket通訊機制中server結點,完成對client結點的響應。
3. “ctrl_iface.h”和“ctrl_iface.c”主要實現了各種request命令的底層處理方法。
下行介面:
wpa_supplicant提供的下行介面主要用於和kernel(driver)進行通訊,下發命令和獲取資訊。
wpa_supplicant下行介面主要包括三種重要的介面:
1. PF_INET socket介面,主要用於向kernel傳送ioctl命令,控制並獲取相應資訊。
2. PF_NETLINK socket介面,主要用於接收kernel傳送上來的event 事件。
3. PF_PACKET socket介面,主要用於向driver傳遞802.1X報文。
(1)“driver.h”,“drivers.c”主要用於封裝底層差異對外顯示一個相同的wpa_driver_ops介面。wpa_supplicant可支援atheros, broadcom, madwifi, ndis,nl80211, wext等多種驅動。
(2)“driver_nl80211.c”實現了nl80211形式的wpa_driver_ops,並建立了PF_INETsocket介面和PF_NETLINK socket介面,然後通過這兩個介面完成與kernel的資訊互動。
wpa_driver_nl80211_event_receive方法:處理kernel主動傳送的event事件的 callback 方法。
(3)“l2_packet.h”和“l2_packet_linux.c”主要用於實現PF_PACKET socket介面,通過該介面,wpa_supplicant可以直接將802.1X packet傳送到L2層,而不經過TCP/IP協議棧。
WEXT(WirelessExtension):使用WEXT的工具通過ioctl和驅動通訊,典型工具ifconfig等;NL80211(Netlink 80211):使用NL80211的工具通過一個特殊的socket( Netlink技術)和驅動打通訊,典型工具包括IW、iwconfig等。
nl80211介面逐漸替代wext介面的原因主要是使用netlink技術在應用層和核心層資料交換上相比ioctl方式具有優勢。 Netlink 是一種在核心與使用者應用間進行雙向資料傳輸的非常好的方式,使用者態應用使用標準的 socket API 就可以使用 netlink 提供的強大功能,核心態需要使用專門的核心 API 來使用 netlink。
wpa_supplocant服務開啟:
service wpa_supplicant/system/bin/wpa_supplicant -Dnl80211 -iwlan0-c/data/misc/wifi/wpa_supplicant.conf
socket wpa_wlan0 dgram 660 wifi wifi
group wifi inet
disabled
oneshot
5.2 WPA_SUPPLICANT分析2,主要涉及下行介面
在drivers.h中定義了一個wpa_driver_ops結構體,結構體成員是一個個方法指標。在driviers.c裡面都是不同驅動操作介面的集合wpa_driver_XXX_ops變數;然後就是定義一個驅
動操作介面集合的陣列,根據巨集定義新增對應的驅動操作介面集合的變數。不同的驅動介面採用不同的檔案來實現,如果wpa_supplicant使用的是wext介面與驅動進行通訊,那麼
就在external/wpa_supplicant_8/src/drivers/driver_nl80211.c檔案裡面對wpa_driver_ops結構體裡面的成員賦值,這些成員指標指向的方法也在這個檔案裡面實現。程式碼如下:
const struct wpa_driver_opswpa_driver_nl80211_ops = {
.name= "nl80211",
.desc= "Linux nl80211/cfg80211",
.get_bssid= wpa_driver_nl80211_get_bssid,
.get_ssid= wpa_driver_nl80211_get_ssid,
.set_key= wpa_driver_nl80211_set_key,
.scan2= wpa_driver_nl80211_scan,
.get_scan_results2= wpa_driver_nl80211_get_scan_results,
.deauthenticate= wpa_driver_nl80211_deauthenticate,
.disassociate= wpa_driver_nl80211_disassociate,
.authenticate= wpa_driver_nl80211_authenticate,
.associate= wpa_driver_nl80211_associate,
.global_init= nl80211_global_init,
.global_deinit= nl80211_global_deinit,
.init2= wpa_driver_nl80211_init,
.deinit= wpa_driver_nl80211_deinit,
.get_capa= wpa_driver_nl80211_get_capa,
.set_operstate= wpa_driver_nl80211_set_operstate,
.set_supp_port= wpa_driver_nl80211_set_supp_port,
.set_country= wpa_driver_nl80211_set_country,
.set_beacon= wpa_driver_nl80211_set_beacon,
.if_add= wpa_driver_nl80211_if_add,
.if_remove= wpa_driver_nl80211_if_remove,
.send_mlme= wpa_driver_nl80211_send_mlme,
.get_hw_feature_data= wpa_driver_nl80211_get_hw_feature_data,
.sta_add= wpa_driver_nl80211_sta_add,
.sta_remove= wpa_driver_nl80211_sta_remove,
.hapd_send_eapol= wpa_driver_nl80211_hapd_send_eapol,
.sta_set_flags= wpa_driver_nl80211_sta_set_flags,
#ifdef HOSTAPD
.hapd_init= i802_init,
.hapd_deinit= i802_deinit,
.set_wds_sta= i802_set_wds_sta,
#endif /* HOSTAPD */
#if defined(HOSTAPD) || defined(CONFIG_AP)
.get_seqnum= i802_get_seqnum,
.flush= i802_flush,
.read_sta_data= i802_read_sta_data,
.get_inact_sec= i802_get_inact_sec,
.sta_clear_stats= i802_sta_clear_stats,
.set_rts= i802_set_rts,
.set_frag= i802_set_frag,
.set_cts_protect= i802_set_cts_protect,
.set_preamble= i802_set_preamble,
.set_short_slot_time= i802_set_short_slot_time,
.set_tx_queue_params= i802_set_tx_queue_params,
.set_sta_vlan= i802_set_sta_vlan,
.set_ht_params= i802_set_ht_params,
.set_rate_sets= i802_set_rate_sets,
.sta_deauth= i802_sta_deauth,
.sta_disassoc= i802_sta_disassoc,
#endif /* HOSTAPD || CONFIG_AP */
.set_freq= i802_set_freq,
.send_action= wpa_driver_nl80211_send_action,
.send_action_cancel_wait= wpa_driver_nl80211_send_action_cancel_wait,
.remain_on_channel= wpa_driver_nl80211_remain_on_channel,
.cancel_remain_on_channel=
wpa_driver_nl80211_cancel_remain_on_channel,
.probe_req_report= wpa_driver_nl80211_probe_req_report,
.disable_11b_rates= wpa_driver_nl80211_disable_11b_rates,
.deinit_ap= wpa_driver_nl80211_deinit_ap,
.resume= wpa_driver_nl80211_resume,
.send_ft_action= nl80211_send_ft_action,
.signal_monitor= nl80211_signal_monitor,
.signal_poll= nl80211_signal_poll,
.send_frame= nl80211_send_frame,
.set_intra_bss= nl80211_set_intra_bss,
.set_param= nl80211_set_param,
.get_radio_name= nl80211_get_radio_name,
.add_pmkid= nl80211_add_pmkid,
.remove_pmkid= nl80211_remove_pmkid,
.flush_pmkid= nl80211_flush_pmkid,
#ifdef ANDROID_BRCM_P2P_PATCH
.get_noa= wpa_driver_get_p2p_noa,
.set_noa= wpa_driver_set_p2p_noa,
.set_p2p_powersave= wpa_driver_set_p2p_ps,
.set_ap_wps_ie= wpa_driver_set_ap_wps_p2p_ie,
#endif
#ifdef ANDROID
.driver_cmd= wpa_driver_nl80211_driver_cmd, //處理DRIVER開頭的命令
#endif
};
在啟動wpa_supplicant服務時,帶了很多引數
Service wpa_supplicant/system/bin/wpa_supplicant -Dnl80211 -iwlan0-c/data/misc/wifi/wpa_supplicant.conf
其中-D<driver>: 驅動型別代表的是驅動型別。也就是-Dnl80211
wpa_supplicant的主方法main中會將驅動型別引數賦給iface->driver
case'D':
iface->driver= optarg;
之後呼叫wpa_supplicant_add_iface(global,&ifaces[i]),wpa_supplicant_add_iface呼叫的是wpa_supplicant_init_iface。在wpa_supplicant_init_iface(struct
wpa_supplicant *wpa_s,struct wpa_interface*iface)方法裡面
driver = iface->driver;
之後呼叫wpa_supplicant_set_driver(wpa_s,driver)來設定wpa_supplicant使用哪個介面與驅動進行通訊:
wpa_s->driver= wpa_drivers[i];
wpa_s->global_drv_priv= wpa_s->global->drv_priv[i];
之後呼叫wpa_supplicant_driver_init初始化。