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

原文链接: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。这两个数据结构几乎贯穿所有的源代码,所以一定要搞清楚这两个数据结构。

  • struct hostapd_iface (src/ap/hostapd.h)。在这个数据结构中,主要有以下字段:

    • struct hostapd_config: 保存对网络接口的配置(从配置文件hostapd.conf中加载)
    • sizt_t num_bss: 此接口下辖的BSS 个数
    • struct hostapd_data **bss:BSS 列表,描述各BSS 的配置
  • struct hostapd_data (src/ap/hostapd.h)。在这个数据结构中,主要有以下字段:
    • 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)  

  说到这里,也许有人会问,实际源文件中有数十个字段,难道只知道这些就足够了吗?我的答案是肯定的——是的,只需要知道这么多。

二、hostapd 初始化

  首先,我们打开hostapd/main.c,并找到主程序入口main()。直接跳转到大概659 行。看到如下代码:

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 行开始),我们看到了如下的一句话:

iface = hostapd_init(interfaces, config_fname);

  嗯,看来,真正的初始化工作是在hostapd_init 函数中进行,并返回一个描述网络接口的结构体(struct hostapd_iface)。然后我们再深一层的挖掘,找到hostapd_init 的定义(从src/ap/hostapd.c 的第1416 行起)。我们先看那句

conf = interfaces->config_read_cb(hapd_iface->config_file);</span>

  这个其实就是读取hostapd 的配置文件hostapd.conf 中的配置信息,并保存到hostapd_conf的结构体中去。之后紧接着就是为每个BSS 分配内存空间:

 hapd_iface->num_bss = conf->num_bss; //从配置信息中获取BSS 的个数
hapd_iface->bss = os_calloc(conf->num_bss, sizeof(struct hostapd_data *)); //分配BSS 列表空间
if (hapd_iface->bss == NULL) //内存空间分配失败
goto fail;
for (i = ; i < conf->num_bss; i++) {
hapd = hapd_iface->bss[i] = hostapd_alloc_bss_data(hapd_iface, conf, conf->bss[i]); //初始化每个BSS 数据结构
if (hapd == NULL) //内存空间分配失败
goto fail;
hapd->msg_ctx = hapd; //这句不知道啥意思,不过也无妨
}

OK,到这里,网络接口和每个BSS 的基本初始化(即为它们分配内存)的工作结束了。回到main,跳转到第715 行,我们看到了这句话:

 if (hostapd_driver_init(interfaces.iface[i])||hostapd_setup_interface(interfaces.iface[i]))
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, &params); //将以上参数传递给驱动程序

那么,问题来了,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 行,就是这个函数的定义。里面有一段话是这么说的:

 /*
* Make sure that all BSSes get configured with a pointer to the same
* driver interface.
*/
for (i = ; i < iface->num_bss; i++) {
iface->bss[i]->driver = hapd->driver;
iface->bss[i]->drv_priv = hapd->drv_priv;
}

还好有注释,就是确保每个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)。先看第一部分代码:

 if (!first || first == -) { //此BSS 不是第一个BSS 的情况
if (hostapd_mac_comp_empty(conf->bssid) == ) { //配置文件没有为此BSS 指定BSSID
/* Allocate the next available BSSID. */
do {
//将上一个BSS 的BSSID 增加1 做为这个BSS 的BSSID,并且反复这一步骤直到不与其他BSSID 重复为止
inc_byte_array(hapd->own_addr, ETH_ALEN);
} 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[]->own_addr) ==) {
wpa_printf(MSG_ERROR, "BSS '%s' may not have "
"BSSID set to the MAC address of "
"the radio", conf->iface);
return -;
}
}
hapd->interface_added = ; //不知道干嘛的
//在内核中为此BSS 创建一个interface(比如wlan0_0)
if (hostapd_if_add(hapd->iface->bss[], WPA_IF_AP_BSS,
conf->iface, hapd->own_addr, hapd, &hapd->drv_priv, force_ifname, if_addr,
conf->bridge[] ? conf->bridge : NULL, first == -)) {
wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID="
MACSTR ")", MAC2STR(hapd->own_addr));
hapd->interface_added = ;
return -;
}
}

再看第二部分:

 …… //此部分是对这个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, );
return ;

啊,终于是return 0 了。好了,重新回到main。至此,网络接口和每个BSS 的初始化工作完成了。

