wpa_supplicant 框架

1.系统架构

Android WiFi系统引入了wpa_supplicant,它的整个WiFi系统以wpa_supplicant为核心来定义上层用户接口和下层驱动接口。整个WiFi系统架构如下图所示:

1.1 WifiService

由SystemServer启动的时候生成的ConnecttivityService创建,负责启动关闭wpa_supplicant,启动和关闭WifiMonitor线程,把命令下发给wpa_supplicant以及更新WIFI的状态。 处理其它模块通过IWifiManager接口发送过来的远端WiFi操作。

1.2 WifiMonitor

负责从wpa_supplicant接收事件通知。

1.3 wpa_supplicant

  1. 读取配置文件
  2. 初始化配置参数,驱动函数
  3. 让驱动scan当前所有的bssid
  4. 检查扫描的参数是否和用户设置的相符
  5. 如果相符,通知驱动进行权限和认证操作
  6. 连上AP

1.4 Wifi驱动模块

厂商提供的source,主要进行load firware和kernel的wireless进行通信

1.5 Wifi电源管理模块

主要控制硬件的GPIO和上下电,让CPU和Wifi模组之间通过sdio接口或USB接口通信

1.6 Wifi工作步骤

  1. Wifi启动
  2. 开始扫描
  3. 显示扫描的AP
  4. 配置AP
  5. 连接AP
  6. 获取IP地址
  7. 上网

2、 wpa_supplicant 初始化流程

2.1. main()函数:

在这个函数中,主要做了四件事。 a. 解析命令行传进的参数。 b. 调用wpa_supplicant_init()函数,做wpa_supplicant的初始化工作。 c. 调用wpa_supplicant_add_iface()函数,增加网络接口。 d. 调用wpa_supplicant_run()函数,让wpa_supplicant真正的run起来。

2.2. wpa_supplicant_init()函数:

a. 打开debug 文件。 b. 注册EAP peer方法。 c. 申请wpa_global内存,该数据结构作为统领其他数据结构的一个核心, 主要包括四个部分:

wpa_supplicant *ifaces   /* 每个网络接口都有一个对应的wpa_supplicant数据结构,该指针指向最近加入的一个,在wpa_supplicant数据结构中有指针指向next */
wpa_params params   /* 启动命令行中带的通用的参数 */
ctrl_iface_global_priv *ctrl_iface  /* global的控制接口 */
ctrl_iface_dbus_priv *dbus_ctrl_iface  /* dbus 的控制接口 */

d. 设置wpa_global中的wpa_params中的参数。 e. 调用eloop_init函数将全局变量eloop中的user_data指针指向wpa_global。 f. 调用wpa_supplicant_global_ctrl_iface_init函数初始化global 控制接口。 g. 调用wpa_supplicant_dbus_ctrl_iface_init函数初始化dbus 控制接口。 h. 将该daemon的pid写入pid_file中。

2.3. wpa_supplicant_add_iface()函数:

该函数根据启动命令行中带有的参数增加网络接口, 有几个就增加几个。 a. 因为wpa_supplicant是与网络接口对应的重要的数据结构,所以,首先分配一个wpa_supplicant数据结构的内存。 b. 调用wpa_supplicant_init_iface() 函数来做网络接口的初始工作,主要包括: 设置驱动类型,现常用nl80211; 读取配置文件,并将其中的信息设置到wpa_supplicant数据结构中的conf 指针指向的数据结构,它是一个wpa_config类型; 命令行设置的控制接口ctrl_interface和驱动参数driver_param覆盖配置文件里设置,命令行中的优先; 拷贝网络接口名称和桥接口名称到wpa_config数据结构; 对于网络配置块有两个链表描述它,一个是 config->ssid,它按照配置文件中的顺序依次挂载在这个链表上,还有一个是pssid,它是一个二级指针,指向一个指针数组,该指针数组按照优先级从高到底的顺序依次保存wpa_ssid指针,相同优先级的在同一链表中挂载。 c. 调用wpa_supplicant_init_iface2() 函数,主要包括: 调用wpa_supplicant_init_eapol()函数来初始化eapol; 调用相应类型的driver的init()函数; 设置driver的param参数; 调用wpa_drv_get_ifname()函数获得网络接口的名称,对于wext类型的driver,没有这个接口函数; 调用wpa_supplicant_init_wpa()函数来初始化wpa,并做相应的初始化工作; 调用wpa_supplicant_driver_init()函数,来初始化driver接口参数;在该函数的最后,会设置

