Linux应用程序获取hostapd热点状态

在hostapd的官网有一些关于应用程序访问hostapd简单的介绍。主要说的就是通过wpa_ctrl.c和wpa_ctrl.h中的接口进行访问。把这两个文件放入到c程序中就可以了,但是这两个文件很难放入到我们的工程源码中,因为这两个文件引用了hostapd源码中其他很多文件,不能单独使用。

关于官网对hostapd如何使用,我把它贴在这里,详细的可以直接访问官网:

http://w1.fi/wpa_supplicant/devel/hostapd_ctrl_iface_page.html

hostapd control interface

hostapd implements a control interface that can be used by external programs to control the operations of the hostapd daemon and to get status information and event notifications. There is a small C library, in a form of a single C file, wpa_ctrl.c, that provides helper functions to facilitate the use of the control interface. External programs can link this file into them and then use the library functions documented in wpa_ctrl.h to interact with wpa_supplicant. This library can also be used with C++. hostapd_cli.c is an example program using this library.

There are multiple mechanisms for inter-process communication. For example, Linux version of hostapd is using UNIX domain sockets for the control interface. The use of the functions defined in wpa_ctrl.h can be used to hide the details of the used IPC from external programs.

 Using the control interface

External programs, e.g., a GUI or a configuration utility, that need to communicate with hostapd should link in wpa_ctrl.c. This allows them to use helper functions to open connection to the control interface with wpa_ctrl_open() and to send commands with wpa_ctrl_request().

hostapd uses the control interface for two types of communication: commands and unsolicited event messages. Commands are a pair of messages, a request from the external program and a response from hostapd. These can be executed using wpa_ctrl_request(). Unsolicited event messages are sent by hostapd to the control interface connection without specific request from the external program for receiving each message. However, the external program needs to attach to the control interface with wpa_ctrl_attach() to receive these unsolicited messages.

If the control interface connection is used both for commands and unsolicited event messages, there is potential for receiving an unsolicited message between the command request and response. wpa_ctrl_request() caller will need to supply a callback, msg_cb, for processing these messages. Often it is easier to open two control interface connections by calling wpa_ctrl_open() twice and then use one of the connections for commands and the other one for unsolicited messages. This way command request/response pairs will not be broken by unsolicited messages. wpa_cli.c is an example of how to use only one connection for both purposes and wpa_gui demonstrates how to use two separate connections.

Once the control interface connection is not needed anymore, it should be closed by calling wpa_ctrl_close(). If the connection was used for unsolicited event messages, it should be first detached by calling wpa_ctrl_detach().

 关于wpa_ctrl中的接口正常的思维应该是工程中产生一个库文件,供用户调用,但是hostapd却没有。不要着急,虽然hostapd没有提供,但是wpa_supplicant工程却提供了.so动态库,这个.so动态库主要的源码就是wpa_ctrl.c中的源码。hostapd和wpa_supplicant是一个开源工程,他们的wpa_ctrl.c的源码也是完全一样的。

在wpa_supplicant工程中可以配置生成wpa_client.so动态库,这样在编译我们自己应用程序的时候,就可以通过这个动态库。这个动态库实现了wpa_ctrl.h中所有的接口。

如果你在使用的是buildroot,那么可以使能如下选项:

嵌入式Linux热点分享网络(五)_热点

 BR2_PACKAGE_WPA_SUPPLICANT_WPA_CLIENT_SO:                                                                                                                                                           │  
  │                                                                                                                                                                                                     │  
  │ Install libwpa_client.so.                                                                                                                                                                           │  
  │                                                                                                                                                                                                     │  
  │ Symbol: BR2_PACKAGE_WPA_SUPPLICANT_WPA_CLIENT_SO [=y]                                                                                                                                               │  
  │ Type  : bool                                                                                                                                                                                        │  
  │ Prompt: Install wpa_client shared library                                                                                                                                                           │  
  │   Location:                                                                                                                                                                                         │  
  │     -> Target packages                                                                                                                                                                              │  
  │       -> Networking applications                                                                                                                                                                    │  
  │         -> wpa_supplicant (BR2_PACKAGE_WPA_SUPPLICANT [=y])                                                                                                                                         │  
  │   Defined at package/wpa_supplicant/Config.in:96                                                                                                                                                    │  
  │   Depends on: BR2_PACKAGE_WPA_SUPPLICANT [=y] && !BR2_STATIC_LIBS [=n] 

下面是我写的一个小例子,供大家参考:

#include <stddef.h>
#include <wpa_ctrl.h>
#include <string.h>
#include <stdio.h>

/**
 *
 */
int strcpyline(char *line, int line_index, char *src)
{
	int i = 0;
	char *p = src;
	char *q = line;

	while (*p && i != line_index ) {
		if (*p == '\n') {
			i++;
		}
		p++;
	}

	if (*p) {
		while (*p && *p != '\n') {
			*q = *p;
			p++;
			q++;
		}
		*q = '\0';
	} else {
		return -1;
	}

	return 0;
}


