现在的位置: 首页 > 综合 > 正文

android_wifi读书笔记之6-wpa_supplicant 与kernel交互

2018年08月23日 ⁄ 综合 ⁄ 共 15267字 ⁄ 字号 评论关闭
文章目录

本文为读书笔记,整理自网络文献和源码

6 wpa_supplicant 与kernel交互

源码位置:external/wpa_supplicant_8/

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

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

2.保存驱动接口

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

4.交互

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

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

 

前3步都已经在5.2中描述过。

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)

……

6.1 kernel态实现

Kernel态实现的操作方法,实现代码见drivers/net/wireless/bcmdhd/wl_w.c

static const iw_handlerwl_iw_handler[] =

{

         (iw_handler)wl_iw_config_commit,      

         (iw_handler)wl_iw_get_name,               

         (iw_handler)NULL,                   

         (iw_handler)NULL,                   

         (iw_handler)wl_iw_set_freq,         

         (iw_handler)wl_iw_get_freq,                  

         (iw_handler)wl_iw_set_mode,               

         (iw_handler)wl_iw_get_mode,               

         (iw_handler)NULL,                   

         (iw_handler)NULL,                   

         (iw_handler)NULL,                   

         (iw_handler)wl_iw_get_range,               

         (iw_handler)wl_iw_set_priv,          

         (iw_handler)NULL,                   

         (iw_handler)NULL,                   

         (iw_handler)NULL,                   

         (iw_handler)wl_iw_set_spy,           

         (iw_handler)wl_iw_get_spy,          

         (iw_handler)NULL,                   

         (iw_handler)NULL,                   

         (iw_handler)wl_iw_set_wap,                  

         (iw_handler)wl_iw_get_wap,                  

#if WIRELESS_EXT > 17

         (iw_handler)wl_iw_mlme,              

#else

         (iw_handler)NULL,                   

#endif

#if defined(WL_IW_USE_ISCAN)

         (iw_handler)wl_iw_iscan_get_aplist,   

#else

         (iw_handler)wl_iw_get_aplist,               

#endif

#if WIRELESS_EXT > 13

#if defined(WL_IW_USE_ISCAN)

         (iw_handler)wl_iw_iscan_set_scan,     

         (iw_handler)wl_iw_iscan_get_scan,     

#else

         (iw_handler)wl_iw_set_scan,                 

         (iw_handler)wl_iw_get_scan,                 

#endif

#else        

         (iw_handler)NULL,                   

         (iw_handler)NULL,                   

#endif      

         (iw_handler)wl_iw_set_essid,                

         (iw_handler)wl_iw_get_essid,                

         (iw_handler)wl_iw_set_nick,         

         (iw_handler)wl_iw_get_nick,         

         (iw_handler)NULL,                   

         (iw_handler)NULL,                   

         (iw_handler)wl_iw_set_rate,                  

         (iw_handler)wl_iw_get_rate,                  

         (iw_handler)wl_iw_set_rts,           

         (iw_handler)wl_iw_get_rts,           

         (iw_handler)wl_iw_set_frag,         

         (iw_handler)wl_iw_get_frag,                  

         (iw_handler)wl_iw_set_txpow,               

         (iw_handler)wl_iw_get_txpow,              

#if WIRELESS_EXT > 10

         (iw_handler)wl_iw_set_retry,                 

         (iw_handler)wl_iw_get_retry,                 

#endif

         (iw_handler)wl_iw_set_encode,            

         (iw_handler)wl_iw_get_encode,            

         (iw_handler)wl_iw_set_power,              

         (iw_handler)wl_iw_get_power,              

#if WIRELESS_EXT > 17

         (iw_handler)NULL,                   

         (iw_handler)NULL,                   

         (iw_handler)wl_iw_set_wpaie,               

         (iw_handler)wl_iw_get_wpaie,              

         (iw_handler)wl_iw_set_wpaauth,         

         (iw_handler)wl_iw_get_wpaauth,         

         (iw_handler)wl_iw_set_encodeext,      

         (iw_handler)wl_iw_get_encodeext,      

         (iw_handler)wl_iw_set_pmksa,                       

#endif

};

const struct iw_handler_defwl_iw_handler_def =

{

         .num_standard= ARRAYSIZE(wl_iw_handler),

         .standard= (iw_handler *) wl_iw_handler,

         .num_private= ARRAYSIZE(wl_iw_priv_handler),

         .num_private_args= ARRAY_SIZE(wl_iw_priv_args),

         .private= (iw_handler *)wl_iw_priv_handler,

         .private_args= (void *) wl_iw_priv_args,

 

#if WIRELESS_EXT >= 19

         get_wireless_stats:dhd_get_wireless_stats,

#endif

         };

