最近在做一个项目,需要通过NL80211和驱动打交道。

以前没用过,走了不少弯路,因此做个笔记,方便自己,当然如果能够帮助别人更好。

无线网卡驱动主要有两个“标准”(这样的形容并不准确!):

  • WEXT(Wireless Extension):使用WEXT的工具通过ioctl和驱动通信,典型工具ifconfig等;
  • NL80211(Netlink 80211):使用NL80211的工具通过一个特殊的socket和驱动打通信,典型工具包括IW、iwconfig等。

这里的NL80211仅仅是netlink工具在无线驱动方面的一个应用,其实它的应用很广泛,也很基础。Netlink提供了一种通信方式,通信双方可以是用户态或内核态,关于这方面的介绍,直接看libnl(netlink的一个实现)的网站(http://www.infradead.org/~tgr/libnl/),讲解的很详细。目前使用netlink主要是通过libnl来做,当然也可以自己拼凑和解析消息。

这里提供一个我编译的libnl动态库,使用的是Google提供的ANDROID-NDK-R7,理论上可用于android 3.2以上的系统。http://115.com/file/e789tu6y#libnltest.so

下面是我使用的一些代码,是安卓的JNI接口,用于在连接上AP后获取信号强度。主要参考了iw和wpa_supplicant的相关实现。

int GetSignalPower(const unsigned char *addr, char *signal)    // 消息函数
{
struct nl_sock *l_nl_handle = NULL;
struct nl_cache *l_nl_cache = NULL;
struct genl_family *l_nl_family = NULL;
struct nl_cb *l_nl_cb = NULL;
int ret = 0;
int err = 1;
struct nl_msg *l_msg = NULL;
__android_log_print(ANDROID_LOG_DEBUG, NL80211_LOG, “Enter GetSignalPower”);
if(signal == NULL)
{
__android_log_print(ANDROID_LOG_ERROR, NL80211_LOG, “Failed to get signal power, bad params.”);
return -1;
}
*signal = 0;
//char signal = 0;
//unsigned char addr[6] = {0×00, 0x1D, 0x0F, 0x3F, 0×43, 0x3E};
l_nl_cb = nl_cb_alloc(NL_CB_DEFAULT);    // 创建回调
if(l_nl_cb == NULL)
{
__android_log_print(ANDROID_LOG_ERROR, NL80211_LOG, “Failed to alloc cb.”);
printf(“Failed to alloc cb.\n”);
ret = -1;
goto error_catch;
}
__android_log_print(ANDROID_LOG_DEBUG, NL80211_LOG, “Succeed to alloc callback.”);
l_nl_handle = nl_socket_alloc_cb(l_nl_cb);    // 根据回调创建netlink socket
if(l_nl_handle == NULL)
{
__android_log_print(ANDROID_LOG_ERROR, NL80211_LOG, “Failed to alloc a netlink socket.”);
printf(“Failed to alloc a netlink socket.\n”);
ret = -1;
goto error_catch;
}
__android_log_print(ANDROID_LOG_DEBUG, NL80211_LOG, “Succeed to alloc socket.”);

ret = genl_connect(l_nl_handle);    // 连接内核
if(ret != 0)
{
__android_log_print(ANDROID_LOG_ERROR, NL80211_LOG, “Failed to connect to generic socket, error code is %d.\n”, ret);
printf(“Failed to connect to generic socket, error code is %d.\n”, ret);
goto error_catch;
}

__android_log_print(ANDROID_LOG_DEBUG, NL80211_LOG, “Succeed to connect generic socket.”);

ret = genl_ctrl_alloc_cache(l_nl_handle, &l_nl_cache);    // 创建cache,我也不清楚这个操作必要性
if(ret < 0)
{
__android_log_print(ANDROID_LOG_ERROR, NL80211_LOG, “Failed to alloc cache.”);
printf(“Failed to alloc cache.\n”);
goto error_catch;
}
__android_log_print(ANDROID_LOG_DEBUG, NL80211_LOG, “Succeed to genl_ctrl_alloc_cache.”);
l_nl_family = genl_ctrl_search_by_name(l_nl_cache, “nl80211″);    // 查找NL80211簇
if (l_nl_family == NULL)
{
__android_log_print(ANDROID_LOG_ERROR, NL80211_LOG, “nl80211: ‘nl80211′ generic netlink not found.”);
printf(“nl80211: ‘nl80211′ generic netlink not found.\n”);
ret = -1;
goto error_catch;
}
__android_log_print(ANDROID_LOG_DEBUG, NL80211_LOG, “Succeed to genl_ctrl_search_by_name.”);

l_msg = nlmsg_alloc();    // 创建netlink message
if (l_msg == NULL)
{
__android_log_print(ANDROID_LOG_ERROR, NL80211_LOG, “Failed to alloc message.”);
printf(“Failed to alloc message.\n”);
ret = -1;
goto error_catch;
}
__android_log_print(ANDROID_LOG_DEBUG, NL80211_LOG, “Succeed to nlmsg_alloc.”);
genlmsg_put(l_msg, 0, 0, genl_family_get_id(l_nl_family), 0,
0, NL80211_CMD_GET_STATION, 0);    // 填充消息,这里的“NL80211_CMD_GET_STATION”是一条NL80211的命令,具体作用参考nl80211.h
nla_put(l_msg, NL80211_ATTR_MAC, ETH_ALEN, addr);    // 填充消息,这里的MAC地址是AP的MAC
nla_put_u32(l_msg, NL80211_ATTR_IFINDEX, if_nametoindex(“wlan0″));    // 填充消息,很明显,这里指定了要查询的网卡

nl_cb_set(l_nl_cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);    // 设置回调,这里的“NL_CB_FINISH”等是libnl定义的一些回调类型,Google一下就知道用处和用法
nl_cb_set(l_nl_cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
nl_cb_err(l_nl_cb, NL_CB_CUSTOM, error_handler, &err);
nl_cb_set(l_nl_cb, NL_CB_VALID, NL_CB_CUSTOM, get_sta_handler, signal);

ret = nl_send_auto_complete(l_nl_handle, l_msg);    // 发送消息
if (ret < 0)
{
__android_log_print(ANDROID_LOG_ERROR, NL80211_LOG, “Failed to send msg.”);
printf(“Failed to send msg.\n”);
ret = -1;
goto error_catch;
}

do{
nl_recvmsgs(l_nl_handle, l_nl_cb);    // 注意!这里要重复读取直到我们的回调函数被调用
sleep(1);
}while(err>0);

error_catch:    // 一些释放工作
if(l_nl_family != NULL)
{
genl_family_put(l_nl_family);
l_nl_family = NULL;
}
if(l_nl_cache != NULL)
{
nl_cache_free(l_nl_cache);
l_nl_cache = NULL;
}
if(l_nl_handle != NULL)
{
nl_socket_free(l_nl_handle);
l_nl_handle = NULL;
}
if(l_nl_cb != NULL)
{
nl_cb_put(l_nl_cb);
l_nl_cb = NULL;
}
if(l_msg != NULL)
{
nlmsg_free(l_msg);
l_msg = NULL;
}

__android_log_print(ANDROID_LOG_DEBUG, NL80211_LOG, “Exit GetSignalPower”);

if(*signal == 0)
{
__android_log_print(ANDROID_LOG_ERROR, NL80211_LOG, “signal 0.”);
return -1;
}
else
{
__android_log_print(ANDROID_LOG_INFO, NL80211_LOG, “signal %d.”, *signal);
return 0;
}
}

static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
void *arg)    // 错误回调函数
{
int *ret = (int *)arg;
*ret = err->error;
__android_log_print(ANDROID_LOG_INFO, NL80211_LOG, “nl80211 Error handle!”);
printf(“Error handle\n”);
return NL_SKIP;
}

static int finish_handler(struct nl_msg *msg, void *arg)    // 结束回调函数
{
int *ret = (int *)arg;
*ret = 0;
__android_log_print(ANDROID_LOG_INFO, NL80211_LOG, “nl80211 Finish handle!”);
printf(“Finish!\n”);
return NL_SKIP;
}

static int ack_handler(struct nl_msg *msg, void *arg)    // 确认回调函数
{
int *ret = (int *)arg;
*ret = 0;
__android_log_print(ANDROID_LOG_INFO, NL80211_LOG, “nl80211 ACK handle!”);
printf(“ACK!\n”);
return NL_STOP;
}

static int get_sta_handler(struct nl_msg *msg, void *arg)    // 这里是我们真正读取消息的地方!!
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct nlattr *tb_nested[NL80211_STA_INFO_MAX + 1];
struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlmsg_hdr(msg));
struct _ap_status *data = (struct _ap_status *)arg;
char signal;