void wifi_ap_info(void)
{
	struct wpa_ctrl *wpa_ctrl = wpa_ctrl_open("/var/run/hostapd/wlan0");

	if (wpa_ctrl == NULL) {
		printf("wpa_ctrl NULL\n");
	}

	char cmd[32];
	char reply[4096];

	strcpy(cmd, "STA-FIRST");
	unsigned int len = 4095;
	wpa_ctrl_request(wpa_ctrl, cmd, strlen(cmd), reply, &len, 0);
	char addr[32];
	
	printf("reply: %s\n", reply);
	strcpyline(addr, 0, reply);


	sprintf(cmd, "STA-NEXT %s", addr);
	len = 4095;
	wpa_ctrl_request(wpa_ctrl, cmd, strlen(cmd), reply, &len, 0);
	printf("reply: %s\n", reply);
	strcpyline(addr, 0, reply);

	addr[0] = '5';
	sprintf(cmd, "STA-NEXT %s", addr);
	len = 4095;
	wpa_ctrl_request(wpa_ctrl, cmd, strlen(cmd), reply, &len, 0);
	printf("reply: %s\n", reply);
	strcpyline(addr, 0, reply);

	sprintf(cmd, "STA-NEXT %s", addr);
	len = 4095;
	wpa_ctrl_request(wpa_ctrl, cmd, strlen(cmd), reply, &len, 0);
	printf("reply: %s\n", reply);
	strcpyline(addr, 0, reply);


	wpa_ctrl_close(wpa_ctrl);
}


/**
 * 获取当前连接到AP所有终端的个数和mac地址
 * @_addrs: 用于存储获取的mac地址数据,例如addr[20][32]
 * @line: _addrs行数
 * @line_size: _addrs单行缓冲区大小
 * @result_cnt: 获取到终端的个数
 * @return: 0 - 成功, 其他失败
 */
int wifi_ap_get_sta_addr(void *_addrs, int line, int line_size, int *result_cnt)
{
	struct wpa_ctrl *wpa_ctrl = wpa_ctrl_open("/var/run/hostapd/wlan0");

	if (!wpa_ctrl) {
		printf("Err: wpa_ctrl_open()\n");
		return -1;
	}

	char *addrs = (char *)_addrs;
	int cnt = 0;
	int ret = 0;
	char cmd[32];
	char reply[4096];
	unsigned int len;
	char addr[32];

	strcpy(cmd, "STA-FIRST");
	len = 4095;
	ret = wpa_ctrl_request(wpa_ctrl, cmd, strlen(cmd), reply, &len, 0);

	if (ret == 0 && len > 0 && strncmp("FAIL", reply, 4) != 0) {
		if (strcpyline(addr, 0, reply) < 0) {
			ret = -1;
			goto ERROR;
		} 
		strcpy(addrs + (cnt * line_size), addr);
		cnt++;
	} else {
		ret = -1;
		goto ERROR;
	}


	while (1) {
		sprintf(cmd, "STA-NEXT %s", addr);
		len = 4095;
		ret = wpa_ctrl_request(wpa_ctrl, cmd, strlen(cmd), reply, &len, 0);

		/*
		 * ret == 0 && len == 0 已经没有更多数据了
		 */
		if (ret ==0 && len > 0 && strncmp("FAIL", reply, 4) != 0) {
			if (strcpyline(addr, 0, reply) < 0) {
				break;
			} 

			strcpy(addrs + (cnt * line_size), addr);
			cnt++;

			if (cnt == line) {
				break;
			}
		} else {
			break;
		}
	}

ERROR:
	wpa_ctrl_close(wpa_ctrl);
	*result_cnt = cnt;
	return ret;
}

/** 
 * 执行一条命令
 * @cmd: 需要执行命令
 * @result: 用于存储命令执行后返回的数据
 * @len: 传入result缓冲区大小, 传出命令执行后实际存储到result的数据大小
 * @return: 0-成功, 其他失败
 * 可以执行哪些命令参考: hostapd源码包中 hostapd/hostapd_cli.c: hostapd_cli_commands[]
 * 常用的命令有:
 *  "GET_CONFIG"
 *  "STATUS"
 *  "STATUS-DRIVER"
 *  "DEAUTHENTICATE"
 *  
 */
int wifi_ap_run_cmd(char *cmd, char *result, unsigned int *len)
{
	struct wpa_ctrl *wpa_ctrl = wpa_ctrl_open("/var/run/hostapd/wlan0");

	if (!wpa_ctrl) {
		printf("Err: wpa_ctrl_open()\n");
		return -1;
	}

	int ret = wpa_ctrl_request(wpa_ctrl, cmd, strlen(cmd), result, len, 0);
	result[*len] = 0;

	wpa_ctrl_close(wpa_ctrl);

	return ret;
}



void test_wpa_ctrl(void)
{
	//wifi_ap_info();
	char addrs[20][32];

	int cnt;
	wifi_ap_get_sta_addr(addrs, 20, 32, &cnt);

	printf("cnt = %d\n", cnt);

	int i;

	for (i = 0; i < cnt; i++) {
		printf("[%d] %s\n", i, addrs[i]);
	}

	char result[512];
	unsigned int len = 512;
	wifi_ap_run_cmd("GET_CONFIG", result, &len);

	printf("get_config: %s\n", result);
}

对于wifi sta的管理与hostapd的方式是一样的,只是使用的命令不同。