粗略了解了hostapd以及用户态与内核态通信的其中一种方式(netlink/genlink),不了解的读者可以查看上面发表的内容。
这一章将以ssid的传输来具体说明ssid的值是怎样通过hostapd传入内核中的,即第三系列:
通过前面几章的描述,基本了解了hostapd的初始化以及代码框架,也基本可以了解hostapd中运用的netlink通信及其特殊的一种通信方式genlink。但感觉如果需要更好地把握这些内容块还需要有一根线能将这些块串联起来,以达到在了解了这些内容怎么来之后,也能进一步知道怎么去用,用在哪。接下来进入正题。
通过前面几章的描述,基本了解了hostapd的初始化以及代码框架,也基本可以了解hostapd中运用的netlink通信及其特殊的一种通信方式genlink。但感觉如果需要更好地把握这些内容块还需要有一根线能将这些块串联起来,以达到在了解了这些内容怎么来之后,也能进一步知道怎么去用,用在哪。接下来进入正题。
下图为hostapd相对于ssid的接口调用关系,从做至右表示从相对底层至上层hostapd的main()接口。当然该图中并不表示包含了全部ssid的传输过程,只是选了其中5条线路进行展示。
因为这里主要说明初始化时ssid的传输,所以读者们可以主要观察红色线路的传输,其他线路了解即可。
因为是初始化说明,所以这里主要叙述上图中的红色线路。
至此后续的传递如上图所示,而ssid则是包含在这些接口的参数中,进行一级一级地向下传递,直至调用到set_ap函数指针指向的wpa_driver_nl80211_set_up接口。展开wpa_driver_nl80211_set_up接口代码说明。
static int wpa_driver_nl80211_set_ap(void *priv,
struct wpa_driver_ap_params *params)
{
...
wpa_hexdump_ascii(MSG_DEBUG, "nl80211: ssid",params->ssid, params->ssid_len); //此处为打印
NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len,params->ssid); //这就厉害了,这里以NL80211_ATTR_SSID为向底层传输的genlink的独特序号,将
//ssid内容通过netlink向底层传输
...
}
到这里,hostapd中的ssid传输说明完成。这一阶段主要说明的是hostapd中主要对于涉及到ssid值的接口调用关系。同时读者也可以以此发现其他wifi配置内容的接口调用关系,套路都是一样的。
通过前面的netlink及genlink的分析,可以对用户态变量至内核态变量的传输有一个基本的了解,在这里主要对获取到ssid后的接口调用流程简单说明。
hostapd(用户态)将ssid通过genlink机制将ssid传输至内核态时,执行如下2个主要的步骤操作,将内容发现并使用(ssid为例):
下面具体通过代码以顺蔓摸瓜,由下而上的方式进行说明。
主要接口及结构体说明:
下面是几个代码块内容的概括。
/* policy for the attributes */
//nl80211策略
static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
...
[NL80211_ATTR_SSID] = { //nl80211中的ssid属性序号
.type = NLA_BINARY, //属性类型
.len = IEEE80211_MAX_SSID_LEN //ssid数据内容最大长度
},
...
};
//nl80211结构体数组
static struct genl_ops nl80211_ops[] = {
...
{
.cmd = NL80211_CMD_START_AP, //命令序号
.policy = nl80211_policy, //nl80211策略(具体见上方代码),其中包含了ssid的属性序号
.flags = GENL_ADMIN_PERM,
.doit = nl80211_start_ap, //命令序号对应的调用接口
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL,
},
...
};
/*启动ap接口*/
static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) //该接口具体在下一模块进行说明
{
...
if (info->attrs[NL80211_ATTR_SSID]) { //匹配ssid属性NL80211_ATTR_SSID
params.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); //获取的数据包中ssid数据内容
params.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); //获取的数据包中ssid的内容数据长度
if (params.ssid_len == 0 || params.ssid_len > IEEE80211_MAX_SSID_LEN)
return -EINVAL;
}
...
}
//nl80211初始化接口
int nl80211_init(void)
{
...
err = genl_register_family_with_ops(&nl80211_fam, nl80211_ops, ARRAY_SIZE(nl80211_ops)); //对nl80211_ops结构体的genlink注册
if (err)
return err;
...
}
/*nl80211初始化接口*/
/////
static int __init cfg80211_init(void)
{
...
err = nl80211_init(); //调用nl80211_init接口
if (err)
goto out_fail_nl80211;
...
}
//最终由subsys_initcall加载(对于驱动模块,使用subsys_initcall等价于使用module_init)
subsys_initcall(cfg80211_init);
至此,知道了无线中用户态(hostapd)至内核态通过genlink的数据传输大致流程。接下来说明传下来的ssid是如何实现向beacon填充的,以最终完结这一整套beacon填充功能的代码说明。
在前面一章beacon填充的方式说明中可以了解到其中主要展现了一个接口wla_beacon_get,以及后续具体项目实现说明的另一个接口:ieee80211_assign_beacon()。这两个接口都可以按照80211协议,巧妙地实现向beacon中填充我们需要的内容。
但其中使用到的数据是如果传给他们的呢?下面的一幅图就可以很明了地说明这两个接口使用的数据的来源。
  上图说明的即是内核态在获取到ssid数据内容后,对于向beacon帧中进行内容填充的流程说明。
至此,关于无线信息传递的系列就已经全部粗略说明完成。主要是针对向beacon中填充ssid进行的,当然关于其他内容也是大致相同,可以以此为出发点,举一反三进行更深入的探究。
有这些基础,下面对项目中向beacon帧中填充需要信息功能进行单一的功能性实现的描述,就不再拓展扰乱文章架构了。