#endif

 

static const iw_handler wl_iw_handler[]与以下的宏顺序一一对应:

/* -------------------------- IOCTL LIST-------------------------- */

#define SIOCSIWCOMMIT       0x8B00              /*Commit pending changes to driver */

#define SIOCGIWNAME  0x8B01              /* getname == wireless protocol */

#define SIOCSIWNWID    0x8B02              /* setnetwork id (pre-802.11) */

#define SIOCGIWNWID   0x8B03              /* getnetwork id (the cell) */

#define SIOCSIWFREQ     0x8B04              /* setchannel/frequency (Hz) */

#define SIOCGIWFREQ    0x8B05              /* getchannel/frequency (Hz) */

#define SIOCSIWMODE   0x8B06              /* setoperation mode */

#define SIOCGIWMODE  0x8B07              /* getoperation mode */

#define SIOCSIWSENS      0x8B08              /* setsensitivity (dBm) */

#define SIOCGIWSENS     0x8B09              /* getsensitivity (dBm) */

#define SIOCSIWRANGE  0x8B0A              /* Unused*/

#define SIOCGIWRANGE 0x8B0B              /* Getrange of parameters */

#define SIOCSIWPRIV      0x8B0C              /*Unused */

#define SIOCGIWPRIV     0x8B0D             /* getprivate ioctl interface info */

#define SIOCSIWSTATS     0x8B0E              /*Unused */

#define SIOCGIWSTATS    0x8B0F              /* Get/proc/net/wireless stats */

#define SIOCSIWSPY         0x8B10              /* setspy addresses */

#define SIOCGIWSPY        0x8B11              /*get spy info (quality of link) */

#define SIOCSIWTHRSPY 0x8B12              /*set spy threshold (spy event) */

#define SIOCGIWTHRSPY         0x8B13              /*get spy threshold */

#define SIOCSIWAP 0x8B14              /* setaccess point MAC addresses */

#define SIOCGIWAP          0x8B15              /*get access point MAC addresses */

#define SIOCGIWAPLIST  0x8B17              /*Deprecated in favor of scanning */

#define SIOCSIWSCAN     0x8B18              /*trigger scanning (list cells) */

#define SIOCGIWSCAN    0x8B19              /* getscanning results */

#define SIOCSIWESSID     0x8B1A              /* setESSID (network name) */

#define SIOCGIWESSID    0x8B1B              /* getESSID */

#define SIOCSIWNICKN   0x8B1C              /* setnode name/nickname */

#define SIOCGIWNICKN  0x8B1D             /* get nodename/nickname */

#define SIOCSIWRATE      0x8B20              /* setdefault bit rate (bps) */

#define SIOCGIWRATE     0x8B21              /* getdefault bit rate (bps) */

#define SIOCSIWRTS        0x8B22              /*set RTS/CTS threshold (bytes) */

#define SIOCGIWRTS       0x8B23              /*get RTS/CTS threshold (bytes) */

#define SIOCSIWFRAG     0x8B24              /* setfragmentation thr (bytes) */

#define SIOCGIWFRAG    0x8B25              /* getfragmentation thr (bytes) */

#define SIOCSIWTXPOW 0x8B26              /* settransmit power (dBm) */

#define SIOCGIWTXPOW          0x8B27              /*get transmit power (dBm) */

#define SIOCSIWRETRY   0x8B28              /* setretry limits and lifetime */

#define SIOCGIWRETRY  0x8B29              /* getretry limits and lifetime */

#define SIOCSIWENCODE        0x8B2A              /*set encoding token & mode */

#define SIOCGIWENCODE       0x8B2B              /*get encoding token & mode */

#define SIOCSIWPOWER 0x8B2C           /* set PowerManagement settings */

#define SIOCGIWPOWER 0x8B2D            /* get Power Management settings */

#define SIOCSIWGENIE   0x8B30              /* setgeneric IE */

#define SIOCGIWGENIE  0x8B31              /* getgeneric IE */

#define SIOCSIWMLME   0x8B16              /*request MLME operation; uses * struct iw_mlme */

#define SIOCSIWAUTH     0x8B32              /* setauthentication mode params */

#define SIOCGIWAUTH    0x8B33              /* getauthentication mode params */

#define SIOCSIWENCODEEXT 0x8B34              /* set encoding token & mode*/

#define SIOCGIWENCODEEXT 0x8B35             /* get encoding token & mode */

#define SIOCSIWPMKSA  0x8B36              /* PMKSAcache operation */

