您的位置:首页 > 其它

一、无线信息传递——user space下的hostapd

2016-09-02 14:56 591 查看

系列说明

  大致简单了解了无线通信在底层的组成,接收和发送之后,接下来希望能更系统地对信息从user space至kernel space,至kernel对信息的使用,发送和接收等一系列步骤进行总结说明。以便后续将wifi的ssid,密码,加密方式等需要的信息填充入beacon帧中进行发送端组包,接收端解包的处理。

  这一章先从user space层的hostapd开始说起,主要目的在于了解hostapd是怎么将hostapd.conf配置文件下的配置内容传递给底层,让底层根据这些信息做相应处理的。

  以下链接是对我了解hostapd过程中起到很大帮助的网址链接:

hostapd源代码分析(一):网络接口和BSS的初始化

hostapd源代码分析(二):hostapd的工作机制

hostapd源代码分析(三):管理帧的收发和处理

wifi相关知识点网址链接

  该篇主要对:hostapd作用,hostapd网络接口和BSS初始化,hostapd信息传输机制,socket注册回调。三个模块进行说明。

一、hostapd作用

  hostapd 是一个用户态用于AP和认证服务器的守护进程。它实现了IEEE 802.11相关的接入管理,IEEE 802.1X/WPA/WPA2/EAP 认证, RADIUS客户端,EAP服务器和RADIUS 认证服务器。Linux下支持的驱动有:Host AP,madwifi,基于mac80211的驱动。

  从上面的一段话中可以了解到,hostapd仅是用于当设备处于ap模式时,进行设备底层的初始化,对需要连接该热点的设备认证及管理。并没有参与相关底层协议帧的组合和发送。

二、hostapd的网络接口和BSS初始化

2.1、简要说明

  在该模块中,hostapd主要进行网络接口及网络网卡的初始化。初始化相应的接收回调函数,网卡相关执行信息的设置以及创建与kernel space层的socket句柄。

2.2、hostapd初始化

  了解hostapd初始化之前首先了解以下两个结构体,因为这两个结构体在hostapd中使用的次数最多,也就是扮演的角色最关键。

/*--------------hostapd中两个关键的结构体----------*/
/**
* struct hostapd_iface - hostapd per-interface data structure
*/
struct hostapd_iface {
struct hostapd_config *conf: 保存对网络接口的配置(从配置文件hostapd.conf中加载)
sizt_t num_bss: 此接口下辖的BSS 个数
struct hostapd_data **bss:BSS 列表,描述各BSS 的配置
。。。
};

/**
* struct hostapd_data - hostapd per-BSS data structure
*/
struct hostapd_data {
struct hostapd_bss_config *conf:保存BSS 的配置信息(从配置文件hostapd.conf 中加载)
u8 own_addr[ETH_ALEN]:表示此BSS 的BSSID (ETH_ALEN 为6,u8 其实是unsigned char)
struct wpa_driver_ops *driver:指向一组驱动程序接口,用来和内核交互。(这里是用的nl80211)
。。。
};

//!!!以上结构体中写出的是最主要的结构体变量,其他的结构体变量暂时不需要重点了解。


  接下来观察main函数中的几个重要的初始化。将以main中接口为入口进行展开说明。

/*----------------hostapd的main函数------------------*/
int main(int argc, char** argv)
{
...

if (hostapd_global_init(&interfaces, entropy_file)) //global的初始化
return -1;

/* Initialize interfaces */
for (i = 0; i < interfaces.count; i++)
{
interfaces.iface[i] = hostapd_interface_init(&interfaces,   //对每个网络接口(物理网卡)初始化
argv[optind + i],
debug);
if (!interfaces.iface[i])
goto out;
}

if (hostapd_global_run(&interfaces, daemonize, pid_file))       //下一模块再进行说明
goto out;
...

}