if( 0 != nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL))    // 解析消息,第一次解析,这里的“NL80211_ATTR_MAX”等的用处和用法参考nl80211.h
{
__android_log_print(ANDROID_LOG_ERROR, NL80211_LOG, “Failed to parse nl attrs.!”);
printf(“Failed to parse nl attrs.\n”);
return NL_SKIP;
}
else
{
__android_log_print(ANDROID_LOG_INFO, NL80211_LOG, “Succeeded to parse nl attrs!”);
printf(“Succeeded to parse nl attrs.\n”);
}

if (tb[NL80211_ATTR_STA_INFO])
{
__android_log_print(ANDROID_LOG_INFO, NL80211_LOG, “We have NL80211_ATTR_STA_INFO!”);
printf(“We have NL80211_ATTR_STA_INFO\n”);
if(nla_parse_nested(tb_nested, NL80211_STA_INFO_MAX, tb[NL80211_ATTR_STA_INFO],  NULL) != 0)    // 解析消息,第二次解析
{
__android_log_print(ANDROID_LOG_ERROR, NL80211_LOG, “Failed to parse nl attrs nested.”);

printf(“Failed to parse nl attrs nested.\n”);
return NL_SKIP;
}
else
{
__android_log_print(ANDROID_LOG_INFO, NL80211_LOG, “Succeeded to parse attrs nested.”);
printf(“Succeeded to parse attrs nested.\n”);
}
if(tb_nested[NL80211_STA_INFO_SIGNAL])
{
signal = nla_get_u8(tb_nested[NL80211_STA_INFO_SIGNAL]);
if(arg != NULL)
{
(*(char *)arg) = signal;
}
__android_log_print(ANDROID_LOG_INFO, NL80211_LOG, “ap signal is %d\n”, *(char *)arg);
printf(“ap signal is %d\n”, signal);
}
//        if(tb_nested[NL80211_STA_INFO_SIGNAL_AVG])
//        {
//            signal = nla_get_u8(tb_nested[NL80211_STA_INFO_SIGNAL_AVG]);
//            printf(“ap avg signal is %d\n”, signal);
//        }
}
else
{
__android_log_print(ANDROID_LOG_ERROR, NL80211_LOG, “No NL80211_ATTR_STA_INFO.”);
printf(“No NL80211_ATTR_STA_INFO\n”);
}