wpa_s->prev_scan_ssid = BROADCAST_SSID_SCAN;
wpa_supplicant_req_scan(wpa_s, interface_count, 100000);

来主动发起scan; 调用wpa_supplicant_ctrl_iface_init()函数,来初始化控制接口;对于UNIX SOCKET这种方式,其本地socket文件是由配置文件里的ctrl_interface参数指定的路径加上网络接口名称;

2.4. wpa_supplicant_run()函数:

初始化完成之后,让wpa_supplicant的main event loop run起来。 在wpa_supplicant中,有许多与外界通信的socket,它们都是需要注册到eloop event模块中的,具体地说,就是在eloop_sock_table中增加一项记录,其中包括了sock_fd, handle, eloop_data, user_data。 eloop event模块就是将这些socket组织起来,统一管理,然后在eloop_run中利用select机制来管理socket的通信。

3. Wpa_supplicant提供的接口

从通信层次上划分,wpa_supplicant提供向上的控制接口 control interface,用于与其他模块(如UI)进行通信,其他模块可以通过control interface 来获取信息或下发命令。Wpa_supplicant通过socket通信机制实现下行接口,与内核进行通信,获取信息或下发命令。

3.1 上行接口

Wpa_supplicant提供两种方式的上行接口。一种基于传统dbus机制实现与其他进程间的IPC通信;另一种通过Unix domain socket机制实现进程间的IPC通信。

3.1.1 Dbus接口

该接口主要在文件“ctrl_iface_dbus.h”,“ctrl_iface_dbus.c”,“ctrl_iface_dbus_handler.h”和“ctrl_iface_dbus_handler.c”中实现,提供一些基本的控制方法。

3.1.2 Unix domain socket 接口

该接口主要在文件“wpa_ctrl.h”,“wpa_ctrl.c”,“ctrl_iface_unix.c”,“ctrl_iface.h”和“ctrl_iface.c”实现。

(1)“wpa_ctrl.h”,“wpa_ctrl.c”完成对control interface的封装,对外提供统一的接口。其主要的工作是通过Unix domain socket建立一个control interface 的client结点,与作为server的wpa_supplicant结点通信。

主要功能函数:

struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path);
/* 建立并初始化一个Unix domain socket的client结点,并与作为server的wpa_supplicant结点绑定 */
void wpa_ctrl_close(struct wpa_ctrl *ctrl);
/* 撤销并销毁已建立的Unix domain socket的client结点 */
int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
                   char *reply, size_t *reply_len,
                   void (*msg_cb)(char *msg, size_t len));
 
/* 用户模块直接调用该函数对wpa_supplicant发送命令并获取所需信息 */

Note: Wpa_supplicant 提供两种由外部模块获取信息的方式:一种是外部模块通过发送request命令然后获取response的问答模式,另一种是wpa_supplicant主动向外部发送event事件,由外部模块监听接收。 一般的常用做法是外部模块通过调用wpa_ctrl_open()两次,建立两个control interface接口,一个为ctrl interface,用于发送命令,获取信息,另一个为monitor interface,用于监听接收来自于wpa_supplicant的event时间。此举可以降低通信的耦合性,避免response和event的相互干扰。

int wpa_ctrl_attach(struct wpa_ctrl *ctrl);
/* 注册 某个 control interface 作为 monitor interface */
int wpa_ctrl_detach(struct wpa_ctrl *ctrl);
/* 撤销某个 monitor interface 为 普通的 control interface  */
int wpa_ctrl_pending(struct wpa_ctrl *ctrl);
/* 判断是否有挂起的event 事件 */
int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len);
/* 获取挂起的event 事件 */

(2)“ctrl_iface_unix.c”实现wpa_supplicant的Unix domain socket通信机制中server结点,完成对client结点的响应。 其中最主要的两个函数为:

static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx,
                                         void *sock_ctx)
/* 接收并解析client发送request命令,然后根据不同的命令调用底层不同的处理函数;
 * 然后将获得response结果回馈到 client 结点。
 */
static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
                                      int level, const char *buf,
                                      size_t len)
/* 向注册的monitor interfaces 主动发送event事件 */

(3)“ctrl_iface.h”和“ctrl_iface.c”主要实现了各种request命令的底层处理函数。

3.2 下行接口

Wpa_supplicant提供的下行接口主要用于和kernel(driver)进行通信,下发命令和获取信息。 Wpa_supplicant下行接口主要包括三种重要的接口: 1. PF_INET socket接口,主要用于向kernel 发送ioctl命令,控制并获取相应信息。 2. PF_NETLINK socket接口,主要用于接收kernel发送上来的event 事件。 3. PF_PACKET socket接口,主要用于向driver传递802.1X报文。

