wpa_supplicant是用来用来支持无线中各种加密方式的,包括WEP、WPA/WPA2和WAPI(中国特有)、EAP(8021x)。wpa_s通过socket与上层(framework)和底层(driver)通信,向上接收命令和传递当前状态,向下发送命令到驱动并接收驱动上传的各种event,严格来讲wap_s和driver中还有一层cfg80211,cfg80211可以理解为linux定义的80211管理控制层的框架,例如扫描、连接这些通用的过程,各个厂商按照cfg80211提供的框架编写各自的驱动,实现具体的帧发送与接收。wpa_s是如何处理各个socket中数据的请看 eloop sun章节。
下图是网页(http://zwz94.blog.163.com/blog/static/3206039520120149580531/)上的一张图片,可以清晰的看到wpa_s早整个wifi架构中的位置
在Android手机中,wpa_supplicant的启动是由framework控制的,frameowork设置property来启动写在init rc文件的中service, 同时会生成两条socket,一条向wpa_s发送命令,一条接收wpa_s上传的event
service p2p_supplicant /system/bin/wpa_supplicant \
-ip2p0 -Dnl80211 -c/data/misc/wifi/p2p_supplicant.conf \
-I/system/etc/wifi/p2p_supplicant_overlay.conf -N \
-iwlan0 -Dnl80211 -c/data/misc/wifi/wpa_supplicant.conf \
-I/system/etc/wifi/wpa_supplicant_overlay.conf \
-O/data/misc/wifi/sockets -puse_p2p_group_interface=1 -dd \
-e/data/misc/wifi/entropy.bin -g@android:wpa_wlan0
class main
socket wpa_wlan0 dgram 660 wifi wifi
disabled
oneshot
其中 socket wpa_wlan0 dgram 660 wifi wifi创建了一个socket,wpa_s使用该socket接收framework的命令和向上传递event。framework同样会调用连接该socket,后面会讲到
wpa_s允许传入很多参数, 参数区分大小写
参数一共有3中类型,
有 –BdhKLqstuvW
-h:帮助文件
-L:输出license
-q:提升wpa_s调试级别(输出log减少),与-d相反
-v:输出wpa_s版本号
-W:等到control interface monitor再运行
-N:当需要设置两个network interface时,需要在两个网口参数中间插入 –N, 例如 一个网口为p2p0,需要插入-N后,再新添加另一个wlan0
wpa_supplicant通过为每一个网络接口设置不同的参数的方式,来实现对多网卡的支持。
结构体 wpa_params在调用 wpa_supplicant_init() 初始化wpa_s使用,控制wpa_s运行
struct wpa_params {
//-B:当做守护进程运行在后台
int daemonize;
//-W:等到control interface monitor再运行
int wait_for_monitor;
//-P: pid_file - Path to a PID (process ID) file
char *pid_file;
//-d:输出log的级别,-d为默认级别,-dd级别降低一级(输出log会增加一级),依次类推,设置代码为params.wpa_debug_level
int wpa_debug_level;
//-K:在log中打印key
int wpa_debug_show_keys;
//-t:调试信息中增加时间戳
int wpa_debug_timestamp;
//ctrl_interface - Global ctrl_iface path/parameter
//-g:global ctrl_interface(字符串为@android+SOCK_NAME, SOCK_NAME是由init.rc)
char *ctrl_interface;
//ctrl_interface_group - Global ctrl_iface group
//-G:global ctrl_interface group
char *ctrl_interface_group;
//-u:支持dbus控制接口
int dbus_ctrl_interface;
//-f:将log输出到file中
const char *wpa_debug_file_path;
//-s:输出log到 syslog, 默认输出到stdout
int wpa_debug_syslog;
//-T:log增加linux trace
int wpa_debug_tracing;
//-o:设置linux网口的目录
char *override_driver;
//-O:设置wpa_s控制sockets的目录
char *override_ctrl_interface;
//-e:entropy_file - Optional entropy file
char *entropy_file;
};
存放到结构体 struct wpa_interface 中,运行wpa_supplicant_add_iface()需要传入该结构体
struct wpa_interface {
//-c:conf配置文件,里面定义了一些参数和一些network节点,一般为wpa_supplicant.conf
const char *confname;
//-I:conf配置文件的补充选项,一般为wpa_supplicant_overlay.conf/
const char *confanother;
//-m:p2p网口的配置文件
const char *conf_p2p_dev;
//-C:控制端口socket的参数
//功能等同conf 文件
const char *ctrl_interface;
//-D:driver的类型,可以设置多个,例如 nl80211,wext
const char *driver;
//driver_param - Driver interface parameters
const char *driver_param;
//-i:linux的网口,可以定义多个,例如wlan0
const char *ifname;
//-b:桥接的interface
const char *bridge_ifname;
//p2p_mgmt - Interface used for P2P management (P2P Device operations)
int p2p_mgmt;
};
主要包括 EAP各种方法的注册,eloop(wpa_s运行的主体)参数,与framework通信socket的初始化
struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
{
//全局信息,params中的内容都会拷贝到该结构体中
struct wpa_global *global;
//初始化eap方法,每种eap方式都有对应的方法
ret = eap_register_methods();
//将params中的信息复制到global中
global = os_zalloc(sizeof(*global));
//初始化 struct eloop_data,该结构体是个全局变量
if (eloop_init())
random_init(params->entropy_file);
//连接与FWKS通信socket(init.rc中定义的),并注册接收cmd的函数
global->ctrl_iface = wpa_supplicant_global_ctrl_iface_init(global);
{
//初始化控制socket
wpas_global_ctrl_iface_open_sock(global, priv) < 0)
{
//获取-g传进来的socket名称,与init.rc中相对应
os_strncmp(ctrl, "@android:", 9) == 0) {
priv->sock = android_get_control_socket(ctrl + 9);
//注册接收socket数据的函数,分发处理传入的命令
eloop_register_read_sock(priv->sock, wpa_supplicant_global_ctrl_iface_receive, global, priv);
//注册发送event到FWKS的函数wpa_supplicant_ctrl_iface_send
wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
}
}
return global;
}
wpa_supplicant_global_ctrl_iface_receive()可以接受两类命令,接口命令和全局命令。一类命令前面有“IFNAME= ”指明处理该命令的接口,然后调用每个接口对应的 wpa_supplicant_ctrl_iface_process()来进行相应处理。另一类未指定接口,由wpa_supplicant_global_ctrl_iface_process()直接处理。
framework新建了两条socket,并与init rc中的socket相关联,一条用于发送CMD,一条用接收event
代码在wifi.c中。
//path 内容为"@android:wpa_wlan0", wpa_wlan0是由init.rc启动的socket
int wifi_connect_on_socket_path(const char *path)
{
ctrl_conn = wpa_ctrl_open(path);
monitor_conn = wpa_ctrl_open(path);
}
在该函数中会调用wpa_ctrl_open()在wpa_s中,具体操作如下
static struct wpa_ctrl *ctrl_conn = wpa_ctrl_open(path);
{
//新建一个UNIX域的socket
struct wpa_ctrl *ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0);
//经socket与文件系统的地址绑定,就是手机目录下的 /data/misc/wifi/sockets
os_snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path), CONFIG_CTRL_IFACE_CLIENT_DIR "/" CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d", (int) getpid(), counter);
bind(ctrl->s, (struct sockaddr *) &ctrl->local, sizeof(ctrl->local))
//将新创建的socket和init.rc启动的socket连接起来
socket_local_client_connect( ctrl->s, ctrl_path + 9, NDROID_SOCKET_NAMESPACE_RESERVED, SOCK_DGRAM)
}
wifi_send_command()用于向wpa_s发送命令
wifi_wait_on_socket()接收wpa_s向上传递的event