Android wpa_supplicant源码分析---nl80211内核通信Generic Netlink
来源:互联网 发布:hadoop2.6.0源码下载 编辑:程序博客网 时间:2024/05/16 04:33
代码位置:
kernel/net/netlink/genetlink.c
kernel/include/net/genetlink.h
GENL简介
netlink仅支持32种协议类型,这在实际应用中可能并不足够。因此产生了generic netlink(以下简称为genl)。
generic netlink支持1024(前10个保留不用)个子协议号,弥补了netlink协议类型较少的缺陷。
1 架构及工作原理:
Generic Netlink是以自定义的genl_family为基本单位的,每一个想使用genl通信的模块都要自己创建一个genl_family然后注册到genl模块(总线)中,最多可以添加1024个。
用户空间在使用genl时,可以将指定将数据发送给你挂载到genl上的family,family再做具体的处理
每个family在注册时都被分配了唯一的family_id,用户空间在发送数据时,将family_id写入nlmsghdr.nlmsg_type字段,genl就可以对应的family。
genl中共分配了16个链表,每个链表都可以挂载自定义的family
static struct list_head family_ht[GENL_FAM_TAB_SIZE=16];
这16个链表中元素的总个数是1024.
结构体中重要参数意义如下:
- id: 注册时指定,若为0则有genl分配一个未占用的id,id的范围是10~1023,0~10保留未使用
id和链表数组family_ht[16]的对应关系首先对id取16的余数 id&0x1111,然后根据余数确定添加到哪一条链表中。这样的方式,比单独一条链表搜索时要节省时间,比建一个1024的数组要节省空间。看函数名字是genl_family_hash(),不知道hash表是不是这么创建的。 - name: 一个字符串,当内核模块和用户空间采用同样的name,才可以建立通信
- ops_list:该family的操纵函数
- mcast_groups:多播组链表,可以往该family中再注册多播组
- family_list:该family在genl模块中的链表索引
struct genl_family { unsigned int id; unsigned int hdrsize; char name[GENL_NAMSIZ]; unsigned int version; unsigned int maxattr; bool netnsok; bool parallel_ops; int (*pre_doit)(struct genl_ops *ops, struct sk_buff *skb, struct genl_info *info); void (*post_doit)(struct genl_ops *ops, struct sk_buff *skb, struct genl_info *info); struct nlattr ** attrbuf; /* private */ struct list_head ops_list; /* private */ struct list_head family_list; /* private */ struct list_head mcast_groups; /* private */ struct module *module;};
genl_family 操作函数
cmd:是个整形,用户空间发送消息时,首先定义好faimily的name,然后再定义好cmd,就可以调用相应的处理函数doit();
struct genl_ops { u8 cmd; u8 internal_flags; unsigned int flags; const struct nla_policy *policy; int (*doit)(struct sk_buff *skb, struct genl_info *info); int (*dumpit)(struct sk_buff *skb, struct netlink_callback *cb); int (*done)(struct netlink_callback *cb); struct list_head ops_list;};
2 genl初始化过程
链表的初始化和消息接收函数
将创建的netlnik socket挂载到net_namespace(不懂这个东东)中
static int __init genl_init(void){ int i, err; //初始化16条链表 for (i = 0; i < GENL_FAM_TAB_SIZE; i++) INIT_LIST_HEAD(&family_ht[i]); //注册一个自己用的family,共上层使用 err = genl_register_family_with_ops(&genl_ctrl, &genl_ctrl_ops, 1); //真正的初始化函数,其中的genl_pernet_init()会创建netlink,定义接收函数genl_rcv() err = register_pernet_subsys(&genl_pernet_ops); genl_pernet_init() { struct netlink_kernel_cfg cfg = { .input = genl_rcv, //接收函数 .flags = NL_CFG_F_NONROOT_RECV, }; /* we'll bump the group number right afterwards */ net->genl_sock = netlink_kernel_create(net, NETLINK_GENERIC, &cfg); } //注册到多播组中 err = genl_register_mc_group(&genl_ctrl, ¬ify_grp);}
2.1 接收数据genl_rcv_msg
genl_rcv()接收到数据会直接调用genl_rcv_msg()
然后根据nlmsghdr中的family_id(type)找到对应的family,
再根据genlmsghdr中的cmd找到family中对应的ops,然后调用doit做相应的处理
static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh){ //nlmsghdr中的type应该和family的id一致, //但是内核中genl注册family时,id是自动非配的,那用户空间发送的消息怎么确认id, family = genl_family_find_byid(nlh->nlmsg_type); //根据nlh中定义的cmd类型决定 genl_family_rcv_msg(family, skb, nlh); { //在传入的nlh的载荷中包含着geml的头genlmsghdr, struct genlmsghdr *hdr = nlmsg_data(nlh); //genl 信息,里面有netlnik head,genl head,user head等信息,最终会由用户(nl80211)定义的ops处理 struct genl_info info; //如果有family有体检需要处理的,可以放在该处 err = family->pre_doit(ops, skb, &info); //通过cmd找到ops,对传入的数据进行处理 ops = genl_get_cmd(hdr->cmd, family); //ops处理数据 err = ops->doit(skb, &info); //family的后续处理 family->post_doit(ops, skb, &info); }}
3 内核中使用genl
3.1 genl family 注册
生成family_id,并将其加载到(android M中已经更改此API)
static inline int genl_register_family_with_ops(struct genl_family *family, struct genl_ops *ops, size_t n_ops){ //将每个family按照id顺序加载到genl的16条链表中 err = __genl_register_family(family); //将genl_ops注册到family的ops_list上 for (i = 0; i < n_ops; ++i, ++ops) { err = genl_register_ops(family, ops); }}
3.2 genl 多播组注册
family指向属于该多播组的family,
id:是多播组id,由内核分配,在genlmsg_multicast()中使用
struct genl_multicast_group { struct genl_family *family; /* private */ struct list_head list; /* private */ char name[GENL_NAMSIZ]; u32 id;};int genl_register_mc_group(struct genl_family *family, struct genl_multicast_group *grp){ .... //有一大堆算id的代码,看不懂, 通过传入的group的name计算出唯一的一个id,作为netlink多播组的group id .... grp->id = id; set_bit(id, mc_groups); list_add_tail(&grp->list, &family->mcast_groups); grp->family = family;}
3.3 发送单播数据
static inline int genlmsg_reply(struct sk_buff *skb, struct genl_info *info)最终会调用netlnik api nlmsg_unicast()
3.4 发送多播数据
genlmsg_multicast_netns(struct net *net, struct sk_buff *skb, u32 portid, unsigned int group, gfp_t flags);
4 用户空间使用genl
代码位置:external/libnl
官方文档 http://www.carisma.slowglass.com/~tgr/libnl/doc/core.html#_socket_structure_struct_nl_sock
netlink通信时同样可以使用libnl中的api,之前看netlink那个实例的时候,就感觉代码很乱,libnl中提供了比较精简的api
4.1 生成socket
#include <netlink/socket.h>struct nl_sock *nl_socket_alloc(void)void nl_socket_free(struct nl_sock *sk)//与kernel中的 genl模块相连接genl_connect(struct nl_sk *)//获取genl中要使用的family的idint genl_ctrl_resolve(struct nl_sock *sk, const char *name){ msg = nlmsg_alloc(); //"nlctrl"是由kernel 的genl模块住的一个family,目前只支持获取genel中 family的id genlmsg_put(msg, 0, 0, genl_ctrl_resolve(global->nl, "nlctrl"), 0, 0, CTRL_CMD_GETFAMILY, 0);}
4.2 发送数据
struct nl_msg *msg = nlmsg_alloc();//msg中填充family ops中定义的CMD//wpa_s 封装为 static void * nl80211_cmd(struct wpa_driver_nl80211_data *drv, struct nl_msg *msg, int flags, uint8_t cmd)//该函数在生成nlmsg时,在其中添加genlmsghdr(genl的头)void *genlmsg_put(struct nl_msg *msg, uint32_t pid, uint32_t seq, int family, int hdrlen, int flags, uint8_t cmd, uint8_t version)示例:genlmsg_put(msg, 0, 0, family_id, 0, 0, cmd, 0);//msg中放置不同类型的属性 attribute,整形或一块内存NLA_PUT_U32(msg, attrtype, value);NLA_PUT(msg, attrtype, attrlen, data)
4.3 发送单播
返回值为发送的字符数,若错误返回负
int nl_send_auto_complete(struct nl_sock *sk, struct nl_msg *msg)
该函数最终调用socket API sendmsg(),函数内部会利用nl_msg中的信息生成 sendmsg 所需的msghdr结构体
4.4 接收多播
nl_socket_add_membership(struct nl_sock *, group_id);
5 GENL 流程图
6 使用实例
只实现了用户空间向内核中的family发送单播消息,
使用在ops对应的cmd doit函数中读取上层传送下来的attr内容
内核向用户空间发送多播消息还没有实现,因为不知道如何拿到genl创建的sock,
6.1 内核空间
//genl test code by cuijiyue#include <.h>#define GENL_TEST_CMD 6#define GENL_TEST_ATRR_TYPE 66char *msg_send = "hello form genl kernel!";static struct genl_family genl_test_fam = { .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */ .name = "genl_test", /* have users key off the name instead */ .hdrsize = 0, /* no private header */ .version = 1, /* no particular meaning now */ .maxattr = NL80211_ATTR_MAX, .netnsok = true,};//genl_info *info 由genl genl_rcv()时生成的static int genl_test_cmd_process(struct sk_buff *skb, struct genl_info *info){ struct sk_buff *msg; struct nlmsghdr *nlh; printk("%s: genl_info nlhdr: len:%u, pid%u, snd_portid:%u\n", "genl_test", info->nlhdr->nlmsg_len, info->nlhdr->nlmsg_pid, info->snd_portid); printk("%s: genl_info genlgdr: cmd:%u\n", "genl_test", info->genlhdr->cmd); //printk("%s: genl_info userhdr:%s\n", "genl_test", (char*)(info->userhdr)); if(info->attrs[GENL_TEST_ATRR_TYPE]) { printk("%s: GENL_TEST_ATRR_TYPE data:%s\n", "genl_test", (char*)nla_data(info->attrs[GENL_TEST_ATRR_TYPE])); } msg = nlmsg_new(4096, GFP_KERNEL); if (!msg) return -ENOMEM; nlh = nlmsg_put(msg, 0, 0, NLMSG_DONE, strlen(msg_send), 0); strcpy(nlmsg_data(nlh), msg_send); //use netlnik unicast发送消息到用户空间 //return genlmsg_reply(msg, info); //multi fail, cann't find this genl sock, it is in first_device //genlmsg_multicast(&genl_test_fam, msg, 0, 0, 0); //nlmsg_multicast(); genlmsg_multicast_allns(&genl_test_fam, msg, 0, 0, GFP_ATOMIC); return 6666;}static struct genl_ops genl_test_ops[] = { { .cmd = GENL_TEST_CMD, .doit = genl_test_cmd_process, },};static struct genl_multicast_group genl_test_group[] = { {.name = "genl_test_group",}};int __init genl_test_init(void){ int err; //err = genl_register_family_with_ops(&genl_test_fam, genl_test_ops, ARRAY_SIZE(genl_test_ops)); //in android M has change this api usage err = genl_register_family_with_ops_groups(&genl_test_fam, genl_test_ops, genl_test_group); if (err) goto err_out; printk("%s: genl_test_fam id:%u, mcgrp_offset:%u\n", "genl_test", genl_test_fam.id, genl_test_fam.mcgrp_offset); //err = genl_register_mc_group(&genl_test_fam, &genl_test_group); //if (err) // goto err_out; //printk("%s: genl_test_group id:%u\n", "genl_test", genl_test_group.id); return 0;err_out: genl_unregister_family(&genl_test_fam); return err;}module_init(genl_test_init);void __exit genl_test_exit(void){ genl_unregister_family(&genl_test_fam);}module_exit(genl_test_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("cuijiyue");
6.2 用户空间
#include <.h>#define MAX_PAYLOAD 1024 /* maximum payload size*/#define GENL_TEST_CMD 6#define GENL_TEST_ATRR_TYPE 66char *attr_data = "66666";struct nl_sock *nl_sk;int main(int argc, char* argv[]) { int ret; int family_id, group_id; struct nl_msg *msg; struct sockaddr_nl src_addr, dest_addr; struct nlmsghdr *nlh = NULL; struct iovec iov; struct msghdr msg_rev; memset(&msg, 0, sizeof(msg)); //创建sock并连接到kernel nl_sk = nl_socket_alloc(); ret = genl_connect(nl_sk); printf("self pid:%u\n", getpid()); if (ret != 0) { printf("genl_connect error%s\n", ret); goto err_out; } //获取自定义的family的id family_id = genl_ctrl_resolve(nl_sk, "genl_test"); printf("genl_test family id:%d\n", ret); // multi group, get it from kernel log group 9 nl_socket_add_membership(nl_sk, 9); //生成msg msg = nlmsg_alloc(); genlmsg_put(msg, 0, 0, family_id, 0, 0, GENL_TEST_CMD, 0); //放置attr nla_put(msg, GENL_TEST_ATRR_TYPE, strlen(attr_data), attr_data); //发送消息 ret = nl_send_auto_complete(nl_sk, msg); printf("nl_send ret:%d\n", ret); //接收multi消息 memset(&dest_addr, 0, sizeof(dest_addr)); nlh=(struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD)); nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD); iov.iov_base = (void *)nlh; iov.iov_len = nlh->nlmsg_len; msg_rev.msg_name = (void *)&dest_addr; msg_rev.msg_namelen = sizeof(dest_addr); msg_rev.msg_iov = &iov; msg_rev.msg_iovlen = 1; printf("waiting rev\n"); recvmsg(nl_sk, &msg_rev, 0); printf(" Received message pid:%d, payload: %s\n", nlh->nlmsg_pid, NLMSG_DATA(nlh));err_out: nl_socket_free(nl_sk); return ret;}
7 wpa_supplicant与内核的通信
7.1 内核中nl80211模块
nl80211直接使用的是Generic Netlink接口,添加了一个name为“nl80211”的family和对应的 ops方法,同时注册了多个多播组
代码位置
kernel/net/wireless/nl80211.c
kernel/include/uapi/linux/nl80211.h
nl80211添加到genl中的family
static struct genl_family nl80211_fam = { .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */ .name = "nl80211", /* have users key off the name instead */ .hdrsize = 0, /* no private header */ .version = 1, /* no particular meaning now */ .maxattr = NL80211_ATTR_MAX, .netnsok = true, .pre_doit = nl80211_pre_doit, .post_doit = nl80211_post_doit,};
static struct genl_ops nl80211_ops[]
是nl80211可以接受到的命令,每种命令都有相应的nl80211处理函数
wifi的scan,associate,connect等等,都是由此处理的
太多了,去看源码吧
7.2 wpa_supplicant中的genl socekt
wpa_s中与nl80211的通信时创建了两条socket,一条用于发送消息到内核,一条接收内核中的多播消息,
scoekt的初始化源码在wpa_driver_nl80211_init_nl_global()中
7.2.1 发送消息的socekt:
global->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);global->nl = nl_create_handle(global->nl_cb, "nl");在nl80211中,每接收到一条命令,都会通过socket发送回相应的处理结果其中nl_cb可以自定义函数处理内核返回的消息。在global->nl发送命令前,通过以下函数设置自定义的结果处理函数。nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, valid_handler, valid_data);
7.2.2接收内核多播消息的socekt:
global->nl_event = nl_create_handle(global->nl_cb, "event");获取nl80211中各个多播组的group_id,并添加socekt到该多播组ret = nl_get_multicast_id(global, "nl80211", "scan");ret = nl_socket_add_membership(global->nl_event, ret);
下一篇 wpa_supplicant源码分析–扫描连接过程
- Android wpa_supplicant源码分析---nl80211内核通信Generic Netlink
- Android wpa_supplicant源码分析---内核通信Netlink简介
- Generic Netlink内核实现分析(二):通信
- Generic+Netlink内核实现分析(二):通信
- Generic Netlink内核实现分析(一):初始化
- Generic+Netlink内核实现分析(一):初始化
- Configuration file: / etc / hostapd / hostapd.conf nl80211: 'nl80211' generic netlink not found
- Netlink 内核实现分析(二):通信
- Netlink+内核实现分析(二):通信
- Generic Netlink分析和使用
- Android wpa_supplicant源码分析--conf配置文件
- Android wpa_supplicant源码分析--bss扫描结果
- Android wpa_supplicant源码分析--运行方式
- Android wpa_supplicant源码分析--conf配置文件
- Android wpa_supplicant源码分析--conf配置文件
- netlink通信机制分析
- netlink通信机制分析
- wpa_supplicant 源码分析
- C语言实现链表节点的插入和删除
- 获取终端的相关信息
- JSESSIONID、cookie与SESSION的区别和联系 同名JSESSIONID处理
- 不定长内存池之apr_pool
- 基于 CXF 的 restful 风格的webservice 实现
- Android wpa_supplicant源码分析---nl80211内核通信Generic Netlink
- 交换两个变量值的方法
- Java版本:图书管理系统
- Apache http安装
- Leetcode 278. First Bad Version
- windows下安装memcache步骤
- 程序切片技术
- 判断是否是从微信中打开的浏览器
- Graham Scan凸包算法