wpa_supplicant与kernel交互

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

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

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

};

psdriver_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_initwpa_driver_nl80211_init方法。以上a/b两点中ioctlnetlink是在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_ioctlkernel态调用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态发送请求”。

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

 1 /*

 2  *    With an ioctl, arg may well be a user mode pointer, but we don't know

 3  *    what to do with it - that's up to the protocol still.

 4  */

 5 

 6 static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)

 7 {

 8     struct socket *sock;

 9     struct sock *sk;

10     void __user *argp = (void __user *)arg;

11     int pid, err;

12     struct net *net;

13 

14     sock = file->private_data;

15     sk = sock->sk;

16     net = sock_net(sk);

17     if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) {

18         err = dev_ioctl(net, cmd, argp);

19     } else

20 #ifdef CONFIG_WEXT_CORE

21     if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {

22         err = dev_ioctl(net, cmd, argp);

23     } else

24 #endif

25         switch (cmd) {

26         case FIOSETOWN:

27         case SIOCSPGRP:

28             err = -EFAULT;

29             if (get_user(pid, (int __user *)argp))

30                 break;

31             err = f_setown(sock->file, pid, 1);

32             break;

33         case FIOGETOWN:

34         case SIOCGPGRP:

35             err = put_user(f_getown(sock->file),

36                        (int __user *)argp);

37             break;

38         case SIOCGIFBR:

39         case SIOCSIFBR:

40         case SIOCBRADDBR:

41         case SIOCBRDELBR:

42             err = -ENOPKG;

43             if (!br_ioctl_hook)

44                 request_module("bridge");

45 

46             mutex_lock(&br_ioctl_mutex);

47             if (br_ioctl_hook)

48                 err = br_ioctl_hook(net, cmd, argp);

49             mutex_unlock(&br_ioctl_mutex);

50             break;

51         case SIOCGIFVLAN:

52         case SIOCSIFVLAN:

53             err = -ENOPKG;

54             if (!vlan_ioctl_hook)

55                 request_module("8021q");

56 

57             mutex_lock(&vlan_ioctl_mutex);

58             if (vlan_ioctl_hook)

59                 err = vlan_ioctl_hook(net, argp);

60             mutex_unlock(&vlan_ioctl_mutex);

61             break;

62         case SIOCADDDLCI:

63         case SIOCDELDLCI:

64             err = -ENOPKG;

65             if (!dlci_ioctl_hook)

66                 request_module("dlci");

67 

68             mutex_lock(&dlci_ioctl_mutex);

69             if (dlci_ioctl_hook)

70                 err = dlci_ioctl_hook(cmd, argp);

71             mutex_unlock(&dlci_ioctl_mutex);

72             break;

73         default:

74             err = sock_do_ioctl(net, sock, cmd, arg);

75             break;

76         }

77     return err;