/*----------------hostapd_global_init函数---------------*/
//global 初始化接口
static int hostapd_global_init(struct hapd_interfaces *interfaces,
const char *entropy_file)
{
int i;
os_memset(&global, 0, sizeof(global));
hostapd_logger_register_cb(hostapd_logger_cb);
if (eap_server_register_methods()) {                    //初始化认证服务器算法
wpa_printf(MSG_ERROR, "Failed to register EAP methods");
return -1;
}
if (eloop_init()) {                             //eloop初始化 主要为初始化eloop_data结构体
wpa_printf(MSG_ERROR, "Failed to initialize event loop");
return -1;
}
random_init(entropy_file);
eloop_register_signal_terminate(handle_term, interfaces);       //相关信号处理回调初始化
for (i = 0; wpa_drivers[i]; i++)
global.drv_count++;
if (global.drv_count == 0) {
wpa_printf(MSG_ERROR, "No drivers enabled");
return -1;
}
global.drv_priv = os_calloc(global.drv_count, sizeof(void *));      //申请driver驱动私有数据空间
if (global.drv_priv == NULL)
return -1;
return 0;
}


/*-------------hostapd_interface_init函数--------------*/
//hostapd_interface_init:网卡初始化
static struct hostapd_iface *
hostapd_interface_init(struct hapd_interfaces *interfaces,
const char *config_fname, int debug)
{
struct hostapd_iface *iface;
int k;
wpa_printf(MSG_ERROR, "Configuration file: %s", config_fname);
iface = hostapd_init(config_fname);             //用于初始化网卡,iface为hostapd_init的(struct hostapd_iface结构体)返回值
if (!iface)
return NULL;
iface->interfaces = interfaces;
for (k = 0; k < debug; k++) {
if (iface->bss[0]->conf->logger_stdout_level > 0)
iface->bss[0]->conf->logger_stdout_level--;
}
if (iface->conf->bss[0].iface[0] != 0 ||
hostapd_drv_none(iface->bss[0]))
{
if (hostapd_driver_init(iface) ||           //初始化网络驱动
hostapd_setup_interface(iface)) {       //设置网络接口
hostapd_interface_deinit_free(iface);
return NULL;
}
}
return iface;
}


/*---------------hostapd_init函数----------------*/
/**
* hostapd_init - Allocate and initialize per-interface data
* @config_file: Path to the configuration file
* Returns: Pointer to the allocated interface data or %NULL on failure
*
* This function is used to allocate main data structures for per-interface
* data. The allocated data buffer will be freed by calling
* hostapd_cleanup_iface().
*/
static struct hostapd_iface * hostapd_init(const char *config_file)
{
struct hostapd_iface *hapd_iface = NULL;
struct hostapd_config *conf = NULL;
struct hostapd_data *hapd;
size_t i;
hapd_iface = os_zalloc(sizeof(*hapd_iface));
if (hapd_iface == NULL)
goto fail;
hapd_iface->config_fname = os_strdup(config_file);
if (hapd_iface->config_fname == NULL)
goto fail;
conf = hostapd_config_read(hapd_iface->config_fname);       //用于读取配置文件中的配置信息,一般的配置文件名(hostapd.conf)
if (conf == NULL)
goto fail;
hapd_iface->conf = conf;
hapd_iface->num_bss = conf->num_bss;                //从配置中获取BSS的个数
hapd_iface->bss = os_calloc(conf->num_bss,          //分配BSS列表空间
sizeof(struct hostapd_data *));
if (hapd_iface->bss == NULL)
goto fail;
for (i = 0; i < conf->num_bss; i++) {
hapd = hapd_iface->bss[i] =
hostapd_alloc_bss_data(hapd_iface, conf,    //初始化每个BSS数据结构
&conf->bss[i]);
if (hapd == NULL)
goto fail;
hapd->msg_ctx = hapd;
hapd->setup_complete_cb = hostapd_setup_complete_cb;
}
return hapd_iface;
fail:
if (conf)
hostapd_config_free(conf);
if (hapd_iface) {
os_free(hapd_iface->config_fname);
os_free(hapd_iface->bss);
os_free(hapd_iface);
}
return NULL;
}