未完待续……下一篇将详细讲解hostapd的工作机制。

 

  

hostapd源代码分析(一):网络接口和BSS的初始化的更多相关文章

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

    [转]hostapd源代码分析(二):hostapd的工作机制 原文链接:http://blog.csdn.net/qq_21949217/article/details/46004433 在我的上一 ...

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

    hostapd源代码分析(三):管理帧的收发和处理 原文链接:http://blog.csdn.net/qq_21949217/article/details/46004379 这篇文章我来讲解一下h ...

  3. 【转载】linux环境下tcpdump源代码分析

    linux环境下tcpdump源代码分析 原文时间 2013-10-11 13:13:02  CSDN博客 原文链接  http://blog.csdn.net/han_dawei/article/d ...

  4. linux环境下tcpdump源代码分析

    Linux 环境下tcpdump 源代码分析 韩大卫@吉林师范大学 tcpdump.c 是tcpdump 工具的main.c, 本文旨对tcpdump的框架有简单了解,只展示linux平台使用的一部分 ...

  5. android-plugmgr源代码分析

    android-plugmgr是一个Android插件加载框架,它最大的特点就是对插件不需要进行任何约束.关于这个类库的介绍见作者博客,市面上也有一些插件加载框架,但是感觉没有这个好.在这篇文章中,我 ...

  6. Twitter Storm源代码分析之ZooKeeper中的目录结构

    徐明明博客:Twitter Storm源代码分析之ZooKeeper中的目录结构 我们知道Twitter Storm的所有的状态信息都是保存在Zookeeper里面,nimbus通过在zookeepe ...

  7. 转:SDL2源代码分析

    1:初始化(SDL_Init()) SDL简介 有关SDL的简介在<最简单的视音频播放示例7:SDL2播放RGB/YUV>以及<最简单的视音频播放示例9:SDL2播放PCM>中 ...

  8. 转:RTMPDump源代码分析

    0: 主要函数调用分析 rtmpdump 是一个用来处理 RTMP 流媒体的开源工具包,支持 rtmp://, rtmpt://, rtmpe://, rtmpte://, and rtmps://. ...

  9. 转:ffdshow 源代码分析

    ffdshow神奇的功能:视频播放时显示运动矢量和QP FFDShow可以称得上是全能的解码.编码器.最初FFDShow只是mpeg视频解码器,不过现在他能做到的远不止于此.它能够解码的视频格式已经远 ...

随机推荐

  1. c#委托之最大

    public delegate int ceshi(object o1, object o2); static void Main(string[] args) { string[] a = { &q ...

  2. Java中的ClassLoader

      Java中类的加载过程(如Dog类):   通过类型信息定位Dog.class文件. 载入Dog.class文件,创建相应的Class对象. 执行父类的静态字段定义时初始化语句和父类的静态初始化块 ...

  3. (翻译)理解Java当中的回调机制

    原文地址:http://cleancodedevelopment-qualityseal.blogspot.com/2012/10/understanding-callbacks-with-java. ...

  4. [转]Unity: make your lists functional with ReorderableList

    原文地址:http://va.lent.in/unity-make-your-lists-functional-with-reorderablelist/ This article is reprod ...

  5. 将本地项目上传到Github

    将本地项目上传到Github 转载请注明出自天外归云的博客. 前提 已经下载了Git到本地. 创建Repository 首先登陆我的Github页面,在Repositories中New一个并起Repo ...

  6. UML的概念模型

    为 了理解UML,需要形成该语言的概念模型,这要求学习建模的3个要素:UML的基本构造块.支配这些构造块如何放在一起的规则和一些运用于整个UML的公 共机制.如果掌握了这些思想,就能够读懂UML模型, ...

  7. js的解析--预处理(三)

    js的解析与执行过程  分全局  {预处理阶段和执行阶段}  函数{预处理函数和执行阶段}   1/创建词法环境(环境上下文) LexicalEnvironment   === window { } ...

  8. html与表格知识

    <html>    --开始标签 <head> 网页上的控制信息 <title>页面标题</title> </head> <body& ...

  9. Cpdetector编码识别

    概述 浏览器在打开一个网页时,首要任务是判断网页的编码格式,然后采用合适的编码进行解析:我们常用的文本编辑器在打开文档时同样需要判断文档的编码进行相应的解析.这涉及到的技术就是编码甄别,下面我们介绍一 ...

  10. QQ邮箱

    yourTEXThere