return NL_SKIP;
}

NL80211使用笔记相关推荐

  1. nl80211 和 wext

    NL80211使用笔记 Posted on 2012 年 04 月 18 日 by sqbing 最近在做一个项目,需要通过NL80211和驱动打交道. 以前没用过,走了不少弯路,因此做个笔记,方便自 ...

  2. iptables学习笔记:使用NAT实现简单的无线AP

    之前使用的是无线路由让手机上网.学习了iptables后,尝试在非openwrt系统的Linux上实现相同功能.本文简单记录一下. 手上有块X86的板子,上面安装了Linux系统.几个月前研究了WIF ...

  3. [Android]Android P(9) WIFI学习笔记 - HAL (1)

    目录 前文回顾 前言 入口 WifiNative 初始化 打开WIFI IWifiChip IWifiCond ISupplicant 前文回顾 WIFI学习笔记 - Framework (1) WI ...

  4. 树莓派学习笔记——Wifi AP热点模式 使用RT5370

    0.前言 本文详细说明为树莓派增加Wifi AP热点功能的具体步骤.配置完成之后,树莓派将增加一个Wifi热点功能,使用笔记本或手机便可连接树莓派,树莓派具有了AP热点功能,可更一步扩展树莓派相关的W ...

  5. 【读书笔记】知易行难,多实践

    前言: 其实,我不喜欢看书,只是喜欢找答案,想通过专业的解答来解决我生活的困惑.所以,我听了很多书,也看了很多书,但看完书,没有很多的实践,导致我并不很深入在很多时候. 分享读书笔记: <高效1 ...

  6. 【运维学习笔记】生命不息,搞事开始。。。

    001生命不息,搞事不止!!! 这段时间和hexesdesu搞了很多事情! 之前是机械硬盘和固态硬盘的测速,我就在那默默的看着他一个硬盘一个机械测来测去. 坐在他后面,每天都能看到这位萌萌的小男孩,各 ...

  7. SSAN 关系抽取 论文笔记

    20210621 https://zhuanlan.zhihu.com/p/353183322 [KG笔记]八.文档级(Document Level)关系抽取任务 共指id嵌入一样 但是实体嵌入的时候 ...

  8. pandas以前笔记

    # -*- coding: utf-8 -*- """ Created on Sat Jul 21 20:06:20 2018@author: heimi "& ...

  9. PyTorch 学习笔记(六):PyTorch hook 和关于 PyTorch backward 过程的理解 call

    您的位置 首页 PyTorch 学习笔记系列 PyTorch 学习笔记(六):PyTorch hook 和关于 PyTorch backward 过程的理解 发布: 2017年8月4日 7,195阅读 ...

