- wpa_supplicant结构体与网络接口
在手机adb中运行 netcfg或者ifconfig可以看到相关的网络接口的ip,掩码,mac地址等信息
Wpa_supplicant为每个网络接口都分配了一个struct wpa_supplicant, 该结构体存储了一些必要信息例如 struct dl_list bss(扫描结果); struct wpa_config *conf(配置文件)等等。
每一个网络接口的扫描,连接等操作都是通过struct wpa_supplicant中定义的相关函数和数据来实现的。下面是结构体中一些重要的元素介绍
struct wpa_supplicant{
//接口的名字和mac地址
unsigned char own_addr[ETH_ALEN];
char ifname[100];
/**************************************/
//外部控制wpa_s的socket
struct ctrl_iface_priv *ctrl_iface;
//网络接口当前的状态,断开/关联/连接的bssid ssid等
enum wpa_states wpa_state;
u8 bssid[ETH_ALEN];
u8 pending_bssid[ETH_ALEN]; /* If wpa_state == WPA_ASSOCIATING, this
* field contains the target BSSID. */
int reassociate; /* reassociation requested */
int disconnected; /* all connections disabled; i.e., do no reassociate
* before this has been cleared */
struct wpa_ssid *current_ssid;
struct wpa_bss *current_bss;
int ap_ies_from_associnfo;
unsigned int assoc_freq;
//是否支持2.4G/5G
enum { WPA_SETBAND_AUTO, WPA_SETBAND_5G, WPA_SETBAND_2G } setband;
/**************************************/
//配置文件
struct wpa_config *conf;
//配置文件中的ESS网络的信息
/* Selected configuration (based on Beacon/ProbeResp WPA IE) */
int pairwise_cipher;
int group_cipher;
int key_mgmt;
int wpa_proto;
int mgmt_group_cipher;
/**************************************/
//扫描结果的处理,每次扫描后的结果都会做相应的更新
void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res);
struct dl_list bss; /* struct wpa_bss::list */
struct dl_list bss_id; /* struct wpa_bss::list_id */
size_t num_bss;
unsigned int bss_update_idx;
unsigned int bss_next_id;
//黑名单
struct wpa_blacklist *blacklist;
/**************************************/
//底层相关,驱动的操作函数
struct wpa_driver_ops *driver;
int interface_removed; /* whether the network interface has been removed */
void *drv_priv; /* private data used by driver_ops */
void *global_drv_priv;
//用来处理密钥相关netlink socket和处理方法
struct l2_packet_data *l2;
struct l2_packet_data *l2_br;
/**************************************/
//扫描相关的一些信息
struct wpa_radio_work *scan_work;
int scanning;
int sched_scanning;
struct os_reltime scan_trigger_time, scan_start_time;
int scan_runs; /* number of scan runs since WPS was started */
int *next_scan_freqs;
int scan_interval; /* time in sec between scans to find suitable AP */
int normal_scans; /* normal scans run before sched_scan */
int scan_for_connection; /* whether the scan request was triggered for
* finding a connection */
//sched_scan
struct wpa_ssid *prev_sched_ssid; /* last SSID used in sched scan */
int sched_scan_timeout;
int sched_scan_interval;
int first_sched_scan;
int sched_scan_timed_out;
//pno扫描
int pno;
int pno_sched_pending;
}
- 初始化网络接口配置
在wpa_s的main()函数中,进行完全局初始化后,会调用 wpa_supplicant_add_iface(),为wpa_s运行时传入的每个网络接口分配wpa_supplicant结构体,并进行相应的初始化
struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
struct wpa_interface *iface)
{
struct wpa_supplicant *wpa_s = wpa_supplicant_alloc();
wpa_s->global = global;
//网络接口的初始化,与底层通信的socket,密钥数据包的socket,wpa_s的初始状态等
if (wpa_supplicant_init_iface(wpa_s, &t_iface))
{
//读取两个conf配置文件
//wpa_s允许提供两个配置文件,一个是原始的一个overlay,在overlay中的文件可以重新一些参数的值
wpa_s->conf = wpa_config_read(wpa_s->confname, NULL);
wpa_config_read(wpa_s->confanother, wpa_s->conf);
//初始化驱动接口,并注册接收驱动event的函数 nl80211
if (wpas_init_driver(wpa_s, iface) < 0)
{
//根据传进来的-inl80211选定对应的wpa_driver_ops(driver_nl80211.c),里面定义了驱动的操作方法,同时调用对应的global_init()进行初始化
if (wpa_supplicant_set_driver(wpa_s, driver) < 0)
...
- 列表内容
驱动初始化
下一步是初始化相关驱动的操作参数和与驱动通信的socket
wpa_supplicant支持多种架构的驱动,Android中多使用的是nl80211架构,相关操作函数在文件drivers_ml80211.c中
//根据名称选择网络ops,创建于driver通信的socket
select_driver(wpa_s, i)
{
//调用对应的global_init(),void * nl80211_global_init(void)
global->drv_priv[i] = wpa_drivers[i]->global_init();
{
//初始化netlink socket,设置两个消息处理函数,netlink_receive中使用
cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink;
cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink;
//创建一个接收kernel中 route 子系统的消息socket
global->netlink = netlink_init(cfg);
{
//*****该socket用于接收 kernel中
netlink->sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
bind(netlink->sock, (struct sockaddr *) &local, sizeof(local));
//注册内核消息接收函数, 根据接收到的消息type选择以下两个处理函数
eloop_register_read_sock(netlink->sock, netlink_receive, netlink, NULL);
static void netlink_receive(int sock, void *eloop_ctx, void *sock_ctx)
{
char buf[8192]; //接收消息的字节数,
left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr *) &from, &fromlen);
//buf中是一个netlink消息,符合netlink规范,根据消息type选在相应的处理函数 newlink 和 dellink
}
}
//创建 global结构体中的 nl_cb(接收消息处理)和nl(发送消息)
if (wpa_driver_nl80211_init_nl_global(global) < 0)
{
//创建连接到 GENERIC_NETLINK 的socket
global->nl = nl_create_handle(global->nl_cb, "nl");
//将nl连接到 内核中的nl80211模块
global->nl80211_id = genl_ctrl_resolve(global->nl, "nl80211");
//创建接收消息的event socket, 并分别加入到组播组scan mlme regulatory vendor 中
global->nl_event = nl_create_handle(global->nl_cb, "event");
ret = nl_get_multicast_id(global, "nl80211", "scan");
ret = nl_socket_add_membership(global->nl_event, ret);
//设置nl_cb的回调函数 process_global_event(), 该函数又会调用 wpa_supplicant_event() 做进一步处理
nl_cb_set(global->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, process_global_event, global);
//为nl_event注册接收函数, 通过nl_recvmsgs()调用 process_global_event()
nl80211_register_eloop_read(&global->nl_event, wpa_driver_nl80211_event_receive, global->nl_cb);
}
//创建一个 ioctl_sock, 还不知道干啥用,该sock用于通过ioctl方式向底层发送命令
global->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
}
}
- 网络接口初始配置
例如up对应的接口
//调用对应的初始化函数wpa_driver_nl80211_drv_init(),传入的ifname名称为 wlan0
wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname);
static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname, void *global_priv, int hostapd, const u8 *set_addr)
{
//挺重要的一个结构体
struct wpa_driver_nl80211_data *drv;
//添加了回调函数drv->nl_cb process_drv_event(),最终会调用 wpa_supplicant_event()
if (wpa_driver_nl80211_init_nl(drv))
//添加bss回调函数bss->nl_cb,看着没啥用,最终处理 wpa_supplicant_event(),这两个回调函数没有和socket绑定,目前不清楚调用地方
if (nl80211_init_bss(bss))
//RF射频省电相关,读取节点“/dev/rfkill”只是接受消息打印log,不做任何操作
rcfg->blocked_cb = wpa_driver_nl80211_rfkill_blocked;
rcfg->unblocked_cb = wpa_driver_nl80211_rfkill_unblocked;
drv->rfkill = rfkill_init(rcfg);
//检查当前接口是否为UP状态
linux_iface_up(drv->global->ioctl_sock, ifname)
//主要为操作网卡wlan0
wpa_driver_nl80211_finish_drv_init(drv, set_addr, 1)
{
//得知网卡地址和设置网口为UP
if (set_addr && (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0) || linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname, set_addr)))
//获取网口能力,具体能力请查阅struct wpa_driver_capa
if (wpa_driver_nl80211_capa(drv))
//设置网口信息 IF_OPER_DORMANT
netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, 1, IF_OPER_DORMANT);
//获取网口地址
if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, bss->addr))
}
//创建一个socket,并注册接收函数wpa_driver_nl80211_handle_eapol_tx_status
drv->eapol_tx_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
eloop_register_read_sock(drv->eapol_tx_sock, wpa_driver_nl80211_handle_eapol_tx_status, drv, NULL);
}
5 密钥处理数据包收发socekt
由netlink创建
//初始化驱动,主要完成了l2_paket回掉函数的初始化,与4次握手相关
if (wpa_supplicant_driver_init(wpa_s) < 0)
{
//主要函数,该函数会初始化l2_packet,并注册回掉函数,与4次握手相关
if (wpa_supplicant_update_mac_addr(wpa_s) < 0)
{
//wpa_supplicant_rx_eapol()就是4次握手的处理函数了
wpa_s->l2 = l2_packet_init(wpa_s->ifname, wpa_drv_get_mac_addr(wpa_s), ETH_P_EAPOL, wpa_supplicant_rx_eapol, wpa_s, 0);
{
//新建socket
struct l2_packet_data *l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM, htons(protocol));
//bind
if (bind(l2->fd, (struct sockaddr *) &ll, sizeof(ll)) < 0)
//将socket与回掉函数l2_packet_receive相关联,内部实际会调用 wpa_supplicant_rx_eapol()
eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL);
{
//相关步骤请看eloop_data结构体,所有的回掉函数和回掉所用的数据都在保存在该结构体中
res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll, &fromlen);
l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res);
}
}
}
}
- wap_cli控制socket
在linux中可以通过wpa_supplicant提供的wpa_cli工具控制wpa_supplicant,在android中也可以通过wpa_cli在没有framework的情况下实现扫描连接工作
下述代码就是创建需要的socket和接收发送函数。
在android手机中打开wifi后再 /data/misc/wifi/sockets下的wlan0 和p2p0 就是该段代码生成的
wpa_s->ctrl_iface = wpa_supplicant_ctrl_iface_init(wpa_s);
{
//分配空间,然后执行真正的初始化工作
struct ctrl_iface_priv *priv;
if (wpas_ctrl_iface_open_sock(wpa_s, priv) < 0)
{
//android上运行不会执行以下两行代码
//os_snprintf(addr.sun_path, sizeof(addr.sun_path), "wpa_%s", wpa_s->conf->ctrl_interface);
//priv->sock = android_get_control_socket(addr.sun_path);
//新建sock
priv->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
//bind,地址为,/data/misc/wifi/sockets/wlan0,这个地址是新生成的
bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr))
//注册接收socket的函数wpa_supplicant_ctrl_iface_receive, 其核心调用函数是 wpa_supplicant_ctrl_iface_process
eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive, wpa_s, priv);
//向socket上传event的函数
wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
}
}
通过wpa_cli工具使用连接网络的步骤:
- 添加接口过程中的全部代码
struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
struct wpa_interface *iface)
{
struct wpa_supplicant *wpa_s;
struct wpa_interface t_iface;
struct wpa_ssid *ssid;
struct wpa_supplicant *wpa_s = wpa_supplicant_alloc();
wpa_s->global = global;
//网络接口的初始化,与底层通信的socket,密钥数据包的socket,wpa_s的初始状态等
if (wpa_supplicant_init_iface(wpa_s, &t_iface))
{
//读取两个conf配置文件
wpa_s->conf = wpa_config_read(wpa_s->confname, NULL);
wpa_config_read(wpa_s->confanother, wpa_s->conf);
//初始化驱动接口,并注册接收驱动event的函数 nl80211
if (wpas_init_driver(wpa_s, iface) < 0)
{
//根据传进来的-inl80211选定对应的wpa_driver_ops(driver_nl80211.c),里面定义了驱动的操作方法(),同时调用对应的global_init()进行初始化
if (wpa_supplicant_set_driver(wpa_s, driver) < 0)
{
//根据名称选择网络ops
select_driver(wpa_s, i)
{
//调用对应的global_init(),void * nl80211_global_init(void)
global->drv_priv[i] = wpa_drivers[i]->global_init();
{
//初始化netlink socket,设置两个消息处理函数,netlink_receive中使用
cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink;
cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink;
//创建一个接收kernel中 route 子系统的消息socket
global->netlink = netlink_init(cfg);
{
//*****该socket用于接收 kernel中
netlink->sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
bind(netlink->sock, (struct sockaddr *) &local, sizeof(local));
//注册内核消息接收函数, 根据接收到的消息type选择以下两个处理函数
eloop_register_read_sock(netlink->sock, netlink_receive, netlink, NULL);
static void netlink_receive(int sock, void *eloop_ctx, void *sock_ctx)
{
char buf[8192]; //接收消息的字节数,
left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr *) &from, &fromlen);
//buf中是一个netlink消息,符合netlink规范,根据消息type选在相应的处理函数 newlink 和 dellink
}
}
//创建 global结构体中的 nl_cb(接收消息处理)和nl(发送消息)
if (wpa_driver_nl80211_init_nl_global(global) < 0)
{
//创建连接到 GENERIC_NETLINK 的socket
global->nl = nl_create_handle(global->nl_cb, "nl");
//将nl连接到 内核中的nl80211模块
global->nl80211_id = genl_ctrl_resolve(global->nl, "nl80211");
//创建接收消息的event socket, 并分别加入到组播组scan mlme regulatory vendor 中
global->nl_event = nl_create_handle(global->nl_cb, "event");
ret = nl_get_multicast_id(global, "nl80211", "scan");
ret = nl_socket_add_membership(global->nl_event, ret);
//设置nl_cb的回调函数 process_global_event(), 该函数又会调用 wpa_supplicant_event() 做进一步处理
nl_cb_set(global->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, process_global_event, global);
//为nl_event注册接收函数, 通过nl_recvmsgs()调用 process_global_event()
nl80211_register_eloop_read(&global->nl_event, wpa_driver_nl80211_event_receive, global->nl_cb);
}
//创建一个 ioctl_sock, 还不知道干啥用,该sock用于通过ioctl方式向底层发送命令
global->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
}
}
}
//调用对应的初始化函数wpa_driver_nl80211_drv_init(),传入的ifname名称为 wlan0
wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname);
static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname, void *global_priv, int hostapd, const u8 *set_addr)
{
//挺重要的一个结构体
struct wpa_driver_nl80211_data *drv;
//添加了回调函数drv->nl_cb process_drv_event(),最终会调用 wpa_supplicant_event()
if (wpa_driver_nl80211_init_nl(drv))
//添加bss回调函数bss->nl_cb,看着没啥用,最终处理 wpa_supplicant_event(),这两个回调函数没有和socket绑定,目前不清楚调用地方
if (nl80211_init_bss(bss))
//RF射频省电相关,读取节点“/dev/rfkill”只是接受消息打印log,不做任何操作
rcfg->blocked_cb = wpa_driver_nl80211_rfkill_blocked;
rcfg->unblocked_cb = wpa_driver_nl80211_rfkill_unblocked;
drv->rfkill = rfkill_init(rcfg);
//检查当前接口是否为UP状态
linux_iface_up(drv->global->ioctl_sock, ifname)
//主要为操作网卡wlan0
wpa_driver_nl80211_finish_drv_init(drv, set_addr, 1)
{
//得知网卡地址和设置网口为UP
if (set_addr && (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0) || linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname, set_addr)))
//获取网口能力,具体能力请查阅struct wpa_driver_capa
if (wpa_driver_nl80211_capa(drv))
//设置网口信息 IF_OPER_DORMANT
netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, 1, IF_OPER_DORMANT);
//获取网口地址
if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, bss->addr))
}
。。创建一个socket,并注册接收函数wpa_driver_nl80211_handle_eapol_tx_status
drv->eapol_tx_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
eloop_register_read_sock(drv->eapol_tx_sock, wpa_driver_nl80211_handle_eapol_tx_status, drv, NULL);
}
}
//初始化wpa_s,主要设置 wpa 状态机
if (wpa_supplicant_init_wpa(wpa_s) < 0)
{
//设置一些操作函数,请查看结构体 struct wpa_sm_ctx
。。。
//
struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
{
//主要是3个变量值
sm->dot11RSNAConfigPMKLifetime = 43200; //PMK的生存时间(秒)(12小时),超时后要重新计算
sm->dot11RSNAConfigPMKReauthThreshold = 70; //PMK超时多少(70%)后,需要重新身份认证
sm->dot11RSNAConfigSATimeout = 60; //进行身份验证的最长时间(秒)
}
}
//初始化驱动,主要完成了l2_paket回掉函数的初始化,与4次握手相关
if (wpa_supplicant_driver_init(wpa_s) < 0)
{
//主要函数,该函数会初始化l2_packet,并注册回掉函数,与4次握手相关
if (wpa_supplicant_update_mac_addr(wpa_s) < 0)
{
//wpa_supplicant_rx_eapol()就是4次握手的处理函数了
wpa_s->l2 = l2_packet_init(wpa_s->ifname, wpa_drv_get_mac_addr(wpa_s), ETH_P_EAPOL, wpa_supplicant_rx_eapol, wpa_s, 0);
{
//新建socket
struct l2_packet_data *l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM, htons(protocol));
//bind
if (bind(l2->fd, (struct sockaddr *) &ll, sizeof(ll)) < 0)
//将socket与回掉函数l2_packet_receive相关联,内部实际会调用 wpa_supplicant_rx_eapol()
eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL);
{
//相关步骤请看eloop_data结构体,所有的回掉函数和回掉所用的数据都在保存在该结构体中
res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll, &fromlen);
l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res);
}
}
}
}
//设置国家码到驱动中
wpa_drv_set_country(wpa_s, wpa_s->conf->country))
//wpa_s中的 struct wps_context *wps 节点
if (wpas_wps_init(wpa_s))
{
//wps_context *wps中定义了一些方法,具体请查看注释,没什么逻辑处理的问题
}
//初始化 EAPOOL,参考eapol模块信息
wpa_supplicant_init_eapol(wpa_s)
{
//设置一些方法
。。。
ctx->eapol_send = wpa_supplicant_eapol_send;
。。。
//初始化eapol状态机
wpa_s->eapol = eapol_sm_init(ctx);
}
//初始化与FWKS的通信的socket???
wpa_s->ctrl_iface = wpa_supplicant_ctrl_iface_init(wpa_s);
{
//分配空间,然后执行真正的初始化工作
struct ctrl_iface_priv *priv;
if (wpas_ctrl_iface_open_sock(wpa_s, priv) < 0)
{
//参数通过启动参数传入wlan0的名称,然后获取init.rc中wpa_wlan0的socket
//android上运行不会执行以下两行代码
//os_snprintf(addr.sun_path, sizeof(addr.sun_path), "wpa_%s", wpa_s->conf->ctrl_interface);
//priv->sock = android_get_control_socket(addr.sun_path);
//新建sock
priv->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
//bind,地址为,/data/misc/wifi/sockets/wlan0,这个地址是新生成的
bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr))
//注册接收socket 命令的函数wpa_supplicant_ctrl_iface_receive, 其核心调用函数是 wpa_supplicant_ctrl_iface_process
eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive, wpa_s, priv);
//向socket上传event的函数
wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
}
}
//初始化bss相关,主要是wpa_s中的两个链表
if (wpa_bss_init(wpa_s) < 0)
{
//初始化wpa_s中bss和bssid链表
dl_list_init(&wpa_s->bss);
dl_list_init(&wpa_s->bss_id);
//注册超时函数 ,每隔10秒刷新下bss,去掉无用的bss
eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD=10, wpa_bss_timeout, wpa_s, NULL);
}
//sim卡 sd卡相关
if (pcsc_reader_init(wpa_s) < 0)
//不知道做什么用的
if (wpas_init_ext_pw(wpa_s) < 0)
}
/* Notify the control interfaces about new iface */
if (wpas_notify_iface_added(wpa_s)) {
wpa_supplicant_deinit_iface(wpa_s, 1, 0);
return NULL;
}
for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
wpas_notify_network_added(wpa_s, ssid);
//设置wpas状态
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
return wpa_s;
}
- 流程图
其中虚线表示数据的传递,深棕色部分表示创建的socket以及收发函数
下一篇为wpa_supplicant中的两个重要的结构体bss和conf
bss中保存着扫描结果, conf中为wpa_s的参数和保存的网络