78 }
sock_ioctl
  1 /**

  2  *    dev_ioctl    -    network device ioctl

  3  *    @net: the applicable net namespace

  4  *    @cmd: command to issue

  5  *    @arg: pointer to a struct ifreq in user space

  6  *

  7  *    Issue ioctl functions to devices. This is normally called by the

  8  *    user space syscall interfaces but can sometimes be useful for

  9  *    other purposes. The return value is the return from the syscall if

 10  *    positive or a negative errno code on error.

 11  */

 12 

 13 int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)

 14 {

 15     struct ifreq ifr;

 16     int ret;

 17     char *colon;

 18 

 19     /* One special case: SIOCGIFCONF takes ifconf argument

 20        and requires shared lock, because it sleeps writing

 21        to user space.

 22      */

 23 

 24     if (cmd == SIOCGIFCONF) {

 25         rtnl_lock();

 26         ret = dev_ifconf(net, (char __user *) arg);

 27         rtnl_unlock();

 28         return ret;

 29     }

 30     if (cmd == SIOCGIFNAME)

 31         return dev_ifname(net, (struct ifreq __user *)arg);

 32 

 33     if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))

 34         return -EFAULT;

 35 

 36     ifr.ifr_name[IFNAMSIZ-1] = 0;

 37 

 38     colon = strchr(ifr.ifr_name, ':');

 39     if (colon)

 40         *colon = 0;

 41 

 42     /*

 43      *    See which interface the caller is talking about.

 44      */

 45 

 46     switch (cmd) {

 47     /*

 48      *    These ioctl calls:

 49      *    - can be done by all.

 50      *    - atomic and do not require locking.

 51      *    - return a value

 52      */

 53     case SIOCGIFFLAGS:

 54     case SIOCGIFMETRIC:

 55     case SIOCGIFMTU:

 56     case SIOCGIFHWADDR:

 57     case SIOCGIFSLAVE:

 58     case SIOCGIFMAP:

 59     case SIOCGIFINDEX:

 60     case SIOCGIFTXQLEN:

 61         dev_load(net, ifr.ifr_name);

 62         rcu_read_lock();

 63         ret = dev_ifsioc_locked(net, &ifr, cmd);

 64         rcu_read_unlock();

 65         if (!ret) {

 66             if (colon)

 67                 *colon = ':';

 68             if (copy_to_user(arg, &ifr,

 69                      sizeof(struct ifreq)))

 70                 ret = -EFAULT;

 71         }

 72         return ret;

 73 

 74     case SIOCETHTOOL:

 75         dev_load(net, ifr.ifr_name);

 76         rtnl_lock();

 77         ret = dev_ethtool(net, &ifr);

 78         rtnl_unlock();

 79         if (!ret) {

 80             if (colon)

 81                 *colon = ':';

 82             if (copy_to_user(arg, &ifr,

 83                      sizeof(struct ifreq)))

 84                 ret = -EFAULT;

 85         }

 86         return ret;

 87 

 88     /*

 89      *    These ioctl calls:

 90      *    - require superuser power.

 91      *    - require strict serialization.

 92      *    - return a value

 93      */

 94     case SIOCGMIIPHY:

 95     case SIOCGMIIREG:

 96     case SIOCSIFNAME:

 97         if (!capable(CAP_NET_ADMIN))

 98             return -EPERM;

 99         dev_load(net, ifr.ifr_name);

100         rtnl_lock();

101         ret = dev_ifsioc(net, &ifr, cmd);

102         rtnl_unlock();

103         if (!ret) {

104             if (colon)

105                 *colon = ':';

106             if (copy_to_user(arg, &ifr,

107                      sizeof(struct ifreq)))

108                 ret = -EFAULT;

109         }

110         return ret;

111 

112     /*

113      *    These ioctl calls:

114      *    - require superuser power.

115      *    - require strict serialization.

116      *    - do not return a value

117      */

118     case SIOCSIFFLAGS:

119     case SIOCSIFMETRIC:

120     case SIOCSIFMTU:

121     case SIOCSIFMAP:

122     case SIOCSIFHWADDR:

123     case SIOCSIFSLAVE:

124     case SIOCADDMULTI:

125     case SIOCDELMULTI:

126     case SIOCSIFHWBROADCAST:

127     case SIOCSIFTXQLEN:

128     case SIOCSMIIREG:

129     case SIOCBONDENSLAVE:

130     case SIOCBONDRELEASE:

131     case SIOCBONDSETHWADDR:

132     case SIOCBONDCHANGEACTIVE:

133     case SIOCBRADDIF:

134     case SIOCBRDELIF:

135     case SIOCSHWTSTAMP:

136         if (!capable(CAP_NET_ADMIN))

137             return -EPERM;

138         /* fall through */

139     case SIOCBONDSLAVEINFOQUERY:

140     case SIOCBONDINFOQUERY:

141         dev_load(net, ifr.ifr_name);

142         rtnl_lock();

143         ret = dev_ifsioc(net, &ifr, cmd);

144         rtnl_unlock();

145         return ret;

146 

147     case SIOCGIFMEM:

148         /* Get the per device memory space. We can add this but

149          * currently do not support it */

150     case SIOCSIFMEM:

151         /* Set the per device memory buffer space.

152          * Not applicable in our case */

153     case SIOCSIFLINK:

154         return -ENOTTY;

155 

156     /*

157      *    Unknown or private ioctl.

158      */

159     default:

160         if (cmd == SIOCWANDEV ||

161             (cmd >= SIOCDEVPRIVATE &&

162              cmd <= SIOCDEVPRIVATE + 15)) {

163             dev_load(net, ifr.ifr_name);

164             rtnl_lock();

165             ret = dev_ifsioc(net, &ifr, cmd);

166             rtnl_unlock();

167             if (!ret && copy_to_user(arg, &ifr,

168                          sizeof(struct ifreq)))

169                 ret = -EFAULT;

170             return ret;

171         }

172         /* Take care of Wireless Extensions */

173         if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST)

174             return wext_handle_ioctl(net, &ifr, cmd, arg);  //执行wext_handle_ioctl方法

175         return -ENOTTY;

176     }

177 }
dev_ioctl
int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd,

              void __user *arg)

