1 wpa_supplicant.conf
Android系统中,wpa_supplicant启动是通过
setprop ctrl.start wpa_supplicant
来触发init进程去fork一个子进程来完成的
wpa_supplicant源代码中包含一个启动配置文件的模板,路径为
external/wpa_supplicant_8/wpa_supplicant/wpa_supplicant.conf
该文件对各项配置参数都有说明
下面是我司手机pull出来的一份配置
./data/vendor/wifi/wpa/wpa_supplicant.conf
或 ./vendor/etc/wifi/wpa_supplicant.conf
ctrl_interface=wlan0
update_config=1
manufacturer=MediaTek Inc.
device_name=Wireless Client
model_name=MTK Wireless Model
model_number=1.0
serial_number=2.0
device_type=10-0050F204-5
os_version=01020300
config_methods=display push_button keypad
p2p_no_group_iface=1
driver_param=use_p2p_group_interface=1
hs20=1
pmf=1
ctrl_interface指明控制接口unix域socket的文件名
update_config表示如果wpa_supplicant运行过程中修改了配置信息,则需要把它们保存到此文件中
wpa_supplicant运行过程中得到的无线网络信息会通过一个"network"配置项保存到此配置文件中
如果该信息完整,一旦wpa_supplicant找到该网络就会尝试用保存的信息去加入它(这也是为什么用户在settings打开无线网络后,手机能自动加入周围某个曾经登录过的无线网络的原因)
看了下我司的,不会保存,需要研究下为什么不保存
2 main函数
wpa_supplicant/main.c
int main(int argc, char *argv[])
{
int c, i;
struct wpa_interface *ifaces, *iface;
int iface_count, exitcode = -1;
struct wpa_params params;
struct wpa_global *global;
/*
Android平台中,os_program_init()实现在os_unix.c中,Android做了一些修改
主要是权限方面的设置防止某些情况下被破解者利用权限漏洞以获取root权限
*/
if (os_program_init())
return -1;
os_memset(¶ms, 0, sizeof(params));
params.wpa_debug_level = MSG_INFO;
iface = ifaces = os_zalloc(sizeof(struct wpa_interface));
if (ifaces == NULL)
return -1;
iface_count = 1;
//输入输出重定向到 /dev/null设备
wpa_supplicant_fd_workaround(1);
//参数解析
for (;;) {
c = getopt(argc, argv,
"b:Bc:C:D:de:f:g:G:hi:I:KLMm:No:O:p:P:qsTtuvW");
if (c < 0)
break;
switch (c) {
case 'b':
iface->bridge_ifname = optarg;
break;
case 'B':
params.daemonize++;
break;
case 'c':
//指定配置文件名,该参数赋值给wpa_interface中的变量
iface->confname = optarg;
break;
case 'C':
iface->ctrl_interface = optarg;
break;
case 'D':
//指定driver名称,同样赋值给wpa_interface的变量
iface->driver = optarg;
break;
case 'd':
#ifdef CONFIG_NO_STDOUT_DEBUG
printf("Debugging disabled with "
"CONFIG_NO_STDOUT_DEBUG=y build time "
"option.\n");
goto out;
#else /* CONFIG_NO_STDOUT_DEBUG */
params.wpa_debug_level--;
break;
#endif /* CONFIG_NO_STDOUT_DEBUG */
case 'e':
//指定初始随机数文件,用于后续随机数的生成
params.entropy_file = optarg;
break;
#ifdef CONFIG_DEBUG_FILE
case 'f':
params.wpa_debug_file_path = optarg;
break;
#endif /* CONFIG_DEBUG_FILE */
case 'g':
params.ctrl_interface = optarg;
break;
case 'G':
params.ctrl_interface_group = optarg;
break;
case 'h':
usage();
exitcode = 0;
goto out;
case 'i':
//指定网络设备接口名
iface->ifname = optarg;
break;
case 'I':
iface->confanother = optarg;
break;
case 'K':
params.wpa_debug_show_keys++;
break;
case 'L':
license();
exitcode = 0;
goto out;
#ifdef CONFIG_P2P
case 'm':
params.conf_p2p_dev = optarg;
break;
#endif /* CONFIG_P2P */
case 'o':
params.override_driver = optarg;
break;
case 'O':
params.override_ctrl_interface = optarg;
break;
case 'p':
iface->driver_param = optarg;
break;
case 'P':
os_free(params.pid_file);
params.pid_file = os_rel2abs_path(optarg);
break;
case 'q':
params.wpa_debug_level++;
break;
#ifdef CONFIG_DEBUG_SYSLOG
case 's':
params.wpa_debug_syslog++;
break;
#endif /* CONFIG_DEBUG_SYSLOG */
#ifdef CONFIG_DEBUG_LINUX_TRACING
case 'T':
params.wpa_debug_tracing++;
break;
#endif /* CONFIG_DEBUG_LINUX_TRACING */
case 't':
params.wpa_debug_timestamp++;
break;
#ifdef CONFIG_CTRL_IFACE_DBUS_NEW
case 'u':
params.dbus_ctrl_interface = 1;
break;
#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
case 'v':
printf("%s\n", wpa_supplicant_version);
exitcode = 0;
goto out;
case 'W':
params.wait_for_monitor++;
break;
#ifdef CONFIG_MATCH_IFACE
case 'M':
params.match_iface_count++;
iface = os_realloc_array(params.match_ifaces,
params.match_iface_count,
sizeof(struct wpa_interface));
if (!iface)
goto out;
params.match_ifaces = iface;
iface = ¶ms.match_ifaces[params.match_iface_count -
1];
os_memset(iface, 0, sizeof(*iface));
break;
#endif /* CONFIG_MATCH_IFACE */
case 'N':
iface_count++;
iface = os_realloc_array(ifaces, iface_count,
sizeof(struct wpa_interface));
if (iface == NULL)
goto out;
ifaces = iface;
iface = &ifaces[iface_count - 1];
os_memset(iface, 0, sizeof(*iface));
break;
default:
usage();
exitcode = 0;
goto out;
}
}
exitcode = 0;
//根据传入的参数,创建并初始化一个wpa_global对象
global = wpa_supplicant_init(¶ms);
if (global == NULL) {
wpa_printf(MSG_ERROR, "Failed to initialize wpa_supplicant");
exitcode = -1;
goto out;
} else {
wpa_printf(MSG_INFO, "Successfully initialized "
"wpa_supplicant");
}
if (fst_global_init()) {
wpa_printf(MSG_ERROR, "Failed to initialize FST");
exitcode = -1;
goto out;
}
#if defined(CONFIG_FST) && defined(CONFIG_CTRL_IFACE)
if (!fst_global_add_ctrl(fst_ctrl_cli))
wpa_printf(MSG_WARNING, "Failed to add CLI FST ctrl");
#endif
for (i = 0; exitcode == 0 && i < iface_count; i++) {
struct wpa_supplicant *wpa_s;
if ((ifaces[i].confname == NULL &&
ifaces[i].ctrl_interface == NULL) ||
ifaces[i].ifname == NULL) {
if (iface_count == 1 && (params.ctrl_interface ||
#ifdef CONFIG_MATCH_IFACE
params.match_iface_count ||
#endif /* CONFIG_MATCH_IFACE */
params.dbus_ctrl_interface))
break;
usage();
exitcode = -1;
break;
}
//wpa_supplicant支持操作多个无线设备,此处需要将它们一一添加到wpa_supplicant中
wpa_s = wpa_supplicant_add_iface(global, &ifaces[i], NULL);
if (wpa_s == NULL) {
exitcode = -1;
break;
}
}
#ifdef CONFIG_MATCH_IFACE
if (exitcode == 0)
exitcode = wpa_supplicant_init_match(global);
#endif /* CONFIG_MATCH_IFACE */
//Android平台中,wpa_supplicant通过select或epoll方式实现多路I/O服用
if (exitcode == 0)
exitcode = wpa_supplicant_run(global);
wpa_supplicant_deinit(global);
//退出
fst_global_deinit();
out:
wpa_supplicant_fd_workaround(0);
os_free(ifaces);
#ifdef CONFIG_MATCH_IFACE
os_free(params.match_ifaces);
#endif /* CONFIG_MATCH_IFACE */
os_free(params.pid_file);
os_program_deinit();
return exitcode;
}
3 几个重要的数据结构
wpa_interface用于描述一个无线网络设备,该参数在初始化时用到
wpa_global是一个全局性质的上下文信息,它通过ifaces变量指向一个wpa_supplicant对象
(此处先mark一下,系统内所有wpa_supplicant对象将通过单向链表连接在一起,所以严格意义上来说,ifaces变量指向一个wpa_supplicant对象链表)
dri_pirv包含driver wrapper所需要的全局上下文信息,其drv_count代表当前编译到系统中的driver wrapper个数
另外,wpa_global有一个全局控制接口,如果设置该接口,其他wpa_interface设置的控制接口将被替代
wpa_supplicant是核心数据结构 ,一个interface对应有一个wpa_supplicant对象,系统中所有wpa_supplicant对象通过next变量链接在一起
ctrl_iface_global_priv是全局控制接口的信息,内部包含一个用于通信的socket句柄
4 wpa_supplicant_init
wpa_supplicant/wpa_supplicant.c
struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
{
struct wpa_global *global;
int ret, i;
if (params == NULL)
return NULL;
#ifdef CONFIG_DRIVER_NDIS
//windows driver支持
{
void driver_ndis_init_ops(void);
driver_ndis_init_ops();
}
#endif /* CONFIG_DRIVER_NDIS */
#ifndef CONFIG_NO_WPA_MSG
//设置全局回调函数
wpa_msg_register_ifname_cb(wpa_supplicant_msg_ifname_cb);
#endif /* CONFIG_NO_WPA_MSG */
if (params->wpa_debug_file_path)
//输出日志文件设置
wpa_debug_open_file(params->wpa_debug_file_path);
if (!params->wpa_debug_file_path && !params->wpa_debug_syslog)
wpa_debug_setup_stdout();
if (params->wpa_debug_syslog)
wpa_debug_open_syslog();
if (params->wpa_debug_tracing) {
ret = wpa_debug_open_linux_tracing();
if (ret) {
wpa_printf(MSG_ERROR,
"Failed to enable trace logging");
return NULL;
}
}
//注册EAP方法
ret = eap_register_methods();
if (ret) {
wpa_printf(MSG_ERROR, "Failed to register EAP methods");
if (ret == -2)
wpa_printf(MSG_ERROR, "Two or more EAP methods used "
"the same EAP type.");
return NULL;
}
//创建一个wpa_global对象
global = os_zalloc(sizeof(*global));
......//初始化global中的其他参数
wpa_printf(MSG_DEBUG, "wpa_supplicant v%s", VERSION_STR);
//初始化事件循环机制
if (eloop_init()) {
wpa_printf(MSG_ERROR, "Failed to initialize event loop");
wpa_supplicant_deinit(global);
return NULL;
}
//初始化随机数相关资源,用于提升后续随机数生成的随机性
random_init(params->entropy_file);
//初始化全局控制接口对象
global->ctrl_iface = wpa_supplicant_global_ctrl_iface_init(global);
if (global->ctrl_iface == NULL) {
wpa_supplicant_deinit(global);
return NULL;
}
//初始化通知机制相关资源,和dbus有关
if (wpas_notify_supplicant_initialized(global)) {
wpa_supplicant_deinit(global);
return NULL;
}
for (i = 0; wpa_drivers[i]; i++)
global->drv_count++;
if (global->drv_count == 0) {
wpa_printf(MSG_ERROR, "No drivers enabled");
wpa_supplicant_deinit(global);
return NULL;
}
//分配全局driver wrapper上下文信息数组
global->drv_priv = os_calloc(global->drv_count, sizeof(void *));
if (global->drv_priv == NULL) {
wpa_supplicant_deinit(global);
return NULL;
}
#ifdef CONFIG_WIFI_DISPLAY
if (wifi_display_init(global) < 0) {
wpa_printf(MSG_ERROR, "Failed to initialize Wi-Fi Display");
wpa_supplicant_deinit(global);
return NULL;
}
#endif /* CONFIG_WIFI_DISPLAY */
eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
wpas_periodic, global, NULL);
return global;
}
wpa_supplicant_init函数的主要功能是初始化wpa_global以及一些与整个程序相关的资源,包括随机数资源、eloop事件循环机制以及设置消息全局回调函数
5 消息全局回调函数
wpa_msg_get_ifname_func
有些输出信息中需要打印出网卡接口名,该回调函数用于获取网卡接口名
wpa_msg_cg_func
除了打印输出信息外,还可通过该回调函数进行一些特殊处理,如把输出信息发送给客户端进行处理
src/utils/wpa_debug.c
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;
}
static wpa_msg_cb_func wpa_msg_cb = NULL;
void wpa_msg_register_cb(wpa_msg_cb_func func) {
wpa_msg_cb = func;
}
6 wpa_supplicant_init的三个关键点
1.eap_register_methods函数
该函数根据编译配置项来注册所需的eap method
2.eloop_init函数及event loop模块
eloop_init函数仅初始化事件驱动的核心数据结构体ellop_data
事件驱动机制的实现是利用了epoll(如果编译时设置了CONFIG_ELOOP_POLL选项)或select实现了I/O复用
select(或epoll)是I/O复用的重要函数,相关学习见 多路I/O复用
从事件角度来看,wpa_supplicant的事件驱动机制支持5种类型的event
1.read_event 读事件,例如来自socket的可读事件
2.write event 写事件,例如socket的可写事件
3.exception event 异常事件,如果socket操作发生错误,则由该事件处理
4.timeout event 定时事件,通过select的等待超时机制来实现定时事件
5.signal event 信号事件,来源于kernel
3.wpa_driver变量
全局数组变量,通过extern方式声明于main.c中,定义却在drivers.c中