主要涉及到的文件包括:“driver.h”,“drivers.c”,“driver_nl80211.h”,“driver_nl80211.c”,“l2_packet.h”和“l2_packet_linux.c”。 其中“driver.h”,“drivers.c”,“driver_wext.h”和“driver_wext.c”实现PF_INETsocket接口和PF_NETLINK socket接口;“l2_packet.h”和“l2_packet_linux.c”实现PF_PACKET socket接口。

(1)“driver.h”,“drivers.c”主要用于封装底层差异对外显示一个相同的wpa_driver_ops接口。Wpa_supplicant可支持atmel, Broadcom, ipw, madwifi, ndis, nl80211, wext等多种驱动。 其中一个最主要的数据结构为wpa_driver_ops, 其定义了driver相关的各种操作接口。

(2)“driver_nl80211.h”,“driver_nl80211.c”实现了netlink形式的wpa_driver_ops,通过netlink完成与kernel的信息交互。

(3)“l2_packet.h”和“l2_packet_linux.c”主要用于实现PF_PACKET socket接口,通过该接口,wpa_supplicant可以直接将802.1X packet发送到L2层,而不经过TCP/IP协议栈。

其中主要的功能函数为:

struct l2_packet_data * l2_packet_init(
       const char *ifname, const u8 *own_addr, unsigned short protocol,
       void (*rx_callback)(void *ctx, const u8 *src_addr,
                         const u8 *buf, size_t len),
       void *rx_callback_ctx, int l2_hdr);
/* 创建并初始化PF_PACKET socket接口,其中rx_callback 为从L2接收到的packet 处理callback函数 */
void l2_packet_deinit(struct l2_packet_data *l2);
/* 销毁 PF_PACKET socket接口 */
int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
                 const u8 *buf, size_t len);
/* L2层packet发送函数,wpa_supplicant用此发送L2层 802.1X packet  */
static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx);
/*  L2层packet接收函数,接收来自L2层数据后,将其发送到上层  */

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • WiFi关联拒绝log分析以及代码流程 ASSOC_REJECT

    最近遇到一个问题,连接WiFi时,密码正确,显示saved,但是一直连接不上,分析log发现问题出在关联的时候,关联被拒绝了。 下面根据log看一下流程:

    用户7557625
  • wpa_supplicant.conf 配置文件解析(一)

    WPA,是Wi-Fi Protected Access,Wi-Fi安全访问的简称。wpa_supplicant是开源项目源码,被谷歌修改后加入android移动...

    用户7557625
  • 从wlan_mac.bin文件中读取MAC地址

    /vendor/qcom/opensource/wlan/qcacld-3.0/Android.mk

    用户7557625
  • WiFi关联拒绝log分析以及代码流程 ASSOC_REJECT

    最近遇到一个问题,连接WiFi时,密码正确,显示saved,但是一直连接不上,分析log发现问题出在关联的时候,关联被拒绝了。 下面根据log看一下流程:

    用户7557625
  • 【RL-TCPnet网络教程】第28章 RL-TCPnet之DNS应用

    本章节为大家讲解RL-TCPnet的DNS应用,学习本章节前,务必要优先学习第27章的DNS基础知识。有了这些基础知识之后,再搞本章节会有事半功倍的效果。

    armfly
  • Android编程基于自定义控件实现时钟功能的方法

    本文实例讲述了Android编程基于自定义控件实现时钟功能的方法。分享给大家供大家参考,具体如下:

    砸漏
  • 主题配置

    ?> JavaScript 对象是被命名值的容器。值以名称:值对的方式来书写(名称和值由冒号分隔)。

    Dean0731
  • iOS流布局UICollectionView系列一——初识与简单使用UICollectionView

            UICollectionView是iOS6之后引入的一个新的UI控件,它和UITableView有着诸多的相似之处,其中许多代理方法都十分类似。...

    珲少
  • 蔚来、小鹏过冬术:昔日死敌,今日亲兄弟

    《蔚来李斌,2019年最惨的人》刷爆朋友圈之际,小鹏汽车创始人何小鹏在微博上对李斌发出如下鼓励和感概。

    刘旷
  • shell学习笔记

    单引号中不能出现单引号,用转义字符转义也不行,双引号可以

    yaohong

扫码关注云+社区

领取腾讯云代金券