/*-------------hostapd_driver_init函数---------------*/
static int hostapd_driver_init(struct hostapd_iface *iface)
{
struct wpa_init_params params;
size_t i;
struct hostapd_data *hapd = iface->bss[0];
struct hostapd_bss_config *conf = hapd->conf;
u8 *b = conf->bssid;
struct wpa_driver_capa capa;
if (hapd->driver == NULL || hapd->driver->hapd_init == NULL) {
wpa_printf(MSG_ERROR, "No hostapd driver wrapper available");
return -1;
}
/* Initialize the driver interface */
if (!(b[0] | b[1] | b[2] | b[3] | b[4] | b[5]))
b = NULL;
os_memset(¶ms, 0, sizeof(params));
for (i = 0; wpa_drivers[i]; i++) {
if (wpa_drivers[i] != hapd->driver)
continue;
if (global.drv_priv[i] == NULL && wpa_drivers[i]->global_init) {
global.drv_priv[i] = wpa_drivers[i]->global_init(); //初始化全局驱动私有数据
if (global.drv_priv[i] == NULL) {
wpa_printf(MSG_ERROR, "Failed to initialize "
"driver '%s'",
wpa_drivers[i]->name);
return -1;
}
}
params.global_priv = global.drv_priv[i]; //指针赋值
break;
}
params.bssid = b; //bssid地址
params.ifname = hapd->conf->iface; //网络接口名称(比如lan0)
params.ssid = hapd->conf->ssid.ssid; //ssid名称
params.ssid_len = hapd->conf->ssid.ssid_len; //ssid字节度
params.test_socket = hapd->conf->test_socket;
params.use_pae_group_addr = hapd->conf->use_pae_group_addr;
params.num_bridge = hapd->iface->num_bss;
params.bridge = os_calloc(hapd->iface->num_bss, sizeof(char *));
if (params.bridge == NULL)
return -1;
for (i = 0; i < hapd->iface->num_bss; i++) {
struct hostapd_data *bss = hapd->iface->bss[i];
if (bss->conf->bridge[0])
params.bridge[i] = bss->conf->bridge;
}
params.own_addr = hapd->own_addr; //网络接口mac地址
hapd->drv_priv = hapd->driver->hapd_init(hapd, ¶ms); //通过hapd_init将以上这些配置信息(params)传递给底层:后续积累netlink以及mac80211的相关操作
os_free(params.bridge);
if (hapd->drv_priv == NULL) {
wpa_printf(MSG_ERROR, "%s driver initialization failed.",
hapd->driver->name);
hapd->driver = NUL;
return -1;
}
if (hapd->driver->get_capa && hapd->driver->get_capa(hapd->drv_priv, &capa) == 0) {
iface->drv_flags = capa.flags;
iface->probe_resp_offloads = capa.probe_resp_offloads;
iface->extended_capa = capa.extended_capa;
iface->extended_capa_mask = capa.extended_capa_mask;
iface->extended_capa_len = capa.extended_capa_len;
}
return 0;
}


/*------------hostapd_setup_interface函数---------------*/
/**
* hostapd_setup_interface - Setup of an interface
* @iface: Pointer to interface data.
* Returns: 0 on success, -1 on failure
*
* Initializes the driver interface, validates the configuration,
* and sets driver parameters based on the configuration.
* Flushes old stations, sets the channel, encryption,
* beacons, and WDS links based on the configuration.
*/
int hostapd_setup_interface(struct hostapd_iface *iface)
{
int ret;
ret = setup_interface(iface); //设置BSS信息
if (ret) {
wpa_printf(MSG_ERROR, "%s: Unable to setup interface.",
iface->bss[0]->conf->iface);
return -1;
}
return 0;
}

static int setup_interface(struct hostapd_iface *iface)
{
struct hostapd_data *hapd = iface->bss[0];
size_t i;
char country[4];
/*
* Make sure that all BSSes get configured with a pointer to the same
* driver interface.
* 即用于确保每一个bss和网络网卡用于同一套驱动配置,以及相同的驱动私有数据
*/
for (i = 1; i < iface->num_bss; i++) {
iface->bss[i]->driver = hapd->driver;
iface->bss[i]->drv_priv = hapd->drv_priv;
}
#if !defined(CONFIG_WAPI) // Arthur can do BSSID range, disable bitmap check,  borrow CONFIG_WAPI to mark this change
if (hostapd_validate_bssid_configuration(iface))
return -1;
#endif
if (hapd->iconf->country[0] && hapd->iconf->country[1]) { //设置网卡country
os_memcpy(country, hapd->iconf->country, 3);
country[3] = '\0';
if (hostapd_set_country(hapd, country) < 0) {
wpa_printf(MSG_ERROR, "Failed to set country code");
return -1;
}
}
if (hostapd_get_hw_features(iface)) {
/* Not all drivers support this yet, so continue without hw
* feature data. */
} else {
int ret = hostapd_select_hw_mode(iface); //设置网卡模式
if (ret < 0) {
wpa_printf(MSG_ERROR, "Could not select hw_mode and "
"channel. (%d)", ret);
return -1;
}
if (ret == 1) {
wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback (ACS)");
return 0;
}
ret = hostapd_check_ht_capab(iface);
if (ret < 0)
return -1;
if (ret == 1) {
wpa_printf(MSG_DEBUG, "Interface initialization will "
"be completed in a callback");
return 0;
}
}
return hostapd_setup_interface_complete(iface, 0);
}