最新文章

  1. js ajax 递归,javascript ajax循环请求/ 长轮询终极解决办法——递归
  2. 醉没醉,带上智能手机走两步就知道
  3. Java PriorityQueue实现大顶堆
  4. web前端开发--列表
  5. C/C++中使用函数memset对int型数组赋值(0,-1,max,min)
  6. soap php 分开类,PHP SoapClient类型映射的行为有所不同
  7. 钉钉项目任务怎么添加审批表单
  8. MySQL表结构优化
  9. IIS 中 另一个程序正在使用此文件,进程无法访问!
  10. 我的成长笔记20210324(进度把控)
  11. 一个敲有趣的R语言拼图工具
  12. TensorFlow实现对花朵数据集的图片分类(保证运行成功)
  13. 小米手机解锁、线刷、开启root权限详细教程
  14. ubuntu 、emacs中使用翻译功能
  15. 算法创作|栈的应用——括号匹配问题解决方法
  16. 开源之道给您拜年啦~~
  17. cf Round#779 D 388535
  18. Linux系统命令与网络、磁盘参数和日志监控
  19. 对于母版页的一些修改
  20. TI杯 LaunchPad MSP430开发环境搭建

热门文章

  1. python 机器学习——K 近邻分类理论及鸢尾( Iris )数据集实例操作
  2. 参考文献格式字号字体_论文格式要求及字体大小
  3. PDF Redactor - 涂黑屏蔽PDF文字让敏感内容不可读的软件工具
  4. 华为路由交换学习篇-STP生成树协议
  5. 电脑桌面突然变成粉色或者紫色怎么回事?
  6. Hive数据仓库实战
  7. 计算器软件测试数据,计算器软件测试报告.doc
  8. C程序设计-方法与实践(清华大学出版社)习题解析
  9. 不同计算机用户的区别是什么意思,电脑操作系统的32位和64位分别是什么意思?有什么区别?...
  10. 怎样才能保证单元测试效果