{

    struct iw_request_info info = { .cmd = cmd, .flags = 0 };

    int ret;



    ret = wext_ioctl_dispatch(net, ifr, cmd, &info,

                  ioctl_standard_call,

                  ioctl_private_call); //调用wext_ioctl_dispatch方法

    if (ret >= 0 &&

        IW_IS_GET(cmd) &&

        copy_to_user(arg, ifr, sizeof(struct iwreq)))  //将执行结果从kernel态copy到用户态

        return -EFAULT;



    return ret;

}
wext_handle_ioctl
 1 /* entry point from dev ioctl */

 2 static int wext_ioctl_dispatch(struct net *net, struct ifreq *ifr,

 3                    unsigned int cmd, struct iw_request_info *info,

 4                    wext_ioctl_func standard,

 5                    wext_ioctl_func private)

 6 {

 7     int ret = wext_permission_check(cmd);

 8 

 9     if (ret)

10         return ret;

11 

12     dev_load(net, ifr->ifr_name);

13     rtnl_lock();

14     ret = wireless_process_ioctl(net, ifr, cmd, info, standard, private); //调用wireless_process_ioctl方法

15     rtnl_unlock();

16 

17     return ret;

18 }
wext_ioctl_dispatch
 1 /*

 2  * Main IOCTl dispatcher.

 3  * Check the type of IOCTL and call the appropriate wrapper...

 4  */

 5 static int wireless_process_ioctl(struct net *net, struct ifreq *ifr,

 6                   unsigned int cmd,

 7                   struct iw_request_info *info,

 8                   wext_ioctl_func standard,

 9                   wext_ioctl_func private)

10 {

11     struct iwreq *iwr = (struct iwreq *) ifr;

12     struct net_device *dev;

13     iw_handler    handler;

14 

15     /* Permissions are already checked in dev_ioctl() before calling us.

16      * The copy_to/from_user() of ifr is also dealt with in there */

17 

18     /* Make sure the device exist */

19     if ((dev = __dev_get_by_name(net, ifr->ifr_name)) == NULL)

20         return -ENODEV;

21 

22     /* A bunch of special cases, then the generic case...

23      * Note that 'cmd' is already filtered in dev_ioctl() with

24      * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */

25     if (cmd == SIOCGIWSTATS)

26         return standard(dev, iwr, cmd, info,

27                 &iw_handler_get_iwstats);

28 

29 #ifdef CONFIG_WEXT_PRIV

30     if (cmd == SIOCGIWPRIV && dev->wireless_handlers)

31         return standard(dev, iwr, cmd, info,

32                 iw_handler_get_private);

33 #endif

34 

35     /* Basic check */

36     if (!netif_device_present(dev))

37         return -ENODEV;

38 

39     /* New driver API : try to find the handler */

40     handler = get_handler(dev, cmd);  //调用get_handler

41     if (handler) {

42         /* Standard and private are not the same */

43         if (cmd < SIOCIWFIRSTPRIV)

44             return standard(dev, iwr, cmd, info, handler);

45         else if (private)

46             return private(dev, iwr, cmd, info, handler);

47     }

48     /* Old driver API : call driver ioctl handler */

49     if (dev->netdev_ops->ndo_do_ioctl)

50         return dev->netdev_ops->ndo_do_ioctl(dev, ifr, cmd);

51     return -EOPNOTSUPP;

52 }
wireless_process_ioctl
 1 /*

 2  * Wrapper to call a standard Wireless Extension handler.

 3  * We do various checks and also take care of moving data between

 4  * user space and kernel space.

 5  */

 6 static int ioctl_standard_call(struct net_device *    dev,

 7                    struct iwreq        *iwr,

 8                    unsigned int        cmd,

 9                    struct iw_request_info    *info,

10                    iw_handler        handler)

11 {

12     const struct iw_ioctl_description *    descr;

13     int                    ret = -EINVAL;

14 

15     /* Get the description of the IOCTL */

16     if (IW_IOCTL_IDX(cmd) >= standard_ioctl_num)

17         return -EOPNOTSUPP;

18     descr = &(standard_ioctl[IW_IOCTL_IDX(cmd)]);

19 

20     /* Check if we have a pointer to user space data or not */

21     if (descr->header_type != IW_HEADER_TYPE_POINT) {

22 

23         /* No extra arguments. Trivial to handle */

24         ret = handler(dev, info, &(iwr->u), NULL);

25 

26         /* Generate an event to notify listeners of the change */

27         if ((descr->flags & IW_DESCR_FLAG_EVENT) &&

28            ((ret == 0) || (ret == -EIWCOMMIT)))

29             wireless_send_event(dev, cmd, &(iwr->u), NULL);

30     } else {

31         ret = ioctl_standard_iw_point(&iwr->u.data, cmd, descr,

32                           handler, dev, info);

33     }

34 

35     /* Call commit handler if needed and defined */

36     if (ret == -EIWCOMMIT)

37         ret = call_commit_handler(dev);

38 

39     /* Here, we will generate the appropriate event if needed */

40 

41     return ret;

42 }
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

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


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

 

你可能感兴趣的:(kernel)