int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
{
//这里进行设置网卡信道,速率,RTS参数等
for (j = 0; j < iface->num_bss; j++) {
hapd = iface->bss[j];
if (j)
os_memcpy(hapd->own_addr, prev_addr, ETH_ALEN);
if (hostapd_setup_bss(hapd, j == 0)) //配置每个bss
goto error;
if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0)
prev_addr = hapd->own_addr;
}
hostapd_tx_queue_params(iface);
ap_list_init(iface); //ap列表初始化
if (hostapd_driver_commit(hapd) < 0) {
wpa_printf(MSG_ERROR, "%s: Failed to commit driver "
"configuration", __func__);
goto error;
}
/*
* WPS UPnP module can be initialized only when the "upnp_iface" is up.
* If "interface" and "upnp_iface" are the same (e.g., non-bridge
* mode), the interface is up only after driver_commit, so initialize
* WPS after driver_commit.
*/
for (j = 0; j < iface->num_bss; j++) {
if (hostapd_init_wps_complete(iface->bss[j]))
return -1;
}
if (hapd->setup_complete_cb)
hapd->setup_complete_cb(hapd->setup_complete_cb_ctx);
wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
iface->bss[0]->conf->iface);
return 0;
error:
wpa_printf(MSG_ERROR, "Interface initialization failed");
eloop_terminate();
return -1;
}

/**
* hostapd_setup_bss - Per-BSS setup (initialization)
* @hapd: Pointer to BSS data
* @first: Whether this BSS is the first BSS of an interface
*
* This function is used to initialize all per-BSS data structures and
* resources. This gets called in a loop for each BSS when an interface is
* initialized. Most of the modules that are initialized here will be
* deinitialized in hostapd_cleanup().
*/
//其中包含两个参数
//hapd为hostapd_data结构体,描述BSS信息
//first表示这个BSS是否为第一个BSS
static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
{
...
if (!first || first == -1) { //此BSS 不是第一个BSS 的情况
if (hostapd_mac_comp_empty(conf->bssid) == 0) { //配置文件没有为此BSS 指定BSSID
/* Allocate the next available BSSID. */
do {
inc_byte_array(hapd->own_addr, ETH_ALEN); //将上一个BSS 的BSSID 增加1 做为这个BSS 的BSSID,并且反复这一步骤直到不与其他BSSID 重复为止

} while (mac_in_conf(hapd->iconf, hapd->own_addr));
} else { //配置文件已经为此BSS 指定BSSID 的情况
/* Allocate the configured BSSID. */
os_memcpy(hapd->own_addr, conf->bssid, ETH_ALEN);
if (hostapd_mac_comp(hapd->own_addr, hapd->iface->bss[0]->own_addr) ==0) {
wpa_printf(MSG_ERROR, "BSS '%s' may not have "
"BSSID set to the MAC address of "
"the radio", conf->iface);
return -1;
}
}
hapd->interface_added = 1;
if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS, //在内核中为此BSS 创建一个interface(比如wlan0_0)
conf->iface, hapd->own_addr, hapd, &hapd->drv_priv, force_ifname, if_addr,
conf->bridge[0] ? conf->bridge : NULL, first == -1)) {
wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID="MACSTR ")", MAC2STR(hapd->own_addr));
hapd->interface_added = 0;
return -1;
}
...

//此部分是对这个BSS 的加密方式,SSID 等参数的设置
//将这个BSS 的interface 激活。(假设这个BSS 的interface 为wlan0_0 的话,那么这句话的意思就相当于在终端下执行“ifconfig wlan0_0 up”命令)
if (hapd->driver && hapd->driver->set_operstate)
hapd->driver->set_operstate(hapd->drv_priv, 1);
return 0;
}


