本文为读书笔记,整理自网络文献和源码
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)也已经分析完毕了。