美文网首页
Wifi笔记 |Android wpa_supplicant源码

Wifi笔记 |Android wpa_supplicant源码

作者: 力卉编程 | 来源:发表于2019-12-27 22:31 被阅读0次

    代码位置:

    kernel/net/netlink/genetlink.c
    kernel/include/net/genetlink.h

    架构及工作原理:

    简介:
    netlink仅支持32种协议类型,这在实际应用中可能并不足够。因此产生了generic netlink(以下简称为genl)。
    generic netlink支持1024(前10个保留不用)个子协议号,弥补了netlink协议类型较少的缺陷。

    原理:
    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的范围是101023,010保留未使用
    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;
    };
    

    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, &notify_grp);
    }
    

    接收数据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);
        }
    }
    

    内核中使用genl

    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);
        }
    }
    

    发送单播数据

    static inline int genlmsg_reply(struct sk_buff *skb, struct genl_info *info)
    #最终会调用 如下:
    netlnik api nlmsg_unicast()
    

    用户空间使用genl
    代码位置:external/libnl
    官方文档 http://www.carisma.slowglass.com/~tgr/libnl/doc/core.html#_socket_structure_struct_nl_sock

    netlink通信时同样可以使用libnl中的api,libnl中提供了比较精简的api

    生成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的id
    int 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);
    }
    

    发送数据

    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)
    

    发送单播

    int nl_send_auto_complete(struct nl_sock *sk, struct nl_msg *msg)
    #该函数最终调用
    socket API sendmsg(),
    #函数内部会利用nl_msg中的信息生成 sendmsg 所需的msghdr结构体
    

    GENL 流程图

    GENL 流程图

    只实现了用户空间向内核中的family发送单播消息,
    使用在ops对应的cmd doit函数中读取上层传送下来的attr内容
    内核向用户空间发送多播消息还没有实现,因为不知道如何拿到genl创建的sock,

    内核空间

    //genl test code by cuijiyue
    #include <.h>
    #define GENL_TEST_CMD 6
    #define GENL_TEST_ATRR_TYPE 66
    
    char *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");
    

    用户空间:

    #include <.h>
    
    #define MAX_PAYLOAD 1024  /* maximum payload size*/
    #define GENL_TEST_CMD 6
    #define GENL_TEST_ATRR_TYPE 66
    char *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;
    }
    ···
    
    文 | 力卉编程

    相关文章

      网友评论

          本文标题:Wifi笔记 |Android wpa_supplicant源码

          本文链接:https://www.haomeiwen.com/subject/gurjoctx.html