三、hostapd的信号传递机制

  在上一模块,我们说明了hostapd的初始化,同时借此也对main函数中的接口调用有了一部分的认识。

  接下来,进行main函数最后一个关键接口的说明,即hostapd_global_run(),该接口处于main函数中的最后一阶段。

  在了解eloop代码之前,先对下面一副图进行简单的了解:



  从上图中可以看出,hostapd通过一个叫做“event_loop”的核心模块来处理来自各个模块的事件。之前以为hostapd与一般的进程一样,会使用多线程的方式进行各种小程序,各种认证程序的处理,但其实在上图中可以看出其实hostapd是以一个单线程的方式运行的。

  hostapd是通过socket来接受其他模块发来的消息的,并通过select()或者poll()系统调用来轮询各个socket的描述符,一旦发现众多socket描述符中有可用的描述符时,便调用相应的回调函数来处理相关事件



  先对hostapd消息处理机制中的几个关键结构体以及关键接口进行代码说明:

/*
* 文件:src/utils/eloop.c
*  hostapd信号传递关键结构体
*/

//eloop_sock表示一个注册的socket
struct eloop_sock {
int sock; //socket的描述符
void *eloop_data; //回调函数的第一个参数
void *user_data; //回调函数的第二个参数
eloop_sock_handler handler; //当事件发生时调用的回调函数入口
....
};

//eloop_sock_table表示已经注册的socket列表
struct eloop_sock_table {
int count; //已注册的socket个数
struct eloop_sock *table; //具体的socket注册信息(描述符,回调函数参数,回调函数入口等)
....
};

//eloop_data表示所有的socket事件
struct eloop_data {
int max_sock; //所有socket描述符中的最大值
int count; /* sum of all table counts */
struct eloop_sock_table readers; //socket“读事件”列表
struct eloop_sock_table writers; //socket“写事件”列表
struct eloop_sock_table exceptions;//socket“意外事件”列表
....
};


/*----------hostapd信号传递关键接口-------------*/
int eloop_register_sock(int sock, eloop_event_type type,
eloop_sock_handler handler,
void *eloop_data, void *user_data)
{
truct eloop_sock_table *table;
ssert(sock >= 0);
able = eloop_get_sock_table(type);
eturn eloop_sock_table_add_sock(table, sock, handler,
oop_data, user_data);
}//socket描述符从表中删除(一般在hostapd退出的时候调用)
void eloop_unregister_sock(int sock, eloop_event_type type)
{
truct eloop_sock_table *table;                 table = eloop_get_sock_table(type);
eloop_sock_table_remove_sock(table, sock);
} //把socket描述符和期相对应的回调函数注册到socket描述符表中去,//这个函数会调用eloop_register_sock函数,并设置EVENT_TYPE_READ标志,表示这个socket主要用于“读”(也即“接收”)。int eloop_register_read_sock(int sock, eloop_sock_handler handler,
void *eloop_data, void *user_data)
{
return eloop_register_sock(sock, EVENT_TYPE_READ, handler,
eloop_data, user_data);
} //解除“读”注册回调
void eloop_unregister_read_sock(int sock)
{
eloop_unregister_sock(sock, EVENT_TYPE_READ);
}


  接下来对hostapd代码说明:

int main(int argc, char** argv)
{
...                             //包含其他一些常用信号的注册处理,如ctrl+c的信号,超时信号等
hostapd_global_ctrl_iface_init(&interfaces);            //创建socket,用于其他模块的消息传递
if (hostapd_global_run(&interfaces, daemonize, pid_file))   //main函数中内容初始化完成后,执行while循环操作
goto out;
...
}


