wpa_supplicant与kernel交互

wpa_supplicant与kernel交互的操作,一般需要先明确驱动接口,以及用户态和kernel态的接口函数,以此来进行调用操作。这里分为4个步骤讨论。

1.首先需要明确指定的驱动接口。因为有较多的驱动接口可以使用,如wext、nl80211等。指定了之后,才能调用相应接口的方法。

2.保存驱动接口

3.接口函数的实现(分为用户态和kernel态)。系统已经定义了,我们只需找到定义的地方,了解有哪些函数。

4.交互

(a)用户态向kernel态发送请求(通过ioctl)

(b)kernel态向用户态发送事件通知(通过netlink)

1.首先需要明确指定的驱动接口

(1)查看init.XX.rc中指定的driver的命令参数;

(2)根据命令参数,在wpa_driver_ops *wpa_drivers[] 中查找对应接口。

wpa_drivers[]的定义是在[-->external/wpa_supplicant_8/src/drivers/drivers.c]

2.保存驱动接口

在wpa_supplicant初始化过程中,在wpa_supplicant_init_iface方法中会调用wpa_supplicant_set_driver方法。该方法中又会调用select_driver方法。

static int select_driver(struct wpa_supplicant *wpa_s, int i) {     struct wpa_global *global = wpa_s->global; if (wpa_drivers[i]->global_init && global->drv_priv[i] == NULL) {      //调用global_init方法,这与driver选择wext调用的流程不同了         global->drv_priv[i] = wpa_drivers[i]->global_init();         if (global->drv_priv[i] == NULL) {             wpa_printf(MSG_ERROR, "Failed to initialize driver "                     "‘%s‘", wpa_drivers[i]->name);              return -1;         }     }   // 根据name进行匹配,并最后保存到wpa_supplicant->dirver中     wpa_s->driver = wpa_drivers[i];     wpa_s->global_drv_priv = global->drv_priv[i];     return 0; }

3.接口操作函数实现

3.1用户态

代码:/external/wpa_supplicant_8/wpa_supplicant/src/drivers/driver_nl80211.c

const struct wpa_driver_ops wpa_driver_nl80211_ops = {     .name = "nl80211",     .desc = "Linux nl80211/cfg80211",     .get_bssid = wpa_driver_nl80211_get_bssid,     .get_ssid = wpa_driver_nl80211_get_ssid,     .set_key = wpa_driver_nl80211_set_key,     .scan2 = wpa_driver_nl80211_scan,     .sched_scan = wpa_driver_nl80211_sched_scan,     .stop_sched_scan = wpa_driver_nl80211_stop_sched_scan,     .get_scan_results2 = wpa_driver_nl80211_get_scan_results,     .deauthenticate = wpa_driver_nl80211_deauthenticate,     .disassociate = wpa_driver_nl80211_disassociate,     .authenticate = wpa_driver_nl80211_authenticate,     .associate = wpa_driver_nl80211_associate,     .global_init = nl80211_global_init,     .global_deinit = nl80211_global_deinit,     .init2 = wpa_driver_nl80211_init,     .deinit = wpa_driver_nl80211_deinit,     .get_capa = wpa_driver_nl80211_get_capa,     .set_operstate = wpa_driver_nl80211_set_operstate,     .set_supp_port = wpa_driver_nl80211_set_supp_port,     .set_country = wpa_driver_nl80211_set_country,     .set_ap = wpa_driver_nl80211_set_ap,     .if_add = wpa_driver_nl80211_if_add,     .if_remove = wpa_driver_nl80211_if_remove,     .send_mlme = wpa_driver_nl80211_send_mlme,     .get_hw_feature_data = wpa_driver_nl80211_get_hw_feature_data,     .sta_add = wpa_driver_nl80211_sta_add,     .sta_remove = wpa_driver_nl80211_sta_remove,     .hapd_send_eapol = wpa_driver_nl80211_hapd_send_eapol, #ifdef ANDROID_QCOM_PATCH     .hapd_set_countermeasures = wpa_driver_nl80211_set_countermeasures, #endif     .sta_set_flags = wpa_driver_nl80211_sta_set_flags, #ifdef HOSTAPD     .hapd_init = i802_init,     .hapd_deinit = i802_deinit,     .set_wds_sta = i802_set_wds_sta, #endif /* HOSTAPD */ #if defined(HOSTAPD) || defined(CONFIG_AP)     .get_seqnum = i802_get_seqnum,     .flush = i802_flush,     .read_sta_data = i802_read_sta_data,     .get_inact_sec = i802_get_inact_sec,     .sta_clear_stats = i802_sta_clear_stats,     .set_rts = i802_set_rts,     .set_frag = i802_set_frag,     .set_tx_queue_params = i802_set_tx_queue_params,     .set_sta_vlan = i802_set_sta_vlan,     .sta_deauth = i802_sta_deauth,     .sta_disassoc = i802_sta_disassoc, #endif /* HOSTAPD || CONFIG_AP */     .set_freq = i802_set_freq,     .send_action = wpa_driver_nl80211_send_action,     .send_action_cancel_wait = wpa_driver_nl80211_send_action_cancel_wait,     .remain_on_channel = wpa_driver_nl80211_remain_on_channel,     .cancel_remain_on_channel =     wpa_driver_nl80211_cancel_remain_on_channel,     .probe_req_report = wpa_driver_nl80211_probe_req_report,     .deinit_ap = wpa_driver_nl80211_deinit_ap,     .resume = wpa_driver_nl80211_resume,     .send_ft_action = nl80211_send_ft_action,     .signal_monitor = nl80211_signal_monitor,     .signal_poll = nl80211_signal_poll,     .send_frame = nl80211_send_frame,     .shared_freq = wpa_driver_nl80211_shared_freq,     .set_param = nl80211_set_param,     .get_radio_name = nl80211_get_radio_name,     .add_pmkid = nl80211_add_pmkid,     .remove_pmkid = nl80211_remove_pmkid,     .flush_pmkid = nl80211_flush_pmkid,     .set_rekey_info = nl80211_set_rekey_info,     .poll_client = nl80211_poll_client,     .set_p2p_powersave = nl80211_set_p2p_powersave, #ifdef CONFIG_TDLS     .send_tdls_mgmt = nl80211_send_tdls_mgmt,     .tdls_oper = nl80211_tdls_oper, #endif /* CONFIG_TDLS */ #ifdef ANDROID_P2P     .set_noa = wpa_driver_set_p2p_noa, #endif #ifdef ANDROID     .driver_cmd = wpa_driver_nl80211_driver_cmd, //处理DRIVER开头的命令 #endif };

ps:driver_cmd用于处理DRIVER的命令,调用流程如下:

wpa_supplicant_ctrl_iface_process-> (根据命令字符串调用对应的函数) wpa_supplicant_driver_cmd-> wpa_drv_driver_cmd-> wpa_s->driver->driver_cmd-> wpa_driver_nl80211_driver_cmd -> (User) ... cfg80211...

3.2 kernel态实现

Kernel态实现的操作函数,实现代码见:net/wireless/wext-compat.c

static const iw_handler cfg80211_handlers[] = {     [IW_IOCTL_IDX(SIOCGIWNAME)] = (iw_handler) cfg80211_wext_giwname,     [IW_IOCTL_IDX(SIOCSIWFREQ)] = (iw_handler) cfg80211_wext_siwfreq,     [IW_IOCTL_IDX(SIOCGIWFREQ)] = (iw_handler) cfg80211_wext_giwfreq,     [IW_IOCTL_IDX(SIOCSIWMODE)] = (iw_handler) cfg80211_wext_siwmode,     [IW_IOCTL_IDX(SIOCGIWMODE)] = (iw_handler) cfg80211_wext_giwmode,     [IW_IOCTL_IDX(SIOCGIWRANGE)]    = (iw_handler) cfg80211_wext_giwrange,     [IW_IOCTL_IDX(SIOCSIWAP)]   = (iw_handler) cfg80211_wext_siwap,     [IW_IOCTL_IDX(SIOCGIWAP)]   = (iw_handler) cfg80211_wext_giwap,     [IW_IOCTL_IDX(SIOCSIWMLME)] = (iw_handler) cfg80211_wext_siwmlme,     [IW_IOCTL_IDX(SIOCSIWSCAN)] = (iw_handler) cfg80211_wext_siwscan,     [IW_IOCTL_IDX(SIOCGIWSCAN)] = (iw_handler) cfg80211_wext_giwscan,     [IW_IOCTL_IDX(SIOCSIWESSID)]    = (iw_handler) cfg80211_wext_siwessid,     [IW_IOCTL_IDX(SIOCGIWESSID)]    = (iw_handler) cfg80211_wext_giwessid,     [IW_IOCTL_IDX(SIOCSIWRATE)] = (iw_handler) cfg80211_wext_siwrate,     [IW_IOCTL_IDX(SIOCGIWRATE)] = (iw_handler) cfg80211_wext_giwrate,     [IW_IOCTL_IDX(SIOCSIWRTS)]  = (iw_handler) cfg80211_wext_siwrts,     [IW_IOCTL_IDX(SIOCGIWRTS)]  = (iw_handler) cfg80211_wext_giwrts,     [IW_IOCTL_IDX(SIOCSIWFRAG)] = (iw_handler) cfg80211_wext_siwfrag,     [IW_IOCTL_IDX(SIOCGIWFRAG)] = (iw_handler) cfg80211_wext_giwfrag,     [IW_IOCTL_IDX(SIOCSIWTXPOW)]    = (iw_handler) cfg80211_wext_siwtxpower,     [IW_IOCTL_IDX(SIOCGIWTXPOW)]    = (iw_handler) cfg80211_wext_giwtxpower,     [IW_IOCTL_IDX(SIOCSIWRETRY)]    = (iw_handler) cfg80211_wext_siwretry,     [IW_IOCTL_IDX(SIOCGIWRETRY)]    = (iw_handler) cfg80211_wext_giwretry,     [IW_IOCTL_IDX(SIOCSIWENCODE)]   = (iw_handler) cfg80211_wext_siwencode,     [IW_IOCTL_IDX(SIOCGIWENCODE)]   = (iw_handler) cfg80211_wext_giwencode,     [IW_IOCTL_IDX(SIOCSIWPOWER)]    = (iw_handler) cfg80211_wext_siwpower,     [IW_IOCTL_IDX(SIOCGIWPOWER)]    = (iw_handler) cfg80211_wext_giwpower,     [IW_IOCTL_IDX(SIOCSIWGENIE)]    = (iw_handler) cfg80211_wext_siwgenie,     [IW_IOCTL_IDX(SIOCSIWAUTH)] = (iw_handler) cfg80211_wext_siwauth,     [IW_IOCTL_IDX(SIOCGIWAUTH)] = (iw_handler) cfg80211_wext_giwauth,     [IW_IOCTL_IDX(SIOCSIWENCODEEXT)]= (iw_handler) cfg80211_wext_siwencodeext,     [IW_IOCTL_IDX(SIOCSIWPMKSA)]    = (iw_handler) cfg80211_wext_siwpmksa, }; const struct iw_handler_def cfg80211_wext_handler = {     .num_standard       = ARRAY_SIZE(cfg80211_handlers),      .standard       = cfg80211_handlers,     .get_wireless_stats = cfg80211_wireless_stats, };

4.用户态和kernel态交互

4.1初始化

首先说明下用户态和kernel态交互的方式,如下所述:

a.用户态向kernel态发送请求时,通过ioctl来实现

b.kernel态向用户态发送事件通知,通过netlink实现

交互的初始化有两部分组成:nl80211_global_init和wpa_driver_nl80211_init方法。以上a/b两点中ioctl和netlink是在nl80211_global_init方法中创建。

(1) nl80211_global_init方法

因为在”2.保存驱动接口”,select_driver方法中调用了global_init方法(会根据用户态的结构体wpa_driver_nl80211_ops中查找对应方法,即nl80211_global_init)。

static void * nl80211_global_init(void) {     struct nl80211_global *global;     struct netlink_config *cfg;     global = os_zalloc(sizeof(*global));     if (global == NULL)         return NULL;     global->ioctl_sock = -1;     dl_list_init(&global->interfaces);     global->if_add_ifindex = -1;     cfg = os_zalloc(sizeof(*cfg));      if (cfg == NULL)         goto err;     cfg->ctx = global;     cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink;     cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink;     global->netlink = netlink_init(cfg); //初始化netlink,并注册事件接收函数      if (global->netlink == NULL) {              os_free(cfg);         goto err;     }       if (wpa_driver_nl80211_init_nl_global(global) < 0)                                                                                                                            goto err; // 此global->ioctl_sock用作为ioctl命令的fd     global->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);     if (global->ioctl_sock < 0) {               perror("socket(PF_INET,SOCK_DGRAM)");         goto err;     }       return global; err:     nl80211_global_deinit(global);     return NULL; }

在nl80211_global_init方法中,有两条关键语句:

(a) // 初始化netlink,并注册事件接收函数  global->netlink = netlink_init(cfg);  (b) // 此global->ioctl_sock用作为ioctl命令的fd global->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);

分析以上两句:

(a)netlink_init方法中创建了一个socket,并添加到eloop_run方法中的rfds中。用于从kernel态发送事件给用户态

netlink->sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); ...... eloop_register_read_sock(netlink->sock, netlink_receive, netlink,NULL);

(b)该socket用于从用户态发送请求给kernel态

(2)wpa_driver_nl80211_init方法

在wpa_supplicant_init_iface方法中有语句:

if (wpa_supplicant_set_driver(wpa_s, driver) < 0)     return -1; wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname);

在设置完驱动后,会调用wpa_drv_init方法,其方法体中会调用init2方法,即wpa_driver_nl80211_init。该方法用来Initialize nl80211 driver interface.

4.2 用户态和kernel态交互之ioctl实现

在用户态可简单执行一个ioctl(fd,cmd,...)命令即可。

先看下socket.c文件

/*  *  Socket files have a set of ‘special‘ operations as well as the generic file ones. These don‘t appear in the operation structures but are done directly via the socketcall() multiplexor.  */ static const struct file_operations socket_file_ops = {     .owner =    THIS_MODULE,     .llseek =   no_llseek,     .aio_read = sock_aio_read,     .aio_write =    sock_aio_write,     .poll =     sock_poll,     .unlocked_ioctl = sock_ioctl, // 这个就是被执行的ioctl  #ifdef CONFIG_COMPAT     .compat_ioctl = compat_sock_ioctl, #endif     .mmap =     sock_mmap,     .open =     sock_no_open,   /* special open code to disallow open via /proc */     .release =  sock_close,     .fasync =   sock_fasync,     .sendpage = sock_sendpage,     .splice_write = generic_splice_sendpage,     .splice_read =  sock_splice_read, };

从用户态调用sock_ioctl到kernel态调用iw_handler的执行流程如下:

sock_ioctl-> (kernel/net/socket.c)   dev_ioctl-> (kernel/net/core/dev.c)     下面的方法都在/net/wireless/wext-core.c中     wext_handle_ioctl-> (把执行结果从kernel态copy到用户态)       wext_ioctl_dispatch->(参数包括cmd/ioctl_standard_call/ioctl_private_call)         wireless_process_ioctl->           get_handler->  (根据cmd来判断调用standard或是private,即ioctl_standard_call或是ioctl_private_call方法)       ioctl_standard_call (执行cmd指定的iw_handler<cfg80211_handlers中定义的>,并返回结果)

这样就完成了”通过ioctl,用户态向kernel态发送请求”。

这个流程的代码稍后贴出。

sock_ioctl dev_ioctl wext_handle_ioctl wext_ioctl_dispatch wireless_process_ioctl ioctl_standard_call

4.3 用户态和kernel态交互之netlink实现

首先看netlink_init方法

struct netlink_data * netlink_init(struct netlink_config *cfg)                                                                                                            {     struct netlink_data *netlink;       struct sockaddr_nl local;      netlink = os_zalloc(sizeof(*netlink));     if (netlink == NULL)         return NULL;     netlink->cfg = cfg;     netlink->sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);                                                                                                              if (netlink->sock < 0) {         wpa_printf(MSG_ERROR, "netlink: Failed to open netlink "                "socket: %s", strerror(errno));         netlink_deinit(netlink);                return NULL;     }     os_memset(&local, 0, sizeof(local));     local.nl_family = AF_NETLINK;       local.nl_groups = RTMGRP_LINK;      if (bind(netlink->sock, (struct sockaddr *) &local, sizeof(local)) < 0)                                                                                                   {         wpa_printf(MSG_ERROR, "netlink: Failed to bind netlink "                "socket: %s", strerror(errno));         netlink_deinit(netlink);                return NULL;     }     eloop_register_read_sock(netlink->sock, netlink_receive, netlink,                                                                                                                      NULL);     return netlink; }

执行完netlink_init方法后,会通过eloop_register_read_sock方法将其中创建的socket以及callback方法注册到eloop_run方法中的rfds中,循环监听。一旦该socket有消息或事件变化,就执行netlink_receive方法。

static void netlink_receive(int sock, void *eloop_ctx, void *sock_ctx) {     struct netlink_data *netlink = eloop_ctx;     char buf[8192];     int left;     struct sockaddr_nl from;     socklen_t fromlen;     struct nlmsghdr *h;     int max_events = 10; try_again:     fromlen = sizeof(from);     left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,             (struct sockaddr *) &from, &fromlen); //从netlink读取事件     if (left < 0) {         if (errno != EINTR && errno != EAGAIN)             wpa_printf(MSG_INFO, "netlink: recvfrom failed: %s",                    strerror(errno));         return;     }     h = (struct nlmsghdr *) buf;     while (NLMSG_OK(h, left)) {         switch (h->nlmsg_type) {         case RTM_NEWLINK:             netlink_receive_link(netlink, netlink->cfg->newlink_cb,                          h); //a             break;         case RTM_DELLINK:             netlink_receive_link(netlink, netlink->cfg->dellink_cb,                          h);  //b             break;         }         h = NLMSG_NEXT(h, left);     }     if (left > 0) {         wpa_printf(MSG_DEBUG, "netlink: %d extra bytes in the end of "                "netlink message", left);     }     if (--max_events > 0) {         /*          * Try to receive all events in one eloop call in order to          * limit race condition on cases where AssocInfo event, Assoc          * event, and EAPOL frames are received more or less at the          * same time. We want to process the event messages first          * before starting EAPOL processing.          */         goto try_again;     } }

a/b中的方法调用,是在driver_nl80211.c中注册的,如下所示。

cfg->ctx = global; cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink; cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink;

这两个方法都会调用wpa_supplicant_event方法来处理。wpa_supplicant_event方法用来Report a driver event for wpa_supplicant。

所以这就完成了kernel向wpa_supplicant上传事件通知的过程了。

因此,kernel态向用户态发送事件通知(通过netlink)也已经分析完毕了。

wpa_supplicant与kernel交互

时间: 2024-10-13 07:11:10

wpa_supplicant与kernel交互的相关文章

如何处理VirtualBox启动错误消息:The vboxdrv kernel module is not loaded

我在启动minikube时,遇到如下错误消息: Starting local Kubernetes v1.10.0 cluster... Starting VM... E1010 03:27:37.920050 4827 start.go:174] Error starting host: Error creating host: Error executing step: Running precreate checks. : We support Virtualbox starting wi

xkungfoo 2015 参会笔记

有点像第二位演讲者玩命说的,"我给技术人员丢脸了",不同之处在于他演讲是去打广告的,而我挂着技术人员的称号,实际上却不太懂技术. 因此,往下看之前请慎重考虑是否要继续点鼠标或下拉条. 0x1 内容 跨进程域利用内核漏洞提升Android权限 打造移动金融核心程序安全流水线 关于Office Word程序的漏洞发掘 基于Binder调用攻击Android系统服务提权 社会工程学攻击的实例化 .... 0x2 跨进程利益内核漏洞提升Android权限 演讲者是申迪 @retme,之前曾搞过

Android电源管理简介(上)

文章转载自:http://blog.csdn.net/hzdysymbol/archive/2009/03/19/4004791.aspx Android Power Management 总体上来说Android的电源管理还是比较简单的, 主要就是通过锁和定时器来切换系统的状态,使系统的功耗降至最低,整个系统的电源管理架构图如下: (注该图来自Steve Guo) 接下来我们从Java应用层面, Android framework层面, Linux内核层面分别进行详细的讨论: 应用层的使用:

Linux基础:Linux(BASH)命令执行和搜索机制

程序是如何被执行的? 当操作系统启动之后,就会等待用户操作.那么用户是如何与操作系统沟通呢?如何触发程序执行呢? 通常,与操作系统交互,有两种实现方式:CLI(command line interface); CUI(graphic user interface). 比如,当我们启动 windows系统,我们想看电影,听音乐,那么我们必须去触发程序的快捷方式,以启动程序. 同理,Linux要与kernel交互,必须通过一个shell的程序.Linux命令的执行必须依赖于Shell命令解释器.Sh

Android热插拔事件处理详解

一.Android热插拔事件处理流程图 Android热插拔事件处理流程如下图所示: 二.组成 1. NetlinkManager: 全称是NetlinkManager.cpp位于Android 4.x 源码位置/system/vold/NetlinkManager.cpp.该类的主要通过引用NetlinkHandler类中的onEvent()方法来接收来自内核的事件消息,NetlinkHandler位于/system/vold/NetlinkHandler.cpp. 2. VolumeMana

【Linux】完全解读Linux环境变量

一.概述 环境变量:bash shell用一个称作"环境变量(environment variables)"的特性来存储有关shell会话和工作环境的信息,它允许你在内存中存储数据,以便运行在shell上的程序和脚本访问,这些数据可以用来识别用户.账户.系统.shell特性以及任何其他你需要存储的数据. shell中的环境变量有全局环境变量和局部环境变量,通过KV(variable=value)的形式声明一个局部变量,export这个局部变量,则升级成为全局环境变量.既然shell分开

音频的采集和播放

音频的采集和播放主要由专门的codec芯片完成,主流的codec芯片厂商有Circus Logic.Wolfson等.采集时codec芯片通过A/D采样把声音的模拟信号转换成数字信号并通过I2S总线送给CPU处理,播放时CPU把处理好的数字信号通过I2S总线送给codec芯片并通过D/A转换为模拟信号播放出来.codec芯片除了A/D, D/A功能外还有其他功能,主要有1)对音频通路进行控制,比如播放音乐打电话等在codec芯片内部的流通线路是不一样的.2)对音频信号做相应的处理,比如音量控制.

研究android系统架构层次

     如借用其它人员研究的wifi层次,对比android 下载后的源码.可看到android一层一层的架构层次是怎么样的. 并了解一个android系统是怎么跑起来的.     所以平时我的的android开发大部分是在java应用层,一些深层次点的就是ndk编码,会通过进行ndk编码.如proxydroid. 他的代理功能其实就是用到了ndk,通过jndi接口,调用到一个c++层,然后c++层次可以用到一些c++的类库,会fork一个后台进程 去进行iptable的代理. 如执行下述命令

docker架构及工作流程

一.概念docker是开源容器引擎,基于cgroup,namespace,unionFS等技术实现,对应用进行封装的虚拟化技术 什么是cgroup? 对系统资源限制,创建容器的过程其实就是在创建进程,对资源的分配和维护使用cgroup来管理,包括cpu,内存,io等? 什么是namespace? 创建容器时,对容器来说就是一个全新的系统,容器内的文件系统要和宿主机文件系统隔离,网络空间隔离,用户权限隔离,这些隔离操作都是有namespace 来管理完成的 什么是unionFS? 联合文件系统,简