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态发送请求”。
这个流程的代码稍后贴出。
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 }
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 }
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; }
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 }
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 }
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 }
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)也已经分析完毕了。