/*---------hostapd_global_ctrl_iface_init函数---------*/
int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface)
{
...

s = socket(PF_UNIX, SOCK_DGRAM, 0); //创建通信socket
if (s < 0) {
perror("socket(PF_UNIX)");
goto fail;
}
os_memset(&addr, 0, sizeof(addr));
#ifdef __FreeBSD__
addr.sun_len = sizeof(addr);
#endif /* __FreeBSD__ */
addr.sun_family = AF_UNIX;
fname = hostapd_global_ctrl_iface_path(interface);
if (fname == NULL)
goto fail;
os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { //bind 描述符
wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s",
strerror(errno));
if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { //connect
wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not"
" allow connections - assuming it was left"
"over from forced program termination");
if (unlink(fname) < 0) {
perror("unlink[ctrl_iface]");
wpa_printf(MSG_ERROR, "Could not unlink "
"existing ctrl_iface socket '%s'",
fname);
goto fail;
}
if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
perror("bind(PF_UNIX)");
goto fail;
}
wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
"ctrl_iface socket '%s'", fname);
} else {
wpa_printf(MSG_INFO, "ctrl_iface exists and seems to "
"be in use - cannot override it");
wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
"not used anymore", fname);
os_free(fname);
fname = NULL;
goto fail;
}
}
if (interface->ctrl_iface_group && chown(fname, -1, interface->ctrl_iface_group) < 0) {
perror("chown[ctrl_interface]");
goto fail;
}
if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
perror("chmod[ctrl_interface/ifname]");
goto fail;
}
os_free(fname);
interface->global_ctrl_sock = s;
eloop_register_read_sock(s, hostapd_global_ctrl_iface_receive, interface, NULL);
return 0;
fail:
if (s >= 0)
close(s);
if (fname) {
unlink(fname);
os_free(fname);
}
return -1;
}


/*-------------hostapd_global_run------------*/
static int hostapd_global_run(struct hapd_interfaces *ifaces, int daemonize,
const char *pid_file)
{
...

eloop_run();
return 0;
}

/*
说明:
*hostapd实际上是通过select()机制来轮询检查各个socket的状态,一旦发现某个socket描述符可读、可写、或者是意外的话,就会通过eloop_sock_table_dispach函数来调用相应的回调函数去处理相关事件。
*/
void eloop_run(void)
{
fd_set *rfds, *wfds, *efds; //读、写、意外文件描述符集合
struct timeval _tv;
int res;
struct os_reltime tv, now;
rfds = os_malloc(sizeof(*rfds));
wfds = os_malloc(sizeof(*wfds));
efds = os_malloc(sizeof(*efds));
if (rfds == NULL || wfds == NULL || efds == NULL)
goto out;
while (!eloop.terminate && (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 || eloop.writers.count > 0 || eloop.exceptions.count > 0)) { //执行eloop循环
struct eloop_timeout *timeout;
timeout = dl_list_first(&eloop.timeout, struct eloop_timeout, list);
if (timeout) {
os_get_reltime(&now);
if (os_reltime_before(&now, &timeout->time))
os_reltime_sub(&timeout->time, &now, &tv);
else
tv.sec = tv.usec = 0;
_tv.tv_sec = tv.sec;
_tv.tv_usec = tv.usec;
}

//!!!通过FD_SET宏设置“读”、“写”、“意外”的文件描述符集合
eloop_sock_table_set_fds(&eloop.readers, rfds);
eloop_sock_table_set_fds(&eloop.writers, wfds);
eloop_sock_table_set_fds(&eloop.exceptions, efds);

//!!!通过select()检查各个文件描述符的状态
res = select(eloop.max_sock + 1, rfds, wfds, efds, timeout ? &_tv : NULL);
if (res < 0 && errno != EINTR && errno != 0) {
wpa_printf(MSG_ERROR, "eloop: %s: %s", <span style="font-family: Arial, Helvetica, sans-serif;">"select"</span>, strerror(errno));
goto out;
}
eloop_process_pending_signals();

//检查是否有超时发生
timeout = dl_list_first(&eloop.timeout, struct eloop_timeout, list);
if (timeout) { //!!!如果有超时发生,执行相应的回调函数处理超时事件
os_get_reltime(&now);
if (!os_reltime_before(&now, &timeout->time)) {
void *eloop_data = timeout->eloop_data;
void *user_data = timeout->user_data;
eloop_timeout_handler handler = timeout->handler;
eloop_remove_timeout(timeout);
handler(eloop_data, user_data);
}
}
if (res <= 0)
continue;

//!!!处理各个socket的事件
eloop_sock_table_dispatch(&eloop.readers, rfds);
eloop_sock_table_dispatch(&eloop.writers, wfds);
eloop_sock_table_dispatch(&eloop.exceptions, efds);
}
eloop.terminate = 0;
out:
os_free(rfds);
os_free(wfds);
os_free(efds);
return;
}

