wpa_supplicant 接口及其应用

Posted by Vector Blog on September 26, 2016

在Android 平台中 WifiService 和 wpa_cli 都是 WAPS 的客户端, 他们在和 WPAS 交互时都会用到 wpa_supplicant 提供的接口。 这些接口全部声明在 wpa_ctrl.h 中,使用之前需要链接 libwpa_client.so 动态库, 另外需包含 wpa_ctrl.h 头文件。

LOCAL_SHARED_LIBRARIES += libwpa_client
#include "libwpa_client/wpa_ctrl.h"

// 连接 wpas , wpa_ctrl_open 等同于 wpa_ctrl_open2(ctrl_path, NULL)
struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path);
struct wpa_ctrl * wpa_ctrl_open2(const char *ctrl_path, const char *cli_path);

// 断开连接
void wpa_ctrl_close(struct wpa_ctrl *ctrl);

// 向 wpas 发送 command ,请求的事件通过 reply 返回
int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
		     char *reply, size_t *reply_len,
		     void (*msg_cb)(char *msg, size_t len));

// 设置接收 wpas unsolicited event
int wpa_ctrl_attach(struct wpa_ctrl *ctrl);
int wpa_ctrl_detach(struct wpa_ctrl *ctrl);

int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len);

int wpa_ctrl_pending(struct wpa_ctrl *ctrl);

int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl);

客户端使用wpa_ctrl时首先要连接 wpa_supplicant 获取到控制对象。 WPAS 回报的事件有两种:
solicited event(有请求的事件), 会通过 wpa_ctrl_request 的 reply 参数返回 ; 另一种是 unsolicited event(未请求的事件),在某些过程中 WPAS 可能会回报一些 unsolicited event, 而这些 unsolicited event 会在 wpa_ctrl_request 的 msg_cb 中处理, 但这些事件默认是不会监听的,需先调用 wpa_ctrl_attach 来设置监听。

以下以 wpa_cli 为例 :

static int wpa_cli_open_connection(const char *ifname, int attach)
{
	// 创建发送 command 的连接 , Android 中 cfile 为 /data/misc/wifi/sockets/wlan0
	ctrl_conn = wpa_ctrl_open2(cfile, client_socket_dir);

	// 创建监听 unsolicited event 的连接
	if (attach && interactive)
		mon_conn = wpa_ctrl_open2(cfile, client_socket_dir);
	else
		mon_conn = NULL;

	if (mon_conn) {
		// 设置监听 unsolicited event
		if (wpa_ctrl_attach(mon_conn) == 0) {
			wpa_cli_attached = 1;
			......
		} 
	}
	return 0;
}

下面再来看一下是如何向 wpas 发送 command :

static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
{
	char buf[4096];
	size_t len;
	int ret;

	if (ifname_prefix) {
		os_snprintf(buf, sizeof(buf), "IFNAME=%s %s",
			    ifname_prefix, cmd);
		buf[sizeof(buf) - 1] = '\0';
		cmd = buf;
	}
	len = sizeof(buf) - 1;
	ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len,
			       wpa_cli_msg_cb);
	
	if (print) {
		buf[len] = '\0';
		// buf 中会包含 solicited event
		printf("%s", buf);
		if (interactive && len > 0 && buf[len - 1] != '\n')
			printf("\n");
	}
	return 0;
}

这里的 cmd 为需要执行的 command , 例如 “scan” , “scan_results” 等, 相对应的 buf 中会被写入 command 的执行结果, 例如 “scan” 成功则直接返回 “OK” , 而 “scan_results” 会返回搜索到的 bssid 及信息。

再来看一下 unsolicited event 的监听 , 以下两个API用于 开启和关闭 unsolicited event 的监听:

int wpa_ctrl_attach(struct wpa_ctrl *ctrl); 
int wpa_ctrl_detach(struct wpa_ctrl *ctrl); 

而如果用户不想下 command ,只想去接收 unsolicited event ,可通过 wpa_ctrl_recv 函数

int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len);  

同样来看 wpa_cli 的代码 :

static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int action_monitor)
{
	if (ctrl_conn == NULL) {
		wpa_cli_reconnect();
		return;
	}
	// 检测是否有可读事件,否则阻塞
	while (wpa_ctrl_pending(ctrl) > 0) {
		char buf[4096];
		size_t len = sizeof(buf) - 1;
		if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
			// buf 中为 wpas 出来的 event
			buf[len] = '\0';
			......
			}
		} else {
			printf("Could not read pending message.\n");
			break;
		}
	}
	......
}