wpa_supplicant 2.0版源代码阅读(3) ---- driver event模块
2014-02-21 10:24
597 查看
3. Driver Event模块(windows篇)
wpa_supplicant通过Driver Event模块获取无线驱动的相关事件,并执行相应的操作。无线驱动的主要事件如网络连接成功(或称关联成功,wpa_supplicant可以开始WPA认证过程);断开网络(wpa_supplicant是否准备重新连接,重新开始WPA认证);扫描结束事件(准备获取驱动的扫描结果);网卡移除事件;网卡插入事件等等。
Driver Event模块的设计分两层功能。
1. 下层负责实现底层无线驱动(或者称OS内核)事件的获取。与无线驱动(OS)事件相关代码实现在src/drivers/目录下。这部分的功能与不同操作系统相关。
其功能包括:
1)驱动事件获取的初始化工作;
2)OS层的事件回调处理,
3)驱动事件的类型转换,即把不同OS下的事件类型转换成统一的WPA Event类型,以便wpa_supplicant处理;
4)通知wpa_supplicant Event Loop模块进行处理。
2. 上层负责wpa_supplicant真正处理相关的wpa事件。在接收到事件通知之后,最终调用wpa_supplicant_event()函数进行事件处理。WPA Event事件处理代码在wpa_supplicant/evnet.c中实现。
本节的代码分析主要是与无线驱动事件(OS相关)功能的分析。与supplicant中WPA认证功能关系不大。WPA Event的处理在以后分析核心功能时具体分析。
3.1 Windows系统Driver Event模块的功能
Windows系统无线驱动是NDIS Miniport驱动,Driver Event模块具体功能如下:1. 采用WMI技术获取驱动事件。
2. 支持轮询方式或者异步方式的事件获取。
3. 异步方式的事件通知回调处理。
4. 异步方式下通知Event Loop主线程,通过管道技术传递事件信息,由主线程从管道中读取事件信息处理。
3.2 NDIS Driver Event的编译配置说明
[cpp]view plaincopy
ifdef CONFIG_DRIVER_NDIS
DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDIS
DRV_WPA_OBJS += src/drivers/driver_ndis.c
ifdef CONFIG_NDIS_EVENTS_INTEGRATED
DRV_WPA_OBJS += src/drivers/driver_ndis_.c
endif
ifndef CONFIG_L2_PACKET
CONFIG_L2_PACKET=pcap
endif
CONFIG_WINPCAP=y
ifdef CONFIG_USE_NDISUIO
DRV_WPA_CFLAGS += -DCONFIG_USE_NDISUIO
endif
endif
配置CONFIG_NDIS_EVENTS_INTEGRATED使其支持事件的异步处理。否则采用轮询的方式。
3.3 Driver Event模块的初始化
3.3.1 wpa_supplicant初始化Driver Event(window系统)
[cpp]view plaincopy
#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
drv->events = ndis_events_init(&drv->events_pipe, &drv->event_avail,
drv->ifname, drv->adapter_desc);
if (drv->events == NULL) {
wpa_driver_ndis_deinit(drv);
return NULL;
}
eloop_register_event(drv->event_avail, sizeof(drv->event_avail),
wpa_driver_ndis_event_pipe_cb, drv, NULL);
#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
wpa_supplicant在软件初始化阶段调用ndis_evnets_init()完成获取NDIS无线驱动事件的相关初始化设置,并调用eloop_register_event注册一个事件处理函数。
3.3.2 ndis_events_init()函数
应用层获取驱动事件的初始化配置在ndis_events_init()函数中实现.[cpp]
view plaincopy
struct ndis_events_data *
ndis_events_init(HANDLE *read_pipe, HANDLE *event_avail,
const char *ifname, const char *desc)
{
HRESULT hr;
IWbemObjectSink *pSink;
struct ndis_events_data *events;
events = os_zalloc(sizeof(*events));
if (events == NULL) {
wpa_printf(MSG_ERROR, "Could not allocate sink for events.");
return NULL;
}
events->ifname = os_strdup(ifname);
if (events->ifname == NULL) {
os_free(events);
return NULL;
}
if (wmi_refcnt++ == 0) {
hr = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(hr)) {
wpa_printf(MSG_ERROR, "CoInitializeEx() failed - "
"returned 0x%x", (int) hr);
os_free(events);
return NULL;
}
}
if (wmi_first) {
/* CoInitializeSecurity() must be called once and only once
* per process, so let's use wmi_first flag to protect against
* multiple calls. */
wmi_first = 0;
hr = CoInitializeSecurity(NULL, -1, NULL, NULL,
RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL, EOAC_SECURE_REFS, NULL);
if (FAILED(hr)) {
wpa_printf(MSG_ERROR, "CoInitializeSecurity() failed "
"- returned 0x%x", (int) hr);
os_free(events);
return NULL;
}
}
hr = CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER,
&IID_IWbemLocator,
(LPVOID *) (void *) &events->pLoc);
if (FAILED(hr)) {
wpa_printf(MSG_ERROR, "CoCreateInstance() failed - returned "
"0x%x", (int) hr);
CoUninitialize();
os_free(events);
return NULL;
}
if (ndis_events_get_adapter(events, ifname, desc) < 0) {
CoUninitialize();
os_free(events);
return NULL;
}
wpa_printf(MSG_DEBUG, "ndis_events: use adapter descriptor '%S'",
events->adapter_desc);
hr = call_IWbemLocator_ConnectServer(
events->pLoc, L"ROOT\\WMI", NULL, NULL,
0, 0, 0, 0, &events->pSvc);
if (FAILED(hr)) {
wpa_printf(MSG_ERROR, "Could not connect to server - error "
"0x%x", (int) hr);
CoUninitialize();
os_free(events->adapter_desc);
os_free(events);
return NULL;
}
wpa_printf(MSG_DEBUG, "Connected to ROOT\\WMI.");
ndis_events_constructor(events);
pSink = &events->sink;
pSink->lpVtbl = &events->sink_vtbl;
events->sink_vtbl.QueryInterface = ndis_events_query_interface;
events->sink_vtbl.AddRef = ndis_events_add_ref;
events->sink_vtbl.Release = ndis_events_release;
events->sink_vtbl.Indicate = ndis_events_indicate;
events->sink_vtbl.SetStatus = ndis_events_set_status;
if (register_async_notification(pSink, events->pSvc) < 0) {
wpa_printf(MSG_DEBUG, "Failed to register async "
"notifications");
ndis_events_destructor(events);
os_free(events->adapter_desc);
os_free(events);
return NULL;
}
*read_pipe = events->read_pipe;
*event_avail = events->event_avail;
return events;
}
1)调用CoInitializeEx(0, COINIT_MULTITHREADED)初始化COM
2)调用CoInitializeSecurity设置通用的COM安全等级
3)调用CoCreateInstance获取WMI Locator。
4)调用ndis_events_get_adapter()函数,该函数通过WMI CIMv2 Win32_NetworkAdapter获取网络适配器的描述符信息。用于检查设备是否插入(如USB无线网卡)。
5)调用call_IWbemLocator_ConnectServer()函数。即调用IWbemLocator_ConnectServer()。连接到本地\root\wmi命名空间(NDIS驱动的事件访问是通过\root\wmi命名空间)。
6)调用ndis_events_constructor()。创建一个event_avail事件和一个管道用于向Event Loop模块进行事件通知和事件信息传递。
7)调用register_async_notification()注册事件异步通知回调处理。并设置哪些NDIS事件需要通知处理。见下节代码分析。
3.3.3 register_async_notification函数分析
[cpp]view plaincopy
static int register_async_notification(IWbemObjectSink *pDestSink,
IWbemServices *pSvc)
{
int i;
const char *class_list[] = {
"MSNdis_StatusMediaConnect",
"MSNdis_StatusMediaDisconnect",
"MSNdis_StatusMediaSpecificIndication",
"MSNdis_NotifyAdapterArrival",
"MSNdis_NotifyAdapterRemoval",
NULL
};
for (i = 0; class_list[i]; i++) {
if (notification_query(pDestSink, pSvc, class_list[i]) < 0)
return -1;
}
return 0;
}
[cpp]
view plaincopy
static int notification_query(IWbemObjectSink *pDestSink,
IWbemServices *pSvc, const char *class_name)
{
HRESULT hr;
WCHAR query[256];
_snwprintf(query, 256,
L"SELECT * FROM %S", class_name);
wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
hr = call_IWbemServices_ExecNotificationQueryAsync(
pSvc, L"WQL", query, 0, 0, pDestSink);
if (FAILED(hr)) {
wpa_printf(MSG_DEBUG, "ExecNotificationQueryAsync for %s "
"failed with hresult of 0x%x",
class_name, (int) hr);
return -1;
}
return 0;
}
1)首先定义一个IWbemObjectSink数据结构变量,设置事件回调处理函数ndis_events_indicate();
2)_snwprintf(query, 256, L"SELECT * FROM %S", class_name)是根据驱动事件类名构造一个类SQL查询语法的字符串;
3)调用IWbemServices_ExecNotificationQueryAsync ()函数注册接收异步事件。这样一旦驱动事件发生,会调用 IWbemObjectSink的Indicate回调函数。
对于NDIS miniport无线驱动一共设置了五种事件需要通知处理。
1) MSNdis_StatusMediaConnect,网络已连接
2) MSNdis_StatusMediaDisconnect,网络断开
3) MSNdis_StatusMediaSpecificIndication,媒体信息指示
4) MSNdis_NotifyAdapterArrival,网卡插入
5) MSNdis_NotifyAdapterRemoval,网卡拔出
3.3 异步方式的事件回调处理
[cpp]view plaincopy
static HRESULT STDMETHODCALLTYPE
ndis_events_indicate(IWbemObjectSink *this, long lObjectCount,
IWbemClassObject __RPC_FAR *__RPC_FAR *ppObjArray)
{
struct ndis_events_data *events = (struct ndis_events_data *) this;
long i;
if (events->terminating) {
wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore "
"indication - terminating");
return WBEM_NO_ERROR;
}
/* wpa_printf(MSG_DEBUG, "Notification received - %d object(s)",
lObjectCount); */
for (i = 0; i < lObjectCount; i++) {
IWbemClassObject *pObj = ppObjArray[i];
HRESULT hr;
VARIANT vtClass, vt;
hr = IWbemClassObject_Get(pObj, L"__CLASS", 0, &vtClass, NULL,
NULL);
if (FAILED(hr)) {
wpa_printf(MSG_DEBUG, "Failed to get __CLASS from "
"event.");
break;
}
/* wpa_printf(MSG_DEBUG, "CLASS: '%S'", vtClass.bstrVal); */
hr = IWbemClassObject_Get(pObj, L"InstanceName", 0, &vt, NULL,
NULL);
if (FAILED(hr)) {
wpa_printf(MSG_DEBUG, "Failed to get InstanceName "
"from event.");
VariantClear(&vtClass);
break;
}
if (wcscmp(vtClass.bstrVal,
L"MSNdis_NotifyAdapterArrival") == 0) {
wpa_printf(MSG_DEBUG, "ndis_events_indicate: Try to "
"update adapter description since it may "
"have changed with new adapter instance");
ndis_events_get_adapter(events, events->ifname, NULL);
}
if (wcscmp(events->adapter_desc, vt.bstrVal) != 0) {
wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore "
"indication for foreign adapter: "
"InstanceName: '%S' __CLASS: '%S'",
vt.bstrVal, vtClass.bstrVal);
VariantClear(&vtClass);
VariantClear(&vt);
continue;
}
VariantClear(&vt);
if (wcscmp(vtClass.bstrVal,
L"MSNdis_StatusMediaSpecificIndication") == 0) {
ndis_events_media_specific(events, pObj);
} else if (wcscmp(vtClass.bstrVal,
L"MSNdis_StatusMediaConnect") == 0) {
ndis_events_media_connect(events);
} else if (wcscmp(vtClass.bstrVal,
L"MSNdis_StatusMediaDisconnect") == 0) {
ndis_events_media_disconnect(events);
} else if (wcscmp(vtClass.bstrVal,
L"MSNdis_NotifyAdapterArrival") == 0) {
ndis_events_adapter_arrival(events);
} else if (wcscmp(vtClass.bstrVal,
L"MSNdis_NotifyAdapterRemoval") == 0) {
ndis_events_adapter_removal(events);
} else {
wpa_printf(MSG_DEBUG, "Unepected event - __CLASS: "
"'%S'", vtClass.bstrVal);
}
VariantClear(&vtClass);
}
return WBEM_NO_ERROR;
}
驱动事件回调函数ndis_events_indicate()是根据需要处理的类名分别处理。在事件注册一节已经说明,wpa_supplicant的ndis driver envent模块共注册了5种事件。在事件回调处理函数中,根据回调参数获取的类名,分别调用:
ndis_events_media_specific()
ndis_events_media_connect()
ndis_events_media_disconnect()
ndis_events_adapter_arrival()
ndis_events_adapter_removal()
这五个函数都调用了ndis_events_send_event(),把NDIS驱动事件类名转换成Driver Event模块NDIS实现代码的事件类型(不是WPA Event类型)。
事件类型如下定义:
[cpp]
view plaincopy
enum event_types { EVENT_CONNECT, EVENT_DISCONNECT, EVENT_MEDIA_SPECIFIC,
EVENT_ADAPTER_ARRIVAL, EVENT_ADAPTER_REMOVAL };
ndis_events_send_event()函数主要是完成把事件信息写入管道,并设置event_avail事件,通知wpa_supplicant的Event Loop模块处理。
[cpp]
view plaincopy
if (WriteFile(events->write_pipe, buf, pos - buf, &written, NULL)) {
SetEvent(events->event_avail);
return 0;
}
这样整个事件回调处理已经完毕。下一步就是Event Loop模块在收到event_avail事件之后,从管道中读取事件信息进行处理。
3.5 Event Loop通过管道获取事件信息处理流程
在3.3.1节提到初始化时,调用eloop_register_event()注册了event_avail事件的回调函数wpa_driver_ndis_event_pipe_cb()。该函数读取管道的事件信息并处理。[cpp]
view plaincopy
void wpa_driver_ndis_event_pipe_cb(void *eloop_data, void *user_data)
{
struct wpa_driver_ndis_data *drv = eloop_data;
u8 buf[512];
DWORD len;
ResetEvent(drv->event_avail);
if (ReadFile(drv->events_pipe, buf, sizeof(buf), &len, NULL))
wpa_driver_ndis_event_process(drv, buf, len);
else {
wpa_printf(MSG_DEBUG, "%s: ReadFile() failed: %d", __func__,
(int) GetLastError());
}
}
1)复位event_avail事件
2)调用ReadFile读取管道的事件信息
3)调用wpa_driver_ndis_event_process()处理。该函数根据解析的Driver Event模块定义的事件类型分别调用:
wpa_driver_ndis_event_connect() 对应EVENT_CONNECT
wpa_driver_ndis_event_disconnect() 对应EVENT_DISCONNECT
wpa_driver_ndis_event_media_specific() 对应EVENT_MEDIA_SPECIFIC
wpa_driver_ndis_event_adapter_arrival() 对应EVENT_ADAPTER_ARRIVAL
wpa_driver_ndis_event_adapter_removal() 对应EVENT_ADAPTER_REMOVAL
4)上面5个函数最终调用wpa_supplicant_event(),并把事件类型转换成WPA EVENT类型。wpa_supplicant_evnet()的处理与OS的驱动层就不相关了。
注:wpa_driver_ndis_event_pipe_cb()在主线程main()中调用,即Event Loop模块中调用,避免同步问题。
3.6 NDIS驱动事件类名到WPA EVENT类型对应关系说明
最终的事件处理在函数wpa_supplicant_evnet()中执行,NDIS驱动的事件类型也转换成了WPA EVENT(为了不同OS下实现的统一)。对于获取的五种NDIS驱动事件,其对应的WPA EVENT类型名说明如下:NDIS事件名 WPA EVENT类型名
1) MSNdis_StatusMediaConnect,网络已连接 EVENT_ASSOC
2) MSNdis_StatusMediaDisconnect,网络断开 EVENT_DISASSOC
3) MSNdis_StatusMediaSpecificIndication,媒体信息指示
Ndis802_11StatusType_Authentication EVENT_MICHAEL_MIC_FAILURE
Ndis802_11StatusType_PMKID_CandidateList EVENT_PMKID_CANDIDATE
4) MSNdis_NotifyAdapterArrival,网卡插入 EVENT_INTERFACE_STATUS
5) MSNdis_NotifyAdapterRemoval,网卡拔出 EVENT_INTERFACE_STATUS
相关文章推荐
- wpa_supplicant 2.0版源代码阅读(2) ---- L2_packet模块
- wpa_supplicant 2.0版源代码阅读(3) ---- driver event模块 (window篇)
- wpa_supplicant 2.0版源代码阅读(1) ---- wpa_supplicant简介
- wpa_supplicant 2.0版源代码阅读(2) ---- L2_packet模块
- wpa_supplicant 2.0版源代码阅读(4)---- Linux wext和nl80211接口简介
- wpa_supplicant 2.0版源代码阅读(4)---- Linux wext和nl80211接口简介
- 如何提高阅读源代码的效率
- 标准【wpa_supplicant】到【神州数码】 认证的修改记录(上)——准备工作
- 在阅读源代码或设计文档时,看到惊艳的技巧
- wpa_supplicant分析 2. wpa_supplicant
- WEP,WPA-PSK,WPA2-PSK握手深入分析3--wpa_supplicant代码分析-扫描
- 如何阅读源代码
- 跟一下wpa_supplicant(2) wifi enable
- 转:linux下wpa_supplicant过802.1x认证的办法
- memcached源代码阅读(2)-main函数
- Java源代码阅读——Object类
- linux下阅读源代码的工具
- Linux下使用wpa_supplicant连接WPA…
- Tomcat源代码阅读----源代码部署(1)