//通过FD_ISSET宏进行判断是否有描述符被激活
static void eloop_sock_table_dispatch(struct eloop_sock_table *table,
fd_set *fds)
{
int i;
if (table == NULL || table->table == NULL)
return;
table->changed = 0;
for (i = 0; i < table->count; i++) { //检查socket表中每个描述符是否可用(读、写、意外)
if (FD_ISSET(table->table[i].sock, fds)) {

//!!!当某个socket描述符处于可用状态时,调用相应的回调函数来处理
table->table[i].handler(table->table[i].sock, table->table[i].eloop_data, table->table[i].user_data);
if (table->changed)
break;
}
}
}


四、hostapd管理帧处理回调样例

  上面介绍了hostapd自身的初始化和外部模块联系的信息传递机制,最后再通过hostapd对管理帧的处理简要说明在接收到外部传递过来的信息时的回调处理机制。

  首先,之前的初始化调用hostapd_interface_init进行网卡初始化,其中再调用hostapd_init接口,然后hostapd_config_read,最后至接口hostapd_config_fill中通过读取配置文件中的“driver”关键字。将结构体wpa_drivers赋值给conf->driver,而结构体wpa_drivers中包含函数指针hapd_init指向i802_init,该i802_init接口中进行调用wpa_driver_nl80211_set_mode,nl80211_setup_ap,nl80211_create_monitor_interface来建立监视接口。该监视接口建立以后,通过socket接收原始帧,并将socket注册到event loop(eloop)中。

  下图进行具体的监视接口在程序中的建立和注册流程说明:



  下面进行更加具体的代码说明流程:

/*---nl80211_create_monitor_interface---*/
static int
nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv)
{
char buf[IFNAMSIZ];
struct sockaddr_ll ll;
int optval;
socklen_t optlen;
if (drv->monitor_ifidx >= 0) {
drv->monitor_refcount++;
return 0;
}
if (os_strncmp(drv->first_bss.ifname, "p2p-", 4) == 0) {
/*
* P2P interface name is of the format p2p-%s-%d. For monitor
* interface name corresponding to P2P GO, replace "p2p-" with
* "mon-" to retain the same interface name length and to
* indicate that it is a monitor interface.
*/
snprintf(buf, IFNAMSIZ, "mon-%s", drv->first_bss.ifname + 4);
} else {
/* Non-P2P interface with AP functionality. */
snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss.ifname);
}
buf[IFNAMSIZ - 1] = '\0';
drv->monitor_ifidx =                                //通过netlink新建一个监听接口
nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL, 0);
if (drv->monitor_ifidx == -EOPNOTSUPP) {
/*
* This is backward compatibility for a few versions of
* the kernel only that didn't advertise the right
* attributes for the only driver that then supported
* AP mode w/o monitor -- ath6kl.
*/
wpa_printf(MSG_DEBUG, "nl80211: Driver does not support "
"monitor interface type - try to run without it");
drv->device_ap_sme = 1;
}
if (drv->monitor_ifidx < 0)
return -1;
if (linux_set_iface_flags(drv->global->ioctl_sock, buf, 1))
goto error;
memset(&ll, 0, sizeof(ll));
ll.sll_family = AF_PACKET;
ll.sll_ifindex = drv->monitor_ifidx;
drv->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));      //新建socket描述符
if (drv->monitor_sock < 0) {
perror("socket[PF_PACKET,SOCK_RAW]");
goto error;
}
if (add_monitor_filter(drv->monitor_sock)) {
wpa_printf(MSG_INFO, "Failed to set socket filter for monitor "
"interface; do filtering in user space");
/* This works, but will cost in performance. */
}
if (bind(drv->monitor_sock, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
perror("monitor socket bind");
goto error;
}
optlen = sizeof(optval);
optval = 20;
if (setsockopt(drv->monitor_sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) {
perror("Failed to set socket priority");
goto error;
}
if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read, drv, NULL)) {  //将原始套接字描述符和handle_monitor_read回调接口注册至eloop
printf("Could not register monitor read socket\n");
goto error;
}
return 0;
error:
nl80211_remove_monitor_interface(drv);
return -1;
}