#define SIOCIWFIRSTPRIV       0x8BE0

#define SIOCIWLASTPRIV         0x8BFF

 

6.2用户态(wpa_supplicant)和kernel态交互初始化

wpa_supplicant与kernel交互是通过以下三个socket实现的:

1.PF_INET socket接口,主要用于向kernel发送ioctl命令,控制并获取相应信息。

2.PF_NETLINK socket接口,主要用于接收kernel发送上来的event 事件。

3.PF_PACKET socket接口,主要用于向driver传递802.1X报文。

 

driver.h”,“drivers.c”主要用于封装底层差异对外显示一个相同的 wpa_driver_ops接口。Wpa_supplicant可支持atmel, Broadcom, ipw, madwifi, ndis, nl80211, wext等多种驱动。其中一个最主要的数据结构为wpa_driver_ops, 其定义了driver相关的各种操作接口。“driver_ nl80211.c”实现了nl80211形式的wpa_driver_ops,并创建了PF_INET socket接口和PF_NETLINK
socket接口,然后通过这两个接口完成与kernel的信息交互。

 

6.2.1、PF_INET和PF_NETLINK的初始化:

在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。在wpa_driver_nl80211_init方法中:

static void * wpa_driver_nl80211_init(void*ctx, const char *ifname,

                                           void *global_priv)

{

         if(wpa_driver_nl80211_init_nl(drv)) {

                   os_free(drv);

                   returnNULL;

         }

         nl80211_get_phy_name(drv);

         drv->ioctl_sock= socket(PF_INET, SOCK_DGRAM, 0);

         if(drv->ioctl_sock < 0) {

                   perror("socket(PF_INET,SOCK_DGRAM)");

                   gotofailed;

         }

         cfg= os_zalloc(sizeof(*cfg));

         if(cfg == NULL)

                   gotofailed;

         cfg->ctx= drv;

         cfg->newlink_cb= wpa_driver_nl80211_event_rtm_newlink;

         cfg->dellink_cb= wpa_driver_nl80211_event_rtm_dellink;

         drv->netlink= netlink_init(cfg);

         if(drv->netlink == NULL) {

                   os_free(cfg);

                   gotofailed;

         }

         rcfg= os_zalloc(sizeof(*rcfg));

         if(rcfg == NULL)

                   gotofailed;

         rcfg->ctx= drv;

         os_strlcpy(rcfg->ifname,ifname, sizeof(rcfg->ifname));

         rcfg->blocked_cb= wpa_driver_nl80211_rfkill_blocked;

         rcfg->unblocked_cb= wpa_driver_nl80211_rfkill_unblocked;

         drv->rfkill= rfkill_init(rcfg);

         if(drv->rfkill == NULL) {

                   wpa_printf(MSG_DEBUG,"nl80211: RFKILL status not available");

                   os_free(rcfg);

         }

         if(wpa_driver_nl80211_finish_drv_init(drv))

                   gotofailed;

         if(drv->global)

                   dl_list_add(&drv->global->interfaces,&drv->list);

……

}

关键的语句:

(a)

// 初始化netlink,并注册事件接收方法

drv->netlink = netlink_init(cfg);

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

 

// 此ioctl_sock用作为ioctl命令的fd

drv->ioctl_sock = socket(PF_INET,SOCK_DGRAM, 0);

该socket用于从用户态发送请求给kernel态。

 

6.2.2、PF_PACKET的初始化:

“l2_packet.h”和“l2_packet_linux.c”主要用于实现PF_PACKET socket接口,通过该接口,wpa_supplicant可以直接将802.1X packet发送到L2层,而不经过TCP/IP协议栈。在l2_packet_linux.c源文件中的l2_packet_init方法中创建PF_PACKET的socket,如下:

l2->fd = socket(PF_PACKET, l2_hdr ?SOCK_RAW : SOCK_DGRAM, htons(protocol));

另外两个方法,用于报文的发送和接收:

int l2_packet_send(struct l2_packet_data*l2, const u8 *dst_addr, u16 proto, const u8 *buf, size_t len);

static void l2_packet_receive(int sock,void *eloop_ctx, void *sock_ctx);

 

 

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

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

先看下socket.c文件

 

/*

 * Socket files have a set of 'special' operations as well as the genericfile ones. These don't appear in the operation structures but are done directly

via the socketcall() multiplexor.

 */

static const struct file_operationssocket_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

 

 

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

 

首先看netlink_init方法

 

struct netlink_data * netlink_init(structnetlink_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)也已经分析完毕了。

抱歉!评论已关闭.