zoukankan      html  css  js  c++  java
  • 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)也已经分析完毕了。

  • 相关阅读:
    JS复制内容到剪切板
    mysql root密码的重设方法(转)
    php生成excel文件示例代码(转)
    php读取文件内容的三种方式(转)
    使用火蜘蛛采集器Firespider采集天猫商品数据并上传到微店
    Mac Android8.0源码编译笔记
    开源 高性能 高可用 可扩展
    开源 模式
    开源 算法 数据结构
    mdb
  • 原文地址:https://www.cnblogs.com/chenbin7/p/3300044.html
Copyright © 2011-2022 走看看