/*----handle_monitor_read---*/
/*
*handle_monitor_read中,会根据IEEE80211_RADIOTAP_RX_FLAGS或者IEEE80211_RADIOTAP_TX_FLAGS标志来调用handle_frame或者handle_tx_callback函数来处理。
*一般handle_frame处理“请求”帧,比如probe request帧,authentication request帧,等等;handle_tx_callback一般用来处理“回应”帧,比如,authentication response帧,association response帧等
*/
static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx)
{
struct wpa_driver_nl80211_data *drv = eloop_ctx;
int len;
unsigned char buf[3000];
struct ieee80211_radiotap_iterator iter;
int ret;
int datarate = 0, ssi_signal = 0;
int injected = 0, failed = 0, rxflags = 0;
len = recv(sock, buf, sizeof(buf), 0); //接收原始套接字接收的帧数据
if (len < 0) {
perror("recv");
return;
}
if (ieee80211_radiotap_iterator_init(&iter, (void*)buf, len)) {
printf("received invalid radiotap frame\n");
return;
}
while (1) {
ret = ieee80211_radiotap_iterator_next(&iter); //抽取radiotap头
if (ret == -ENOENT)
break;
if (ret) {
printf("received invalid radiotap frame (%d)\n", ret);
return;
}
switch (iter.this_arg_index) {
case IEEE80211_RADIOTAP_FLAGS:
if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS)
len -= 4;
break;
case IEEE80211_RADIOTAP_RX_FLAGS: //接收帧(Rx),一般为请求帧
rxflags = 1;
break;
case IEEE80211_RADIOTAP_TX_FLAGS: //发送帧(Tx),一般为回应帧
injected = 1;
failed = le_to_host16((*(uint16_t *) iter.this_arg)) & IEEE80211_RADIOTAP_F_TX_FAIL;
break;
case IEEE80211_RADIOTAP_DATA_RETRIES:
break;
case IEEE80211_RADIOTAP_CHANNEL:
/* TODO: convert from freq/flags to channel number */
break;
case IEEE80211_RADIOTAP_RATE:
datarate = *iter.this_arg * 5;
break;
case IEEE80211_RADIOTAP_DBM_ANTSIGNAL:
ssi_signal = (s8) *iter.this_arg;
break;
}
}
if (rxflags && injected)
return;
if (!injected)
handle_frame(drv, buf + iter.max_length, len - iter.max_length, datarate, ssi_signal); //一般处理“请求”帧
else
handle_tx_callback(drv->ctx, buf + iter.max_length, len - iter.max_length, !failed); //一般用来处理“回应”帧
}


  进入handle_frame后,再调用wpa_supplicant_event(位于src/ap/drv_callbacks.c)来进一步处理。对于“请求帧”,会调用hostapd_mgmt_rx(位于src/ap/drv_callbacks.c)。

  hostapd_mgmt_rx的代码如下:

static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
{
struct hostapd_iface *iface = hapd->iface;
const struct ieee80211_hdr *hdr;
const u8 *bssid;
struct hostapd_frame_info fi;
int ret;
hdr = (const struct ieee80211_hdr *) rx_mgmt->frame;
bssid = get_hdr_bssid(hdr, rx_mgmt->frame_len); //获取该帧所属的BSSID
if (bssid == NULL)
return 0;
hapd = get_hapd_bssid(iface, bssid); //根据BSSID获取相应的BSS
if (hapd == NULL) { //相应的BSS不存在,则抛弃不处理。
u16 fc;
fc = le_to_host16(hdr->frame_control);
/*
* Drop frames to unknown BSSIDs except for Beacon frames which
* could be used to update neighbor information.
*/
if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON)
hapd = iface->bss[0];
else
return 0;
}
os_memset(&fi, 0, sizeof(fi));
fi.datarate = rx_mgmt->datarate;
fi.ssi_signal = rx_mgmt->ssi_signal;
if (hapd == HAPD_BROADCAST) { //广播帧
size_t i;
ret = 0;
//将广播帧发送给每一个BSS
for (i = 0; i < iface->num_bss; i++) {
/* if bss is set, driver will call this function for
* each bss individually. */
if (rx_mgmt->drv_priv && (iface->bss[i]->drv_priv != rx_mgmt->drv_priv))
continue;
if (ieee802_11_mgmt(iface->bss[i], rx_mgmt->frame, rx_mgmt->frame_len, &fi) > 0)
ret = 1;
}
} else //单播帧
ret = ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len, &fi); //然后继续调用ieee802_11_mgmt(位于src/ap/ieee80211.c),根据具体的帧来执行相应的操作
random_add_randomness(&fi, sizeof(fi));
return ret;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: