原文连接:http://blog.csdn.net/qq_21949217/article/details/46004349网络
最近在作一个基于OpenFlow 协议的无线AP 的项目,因而就分析了hostapd 的源代码,并在原有的基础上添加上咱们的代码。通过近半个月的调试和分析,算是基本上搞清楚了hostapd 的运做机制。鉴于网上对于hostapd 的具体资料甚是稀少,因此笔者在此整理学习笔记并在网上与各位读者分享,但愿能对读者们有帮助。若是有分析不恰当或者错误的地方,也欢迎各位指正。另外,本文是在读者已经具备IEEE 802.11 和良好C 语言的基础的假设上写的,所以,本文将直奔主题,对于IEEE 802.11 和C 代码细节将再也不赘述。 数据结构
还有,笔者在分析hostapd 源代码的时候,hostapd/supplicant 开发文档也提供了大量的有用信息,各位读者也能够参考。(http://w1.fi/wpa_supplicant/devel/)函数
这次分析hostapd 源代码,发现了其中出现频率最高的两个数据结构——struct hostapd_iface 和struct hostapd_data。其实很简单,hostapd_iface 结构体描述了一个物理接口(好比wlan0),hostapd_data 则描述一个BSS。这两个数据结构几乎贯穿全部的源代码,因此必定要搞清楚这两个数据结构。学习
说到这里,也许有人会问,实际源文件中有数十个字段,难道只知道这些就足够了吗?个人答案是确定的——是的,只须要知道这么多。加密
首先,咱们打开hostapd/main.c,并找到主程序入口main()。直接跳转到大概659 行。看到以下代码:spa
for (i = 0; i < interfaces.count; i++) { interfaces.iface[i] = hostapd_interface_init(&interfaces,argv[optind + i],debug); if (!interfaces.iface[i]) { wpa_printf(MSG_ERROR, "Failed to initialize interface"); goto out; } }
很明显, 这是对每一个网络接口( 或者干脆叫物理网卡也行) 的初始化。而后找到hostapd_interface_init 的定义(大概从main.c 的第234 行开始),咱们看到了以下的一句话:.net
iface = hostapd_init(interfaces, config_fname);
嗯,看来,真正的初始化工做是在hostapd_init 函数中进行,并返回一个描述网络接口的结构体(struct hostapd_iface)。而后咱们再深一层的挖掘,找到hostapd_init 的定义(从src/ap/hostapd.c 的第1416 行起)。咱们先看那句debug
conf = interfaces->config_read_cb(hapd_iface->config_file);</span>
这个其实就是读取hostapd 的配置文件hostapd.conf 中的配置信息,并保存到hostapd_conf的结构体中去。以后紧接着就是为每一个BSS 分配内存空间:调试
1 hapd_iface->num_bss = conf->num_bss; //从配置信息中获取BSS 的个数 2 hapd_iface->bss = os_calloc(conf->num_bss, sizeof(struct hostapd_data *)); //分配BSS 列表空间 3 if (hapd_iface->bss == NULL) //内存空间分配失败 4 goto fail; 5 for (i = 0; i < conf->num_bss; i++) { 6 hapd = hapd_iface->bss[i] = hostapd_alloc_bss_data(hapd_iface, conf, conf->bss[i]); //初始化每一个BSS 数据结构 7 if (hapd == NULL) //内存空间分配失败 8 goto fail; 9 hapd->msg_ctx = hapd; //这句不知道啥意思,不过也无妨 10 }
OK,到这里,网络接口和每一个BSS 的基本初始化(即为它们分配内存)的工做结束了。回到main,跳转到第715 行,咱们看到了这句话:code
1 if (hostapd_driver_init(interfaces.iface[i])||hostapd_setup_interface(interfaces.iface[i])) 2 goto out;
从字面上理解,那就是“初始化每一个网络接口的驱动程序”和“设置每一个网络接口”。好吧,咱们先看hostapd_driver_init 函数是如何定义的。最关键的是在第185 行到第204 行。代码以下:
params.bssid = b; //BSSID params.ifname = hapd->conf->iface; //网络接口名称(好比wlan0) params.ssid = hapd->conf->ssid.ssid; //SSID params.ssid_len = hapd->conf->ssid.ssid_len; //SSID 长度 …………(这部分不重要。好吧,我认可我也不清楚是干吗的……) params.own_addr = hapd->own_addr; //网络接口的MAC 地址 hapd->drv_priv = hapd->driver->hapd_init(hapd, ¶ms); //将以上参数传递给驱动程序
那么,问题来了,hapd_init 是怎么定义的呢?因为笔者分析的hostapd 是基于nl80211 的,因此hapd_init 指向nl80211 的初始化函数i802_init(定义在src/drivers/nl80211_driver.c 中,本文只分析hostapd 在用户空间的工做原理,至于内核空间是如何工做的,不在本文讨论之列。有兴趣的读者能够查找有关Netlink 和mac80211 的资料。笔者之后也会推出关于mac80211 的学习笔记)。好了,驱动程序初始化完了,下面咱们看如何设置每一个网络接口。hostapd_setup_interface 函数定义在src/ap/hostapd.c,打开这个文件,跳到大概1315 行,里面就一句setup_interface……好吧,找到大概第1040 行,就是这个函数的定义。里面有一段话是这么说的:
1 /* 2 * Make sure that all BSSes get configured with a pointer to the same 3 * driver interface. 4 */ 5 for (i = 1; i < iface->num_bss; i++) { 6 iface->bss[i]->driver = hapd->driver; 7 iface->bss[i]->drv_priv = hapd->drv_priv; 8 }
还好有注释,就是确保每一个BSS 都使用和物理网卡(我喜欢称呼BSS 为虚拟网卡)同一套驱动程序接口。而后直接看return 吧,哎?怎么又有个setup_interface2?好吧,去看看setup_interface2 是怎么回事……找到大概第1113 行,就是它的定义了。这里面主要是设置网卡的硬件模式( a,g,n 等等), 再看return , 我去, 咋还有个hostapd_setup_interface_complete 呢?( 程序做者真磨叽) 好吧,既然说了complete,那应该就是最后一步了吧?那我就硬着头皮继续看看——跳到大概1162 行,就找到了它的定义。这个过程里面主要是设置网卡的信道,速率(根据硬件模式),RTS 参数等等。最后经过hostapd_setup_bss 函数配置每一个BSS 。那咱们就再看看hostapd_setup_bss 是怎么个回事吧( 无语) — — 再跳到大概687 行, 就找到了hostapd_setup_bss 的定义。这个函数须要两个参数,第一个是hostapd_data 结构体,描述一个BSS 的配置信息,第二个参数first 则用来表示这个BSS 是否为第一个BSS(一般,第一个BSS 就是wlan0)。先看第一部分代码:
1 if (!first || first == -1) { //此BSS 不是第一个BSS 的状况 2 if (hostapd_mac_comp_empty(conf->bssid) == 0) { //配置文件没有为此BSS 指定BSSID 3 /* Allocate the next available BSSID. */ 4 do { 5 //将上一个BSS 的BSSID 增长1 作为这个BSS 的BSSID,而且反复这一步骤直到不与其余BSSID 重复为止 6 inc_byte_array(hapd->own_addr, ETH_ALEN); 7 } while (mac_in_conf(hapd->iconf, hapd->own_addr)); 8 } else { //配置文件已经为此BSS 指定BSSID 的状况 9 /* Allocate the configured BSSID. */ 10 os_memcpy(hapd->own_addr, conf->bssid, ETH_ALEN); 11 if (hostapd_mac_comp(hapd->own_addr, hapd->iface->bss[0]->own_addr) ==0) { 12 wpa_printf(MSG_ERROR, "BSS '%s' may not have " 13 "BSSID set to the MAC address of " 14 "the radio", conf->iface); 15 return -1; 16 } 17 } 18 hapd->interface_added = 1; //不知道干吗的 19 //在内核中为此BSS 建立一个interface(好比wlan0_0) 20 if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS, 21 conf->iface, hapd->own_addr, hapd, &hapd->drv_priv, force_ifname, if_addr, 22 conf->bridge[0] ? conf->bridge : NULL, first == -1)) { 23 wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID=" 24 MACSTR ")", MAC2STR(hapd->own_addr)); 25 hapd->interface_added = 0; 26 return -1; 27 } 28 }
再看第二部分:
1 …… //此部分是对这个BSS 的加密方式,SSID 等参数的设置 2 //将这个BSS 的interface 激活。(假设这个BSS 的interface 为wlan0_0 的话,那么这句话的意思就至关于在终端下执行“ifconfig wlan0_0 up”命令) 3 if (hapd->driver && hapd->driver->set_operstate) 4 hapd->driver->set_operstate(hapd->drv_priv, 1); 5 return 0;
啊,终因而return 0 了。好了,从新回到main。至此,网络接口和每一个BSS 的初始化工做完成了。
未完待续……下一篇将详细讲解hostapd的工做机制。