- Android Looper 和 Handler 分析
- Android MediaScanner 详尽分析
- Android 深入浅出之 Binder 机制
- 第一部分 AudioTrack 分析
- 第二部分 AudioFlinger 分析
- Android 深入浅出之 Audio 第三部分 Audio Policy
- Android 深入浅出之 Zygote
- Android 深入浅出之 Surface
- Linux Kernel 系列一 开篇和 Kernel 启动概要
- Linux Kernel 系列二 用户空间的初始化
- Linux Kernel 系列三 Kernel 编译和链接中的 linker script 语法详解
- 第五章 深入理解常见类
- linux kernel 系列四 嵌入式系统中的文件系统以及 MTD
- 随笔之 Android 平台上的进程调度探讨
- Android 4.0 External 下功能库说明
- 随笔之 Android 不吐不快
- Android Rom 移植知识普及
- 深入理解 Android 系列书籍的规划路线图
- Android 4.1 初识 - 7月12号
- Android 4.1 初识 - 7月13号
- Android 4.1 Surface 系统变化说明
- Android BSP 成长计划随笔之虚拟设备搭建和 input 系统
- 深入理解 Android 写作背后的故事
- 随笔之 GoldFish Kernel 启动过程中 arm 汇编分析
- Android Project Butter 分析
- Android Says Bonjour
- MTP in Android
- DRM in Android
- Tieto 公司 Android 多窗口解决方案展示
- 深入理解 SELinux SEAndroid 之二
- 深入理解 SELinux SEAndroid(最后部分)
- 前言
- 附录
- 第一章 准备工作
- 第二章 深入理解 Netd
- 第三章 Wi-Fi 基础知识
- 第四章 深入理解 wpa_supplicant
- 第五章 深入理解 WifiService
- 第六章 深入理解 wi-Fi Simple Configuration
- 第七章 深入理解 Wi-Fi P2P
- 第八章 深入理解 NFC
- 第九章 深入理解 GPS
- Google I/O 2014 之 Android 面面观
- 深入理解 Android 之 Java Security 第一部分
- 深入理解 Android 之 Java Security 第二部分(Final)
- 深入理解 Android 之设备加密 Device Encryption
- 第一章 阅读前的准备工作
- 第二章 深入理解 JNI
- 第三章 深入理解 init
- 第四章 深入理解 Zygote
- 第五章 深入理解常见类
- 第六章 深入理解 Binder
- 第七章 深入理解 Audio 系统
- 第八章 深入理解 Surface 系统
- 第九章 深入理解 Vold 和 Rild
- 第十章 深入理解 MediaScanner
- 第一章 开发环境部署
- 第二章 深入理解 Java Binder 和 MessageQueue
- 第三章 深入理解 SystemServer
- 第四章 深入理解 PackageManagerService
- 第五章 深入理解 PowerManagerService
- 第六章 深入理解 ActivityManagerService
- 第七章 深入理解 ContentProvider
- 第八章 深入理解 ContentService 和 AccountManagerService
- 第一章 开发环境部署
- 第二章 深入理解 Java Binder 和 MessageQueue
- 第三章 深入理解 AudioService
- 第四章 深入理解 WindowManagerService
- 第五章 深入理解 Android 输入系统
- 第六章 深入理解控件(ViewRoot)系统
- 第七章 深入理解 SystemUI
- 第八章 深入理解 Android 壁纸
- 边缘设备、系统及计算杂谈(16)——Apache 学习
- 边缘设备、系统及计算杂谈(17)——Ansible 学习
- ZFS 和 LVM
- Android 4.2 蓝牙介绍
- 了解一下 Android 10 中的 APEX
- 关于 Android 学习的三个终极问题
- 深入理解 Android 之 AOP
- Android 系统性能调优工具介绍
- 深入理解 SELinux SEAndroid(第一部分)
- Android Wi-Fi Display(Miracast)介绍
- 深入理解 Android 之 Gradle
第四章 深入理解 wpa_supplicant
本章主要内容
- 介绍wpa_supplicant的初始化
- 介绍EAP和EAPOL
- 介绍wpa_supplicant如何加入无线网络
4.1 概述
wpa_supplicant是一个开源软件项目,它实现了Station[1]对无线网络进行管理和控制的功能。根据官方[2]描述,wpa_supplicant所支持的功能非常多,此处列举其中几个重要的功能点。
支持WPA和IEEE 802.11i所定义的大部分功能:这部分功能集中在安全方面,包括:
- 支持WPA-PSK(即WPA-Personal)和WPA-Enterprise(即利用RAIDUS认证服务器来完成身份认证的情况)。
- 数据加密方面支持CCMP、TKIP、WEP104和WEP40。注意,WEP104和WEP40中的数字代表密钥的长度。104表示密钥长度为104个二进制位(如以ASCII字符个数来计算的话,WEP104支持的密钥长度为13个ASCII字符)。
- 完全支持WPA和WPA2,包括PMKSA缓存,预认证(pre-authentication)等功能。
- 支持IEEE 802.11r和802.11w,其中802.11r规范定义了快速基础服务转移(Fast Transition)功能,而802.11w则新增了对管理帧的安全保护机制。
- 支持WFA制定的Wi-Fi Proctected Setup功能、P2P、TDLS等。
支持多种EAP Method:主要和802.1X中Supplicant的功能有关,wpa_supplicant支持多达25种EAP Method,包括:
- EAP-TLS:TLS(Transport Layer Security)本身是一种传输层安全协议,它利用密钥算法提供端点身份认证与通讯保密,其基础是公钥基础设施(public key infrastructure,简称PKI)。EAP-TLS定义于RFC 5216。
- EAP-PEAP:PEAP(Protected Extensible Authentication Protocol,可扩展EAP)由微软、思科以及RSA Security三个公司共同开发,是一种利用证书加用户名和密码来进行身份验证的方法。
- EAP-TTLS:它是TLS的拓展,全名为Tunneled Transport Layer Security(隧道传输层安全协议),相比TLS,它简化了认证过程中客户端的工作。
- EAP-SIM、EAP-PSK、EAP-GPSK等其他认证方法。
读者可阅读参考资料[1]以了解更多EAP方法的知识。
对各种无线网卡和驱动的支持:
- 支持nl80211/cfg80211驱动和Linux Wireless Extension驱动[3]。
- 支持Windows平台中的NDIS驱动。
提示:wpa_supplicant虽然支持Windows平台,但笔者相信绝大多数读者使用的都是Windows自带的无线网络管理程序(或者Intel芯片相关软件提供的无线网络管理程序)。从功能角度来说,读者可认为wpa_supplicant是这些私有程序的一种开源实现。
Android做为开源世界的集大成者,它在无线网络管理和控制方面直接使用了wpa_supplicant。Android 4.1中,external目录下有两个和wpa_supplicant相关的目录,分别是wpa_supplicant_6和wpa_supplicant_8。6和8分别代表对应wpa_supplicant的版本号为0.6.10和2.0-devel。
提示:关于wpa_supplicant的发布历史,请读者参考http://hostap.epitest.fi/releases.html。
本书的分析目标是wpa_supplicant_8,它包含三个主要子目录,分别是:
- hostapd:当手机进入SoftAP模式时,手机将扮演AP的角色,故需要hostapd来提供AP的功能。
- wpa_supplicant:Station模式,也叫Managed模式。它是本书分析的重点。
- src:hostapd和wpa_supplicant中都包含了一些通用的数据结构和处理方法,这些内容都放在此src目录中。注意,hostapd/src和wpa_supplicant/src子目录均链接到此src目录。
wpa_supplicant是Android用户空间中无线网络部分的核心模块,所有Framework层中和Wi-Fi相关的操作最终都将借由wpa_supplicant来完成。另外,wpa_supplicant本身对802.11、802.1X和Wi-Fi Alliance定义的一些规范都有极好的支持。所以,分析它将是加深理解802.11及相关理论知识的一个非常重要的途径。
本章拟打算带领读者从两条分析路线来掌握wpa_supplicant和相关的功能模块。
- 路线一:首先将介绍wpa_supplicant的初始化过程。这条路线将帮助读者了解wpa_supplicant中常见的数据结构及之间的关系。这条路非常难走,请读者做好心理准备。
- 路线二:我们将通过命令行发送命令的方式触发wpa_supplicant进行相关工作,使得手机加入一个利用WPA-PSK进行认证的无线网络。这条路线将帮助读者了解wpa_supplicant中的命令处理、scan、association、4-way handshake等相关处理流程。
提示:后续章节还将围绕Android中无线网络技术开展更多的讨论:
1)第5章将介绍Android Framework中的WifiService及其相关模块。
2)第6、7章节将继续wpa_supplicant之旅,其内容和WPS、Wi-Fi P2P以及WifiP2pService有关。
为了行文方便,本书将用WPAS来表示wpa_supplicant。另外,后文代码分析中还能见到一种重要的数据结构,它也叫wpa_supplicant。请读者根据上下文信息来理解wpa_supplicant的含义。
正式开始分析之旅前,我们先来简单了解下wpa_supplicant。
4.2 初识wpa_supplicant
本节介绍WPAS一些外围知识,包括软件结构、编译配置、控制命令和对应控制API的用法。其中,控制命令的格式和API的用法将在后续介绍WifiService相关模块时会见到。另外,在研究WPAS时,能熟练掌握用git查询历史版本信息也非常关键。WPAS的故事首先从其软件架构开始。
4.2.1 wpa_supplicant架构介绍
wpa_supplicant是一个比较庞大的开源软件项目,包含500多个文件,20万行代码,其内部模块构成如图4-1所示[2]。
图4-1 wpa_supplicant软件架构
图4-1所示的WPAS软件架构包括如下重要模块:
- WPAS所有工作都围绕事件(对应图4-1中的event loop模块)展开。即,它是基于事件驱动的。事件驱动和消息驱动类似,主线程等待事件的发生并处理它们。WPAS没有使用多线程编程,所有事件处理都在主线程中完成。从这一点看,WPAS的运行机制倒是很简单。
- 位于event loop模块下方的driver i/f(i/f代表interface)接口模块用于隔离和底层驱动直接交互的那些driver控制模块(如wext、ndiswrapper等,WPAS中称之为driver wrapper)。这些driver wrapper和平台以及芯片所使用的驱动相关。不过,由于driver i/f的隔离作用,WPAS中其他模块将能最大程度保持平台以及驱动无关性。
- driver wrapper经常要返回一些信息给上层。WPAS中,这些信息将通过driver events的方式反馈给WPAS其他模块进行处理。
- 上一章曾介绍过EAP以及EAPOL协议。除了定义消息格式外,RFC4137文档定义了EAP状态机,而802.1X文档中还定义了EAPOL状态机。WPAS根据这两个协议分别实现了EAP和EAPOL状态机。本章后续将详细分析这两个状态机以及背后的协议。除此之外,WPAS还定义了自己的状态机(即WPA/WPA2 State Machine)。
- WPAS实现了多种EAP方法,如图4-1中EAP method模块所示。另外它还包含了TLS模块和crypto模块用于支持对应的EAP方法。
- EAPOL以及EAP消息都属于LLC层数据,所以WPAS的l2_packet模块用于收发EAPOL和EAP消息。
- WPAS支持较多的配置参数,这些参数的处理由configuration模块完成。
- WPAS是C/S结构中的Server端,它通过ctrl i/f模块向客户端提供通信接口。Linux/Unix平台中,Client端利用Unix域socket与其通信。目前常用的Client端wpa_cli(无界面的命令行程序)和wpa_gui(UI用Qt实现)。
WPAS支持众多功能,使用前往往需根据平台或驱动的特性进行编译配置,下面通过一个实例来介绍如何在Android中编译wpa_supplicant。
4.2.2 wpa_supplicant编译配置介绍
本实例的背景情况如下:
笔者有一台三星Galaxy Note2手机,其OS为Android 4.1.2。现在,笔者打算编译一个AOSP(Android Open Source Project)的wpa_supplicant程序以替换Note2中原有的wpa_supplicant。
提示:AOSP即Google公版Android源码。几乎所有手机厂商都会根据芯片、硬件以及厂商自定义的特性去修改它。由于Note 2源码不公开,所以笔者只能编译AOSP版的wpa_supplicant。
假设读者已经按第1章要求部署完毕Android 4.1源码和开发环境,那么接下来要做的是:
cd 4.1source #首先进入4.1源码根目录
source build/envsetup #建立Android源码编译环境
lunch #选择要编译的设备和版本,笔者选择了1,代表full-eng。eng代表工程版,该选项对应的目标设备类型
#(TARGET_PRODUCT)为generic,其编译出来的镜像文件可由模拟器加载并运行
由上述配置可知,笔者将使用generic的版本来编译一个wpa_supplicant以运行在真实的机器上。
提醒:通过执行lunch命令可知,不同的设备应有对应的编译配置项。由于笔者没有Note 2的源码,所以只能尝试编译generic版本。
接下来要为generic平台定制所使用的wpa_supplicant版本,这是通过修改BoardConfig.mk来完成的。
[-->BoardConfig.mk]
#在此文件最后添加如下内容:
WPA_SUPPLICANT_VERSION := VER_0_8_X #表明使用wpa_supplicant_8
BOARD_WPA_SUPPLICANT_DRIVER := NL80211 #表明驱动使用Nl80211
BOARD_WLAN_DEVICE := bcmdhd #表明kernel中的wifi设备为博通公司的bcmdhd
#编译博通公司驱动相关的静态库,该库对应的代码也在AOSP源码中,位置是,
#hardware/broadcom/wlan/bcmdhd/wpa_supplicant_8_lib/
BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_bcmdhd
巧合的是,Note 2使用的wlan芯片刚好为bcmdhd。
除了修改BoardConfig.mk外,WPAS也定义了自己的编译配置文件android.config,其内容如下:
......#该文件主要定义了编译时生成的宏,各平台根据自己的硬件情况去设置需要编译的内容
# Driver interface for generic Linux wireless extensions
CONFIG_DRIVER_WEXT=y #可注释这一条以取消编译WEXT相关代码
# Driver interface for Linux drivers using the nl80211 kernel interface
CONFIG_DRIVER_NL80211=y #可去掉此行的注释符号以增加对Nl80211的支持
CONFIG_LIBNL20=y
......#其他很多编译配置项都可在此文件中修改
#注意,此文件中对CONFIG_DRIVER_NL80211的修改和BoardConfig.mk中的BOARD_WPA_SUPPLICANT_DRIVER
#相重合。BoardConfig.mk的优先级较高,所以请读者先修改它。
配置完毕后,开始编译:
#首先要编译wpa_supplicant依赖的静态库lib_driver_cmd_bcmdhd
mmm hardware/broadcom/wlan/bcmdhd/wpa_supplicant_8_lib/
mmm external/wpa_supplicant_8 #生成wpa_supplicant,同时也会生成wpa_cli
然后将编译后wpa_supplicant替换Note 2的/system/bin/wpa_supplicant并设置其为可运行(通过chmod命令设置其权限位0755)。同时,笔者也把wpa_cli push到/system/bin下为后续测试做准备。
经过测试,笔者发现AOSP的wpa_supplicant以及wpa_cli均能正常工作在Note 2上。这也间接表明Note 2并未对wpa_supplicant以及博通芯片相关的代码做较大改动。
注意:严格来说,android.cfg应该是唯一的编译控制文件。但由于底层wlan芯片不同,WPAS可能还依赖其他模块。所以,在具体实施时,BoardConfig.mk(或其他文件,视具体情况而定)也需要做修改。
4.2.3 命令和控制API介绍
1. 命令介绍
由图4-1的介绍可知,WPAS对外通过控制接口模块与客户端通信。在Android平台中,WPAS的客户端是位于Framework中的WifiService。用户在Settings界面进行Wi-Fi相关的操作最终都会经由WifiService通过发送命令的方式转交给wpa_supplicant去执行。WPAS定义了许多命令,常见的:
- PING:心跳检测命令。客户端用它判断WPAS是否工作正常。WPAS收到”PING”命令后需要回复“PONG”。
- MIB:客户端用该命令获取设备的MIB信息。
- STATUS:客户端用该命令来获取WPAS的工作状态。
- ADD_NETWORK:为WPAS添加一个新的无线网络。它将返回此新无线网络的id(从0开始)。注意:此network id非常重要,客户端后续将通过它来指明自己想操作的无线网络。
- SET_NETWORK <network id> <variable> <value>:network id是无线网络的id。此命令用于设置指定无线网络的信息。其中variable为参数名,value为参数的值。
- ENABLE_NETWORK <network id>:使能某个无线网络。此命令最终将促使WPAS发起一系列操作以加入该无线网络。
除了接收来自Client的命令外,WPAS也会主动给Client发送命令。例如,WPAS需用户为某个无线网络输入密码。这类命令称之为Interactive Request,其格式如下。
- WPAS向客户端发送的命令遵循CTRL-REQ-<field name>-<network id>-<human readable text>格式。如CTRL-REQ-PASSWORD-0-Passwork needed for SSID test-network。这条命令表示需要用户为0号网络输入密码。
- 客户端处理完后,需回复CTRL-RSP-<field name>-<network id>-<value>。目前支持的field包括PASSWORD、IDENTITY(EAP中的identity或者用户名)、PIN等。
- 最后,WPAS还可通过形如“CTRL-EVENT-<field>-<text>”的命令向客户端通知一些事情。
提示:除了“CTRL-EVENT-XXX”之外,WPAS还支持形如“WPA:XXX”和“WPS-XXX”的通知事件。这些事件和WPA和WPS有关。下一章分析WifiService时我们还能见到它们。
图4-2所示为笔者利用wpa_cli测试status命令得到的结果。
图4-2 wpa_cli命令测试示意
图4-2所示为status命令的结果。图中最后几行显示WPAS向wpa_cli返回了两个CTRL-EVENT信息。
2. 控制API介绍
Android平台中WifiService是WPAS的客户端,它和WPAS交互时必须使用wpa_supplicant提供的API。这些API声明于wpa_ctrl.h中,其用法如下:
//必须包含此头文件,链接时需包含libwpa_client.so动态库
#include “wpa_ctrl.h”
客户端使用wpa_ctrl时首先要分配控制对象。下面两个API用于创建和销毁控制对象wpa_ctrl:
//创建一个wpa控制端对象wpa_ctrl。Android平台中,参数ctrl_path代表unix域socket的位置
struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path);
void wpa_ctrl_close(struct wpa_ctrl *ctrl);//注销wpa_ctrl控制对象
下面这个函数用于发送命令给WPAS。
//客户端发送命令给wpa_supplicant,回复的消息保存在reply中
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));
msg_cb是一个回调函数,该参数的设置和WPAS中C/S通信机制的设计有关:
从Client角度来看,它发送给WPAS的命令所对应的回复属于solicited event(意为有请求的事件),而前面所提到的CTRL-EVENT事件(用于通知事件)对应为unsolicited event(意为未请求的事件)。当Client在等待某个命令的回复时,WPAS同时可能有些通知事件要发送给客户端,这些通知事件不是该命令的回复,所以不能通过wpa_ctrl_request的reply参数返回。
为了防止丢失这些通知事件,wpa_cli设计了一个msg_cb回调用于客户端在等待命令回复的时候处理那些unsolicited event。
这种一个函数完成两样完全不同的功能的设计实在有些特别,所以wpa_supplicant规定只有打开通知事件监听功能的wpa_ctrl对象才能在wpa_ctrl_request中通过msg_cb获取通知事件。而打开通知事件监听功能相关的API如下所示。
//打开通知事件监听功能
int wpa_ctrl_attach(struct wpa_ctrl *ctrl);
//打开通知事件监听功能的wpa_ctrl对象能直接调用下面的函数来接收unsolicited event
int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len);
如果客户端并不发送命令,而只是想接收Unsolicited event的话,可通过wpa_ctrl_recv函数来达到此目的。
综上所述,单独使用wpa_ctrl_recv和wpa_ctrl_request都不方便。所以,一种常见的用法是:客户端创建两个wpa_ctrl对象来简化自己的逻辑处理:
- 一个打开了通知事件监听功能的wpa_ctrl对象将只通过wpa_ctrl_recv来接收通知事件。
- 另外一个wpa_ctrl专职用于发送命令和接收回复。由于没有调用wpa_ctrl_attach,故它不会收到通知事件。
提示:下一章分析WifiService时将见到这种创建两个wpa_ctrl对象的做法。
4.2.4 git的使用
WPAS难度较大的一个重要原因是其注释较少,很多变量的含义没有任何解释。笔者也为此大伤脑筋。不得以,只能通过查看WPAS代码的历史版本来寻根溯源。经过实践,笔者总结了利用git来查询WPAS历史版本信息的一些步骤,分别如下。
用git clone命令下载WPAS官方代码。
git clone git://w1.fi/srv/git/hostap.git
以下命令的含义是查询use_monitor在driver_nl80211.c中的变化情况。
git blame src/drivers/driver_nl80211.c | grep use_monitor
因为use_monitor定义于该文件中,所以用git blame去查看它。得到的结果如图4-3所示。
图4-3 git blame结果
图4-3中的第一行显示了use_monitor最早出现的那个patch的情况,其对应的commit id是a11241fa。接着,再通过命令git log a11241fa可查看当时的commit信息,结果如图4-4所示。
图4-4 git log结果
图4-4展示了a11241fa对应的commit消息。由于提交者一般会在该消息中添加注释性内容。所以可通过研究这些内容来了解代码中某些变量的含义。
下面正式开始WPAS的代码分析之旅。首先是WPAS的初始化流程分析。
[1]注意,wpa_supplicant项目中还包含一个名为hostapd程序的代码,它实现了AP的功能。本书不拟讨论hostpad的代码。
[2] wpa_supplicant项目的官方地址为http://hostap.epitest.fi/
[3]根据审稿专家的反馈,wpa_supplicant仅支持Linux Wireless Extension V19以后的版本。
==========略略略略略略略略略略略略略略略===============
4.5 wpa_supplicant连接无线网络分析
本节将介绍本章第二条分析路线,即通过命令行发送命令的方式触发wpa_supplicant进行相关工作,使手机加入一个利用WPA-PSK进行认证的无线网络。
以笔者的Note 2为例,整个过程用到的命令如下所示。
[命令示例]
adb root #获取手机root用户权限。只有root被破解的手机才能成功
adb shell #登录手机shell
#笔者事先已编译wpa_cli并将其放到/system/bin目录中。这个命令用于启动wpa_cli,-i参数指明unix域控制
#socket文件名,它应该和wpa_supplicant启动时设置的控制接口文件名一致
wpa_cli -iwlan0 #该命令执行后,将进入wpa_cli进程,后续操作都在此进程中开展
#发送ADD_NETWORK命令给wpa_supplicant,它将返回一个新网络配置项的编号。请参考4.3.3.1"wpas_ssid结构
#体介绍"一节
ADD_NETWORK #假设wpa_supplicant返回的新网络配置项编号为0
SET_NETWORK 0 ssid "Test" #设置0号网络的ssid为“Test”
SET_NETWORK 0 key_mgmt WPA-PSK #设置0号网络的key_mgmt为“WPA-PSK”
SET_NETWORK 0 psk "12345Test" #设置0号网络的psk为“12345Test”
ENABLE_NETWORK 0 #使能0号网络,它将触发wpa_supplicant扫描、关联等一系列操作直到加入无线网络“Test”
CTRL+C #退出wpa_cli
dhcpcd wlan0 #启动dhcpd,wlan0为无线接口设备名。dhcpcd可为手机从AP那获取一个IP地址
dhcpcd成功执行后,手机将从AP那分配到一个IP地址。至此,手机就可以使用“Test”无线网络了。
注意:上述命令执行前有几个注意事项:
1)先要在Settings中开启无线网络。这个操作完成了wlan驱动及相应固件加载的工作。该工作实际上由netd来完成,而wpa_cli无法完成它。
2)开启无线网络后,WifiService和wpa_supplicant都开始工作了。为了避免WifiService的干扰,可以把Settings中的那些已知的无线网络信息都清除。
3)由于wpa_supplicant支持多个客户端,所以wpa_cli可以和WifiService共同工作。只要不操作Settings中无线网络相关的选项,WifiService就不会干扰wpa_cli。
4)然后按上述步骤执行wpa_cli。
根据前文所述,所有来自客户端的命令都由wpa_supplicant_ctrl_iface_receive函数处理(参考4.3.4中“wpa_supplicant_ctrl_iface_init介绍”一节)。该函数代码非常简单,就是根据客户端发送的命令进行对应处理。
[-->ctrl_iface_unix.c::wpa_supplicant_ctrl_iface_receive]
static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx,
void *sock_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
struct ctrl_iface_priv *priv = sock_ctx;
char buf[4096]; int res; struct sockaddr_un from;
socklen_t fromlen = sizeof(from);
char *reply = NULL; size_t reply_len = 0; int new_attached = 0;
res = recvfrom(sock, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &from, &fromlen);
.....
buf[res] = '\0';
//客户端第一次和WPAS连接时,需要发送"ATTACH"命令
if (os_strcmp(buf, "ATTACH") == 0) {
......//略过相关处理
} .....//"DETACH"和"LEVEL"命令处理
else {
#if defined(CONFIG_P2P) && defined(ANDROID_P2P)
......//P2P处理。虽然WPAS编译时打开了CONFIG_P2P和ANDROID_P2P,但本章不讨论P2P相关的内容
#endif
//大部分的命令处理都在wpa_supplicant_ctrl_iface_process函数中
reply = wpa_supplicant_ctrl_iface_process(wpa_s, buf,&reply_len);
}
if (reply) {//回复客户端
sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,fromlen);
os_free(reply);
} ......
/*
Client成功ATTACH后,将通知EAPOL模块。因为有些认证流程需要用户的参与(例如输入密码之类的),
所以当客户端连接上后,EAPOL模块将判断是否需要和客户端交互。读者可阅读
eapol_sm_notify_ctrl_attached函数。
*/
if (new_attached)
eapol_sm_notify_ctrl_attached(wpa_s->eapol);
}
如上述代码所示,绝大部分命令都由wpa_supplicant_ctrl_iface_process函数处理。下面将按顺序来分析其处理ADD_NETWORK、SET_NETWORK以及ENABLE_NETWORK的代码。
4.5.1 ADD_NETWORK命令处理
[-->ctrl_iface.c::wpa_supplicant_ctrl_iface_process]
char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,char *buf,
size_t *resp_len)
{
char *reply;
const int reply_size = 4096;
int ctrl_rsp = 0;
int reply_len;
......
reply = os_malloc(reply_size);
.....
//开始命令处理
......
else if (os_strcmp(buf, "ADD_NETWORK") == 0) {
reply_len = wpa_supplicant_ctrl_iface_add_network( wpa_s, reply, reply_size);
}else if
......//其他命令处理
if (reply_len < 0) {//命令处理出错
os_memcpy(reply, "FAIL\n", 5);
reply_len = 5;
}
......
*resp_len = reply_len;
return reply;
}
ADD_NETWORK的真正处理在wpa_supplicant_ctrl_iface_add_network函数中,其代码如下所示。
[-->ctrl_iface.c::wpa_supplicant_ctrl_iface_add_network]
static int wpa_supplicant_ctrl_iface_add_network(struct wpa_supplicant *wpa_s,
char *buf, size_t buflen)
{
struct wpa_ssid *ssid;
int ret;
//wpa_config_add_network返回一个wpa_ssid对象,读者还记得它吗?wpa_ssid是无线网络配置项在
//WPAS中的反映(请参考4.3.3中“wpa_ssid结构体介绍”一节)。wpa_config_add_network内部就是
//分配一个wpa_ssid对象,然后将其保存到一个链表中。注意,wpa_config是wpa_supplicant.conf
//在代码中的代表。所以,此处添加的无线网络信息将会保存到配置文件中,以备下次使用。
ssid = wpa_config_add_network(wpa_s->conf);
......
wpas_notify_network_added(wpa_s, ssid);
ssid->disabled = 1; //disabled为1表示该无线网络未启用,需要通过ENABLE_NETWORK来启动它
//设置该无线网络的默认配置项
wpa_config_set_network_defaults(ssid);
//返回该网络的编号(由wpa_ssid的id变量表示。它在wpa_config_add_network函数中被赋值)
ret = os_snprintf(buf, buflen, "%d\n", ssid->id);
......
return ret;
}
上述代码比较简单,无非就是分配一个wpa_ssid对象,然后设置它的一些默认属性。整个函数返回该wpa_ssid对象的id,即它在链表中的顺序。
wpa_ssid的默认属性对后续流程有一些影响,那么默认属性都是什么呢?不妨来看看wpa_config_set_network_defaults函数,代码如下所示。
[-->config.c::wpa_config_set_network_defaults]
void wpa_config_set_network_defaults(struct wpa_ssid *ssid)
{
//设置proto、pairwise_cipher、group_cipher以及key_mgmt的信息,读者还记得这些变量的含义吗?
//请参考4.3.3中“安全相关成员变量及背景知识介绍”一节
ssid->proto = DEFAULT_PROTO;
ssid->pairwise_cipher = DEFAULT_PAIRWISE;
ssid->group_cipher = DEFAULT_GROUP;
ssid->key_mgmt = DEFAULT_KEY_MGMT;
#ifdef IEEE8021X_EAPOL
ssid->eapol_flags = DEFAULT_EAPOL_FLAGS;//EAP相关变量,见下文解释
ssid->eap_workaround = DEFAULT_EAP_WORKAROUND;
ssid->eap.fragment_size = DEFAULT_FRAGMENT_SIZE;
#endif /* IEEE8021X_EAPOL */
#ifdef CONFIG_HT_OVERRIDES
......//和802.11n有关,本书不涉及
#endif /* CONFIG_HT_OVERRIDES */
}
上述代码中出现了三个和EAPOL相关的变量,此处简单介绍一下:
- eapol_flags:它和动态WEP key有关。只适用于非WPA安全环境中,可取值有三个,分别是1(代码中定义为BIT(0),表示需要为单播数据传输使用动态WEP Key,对应宏为EAPOL_FLAG_REQUIRE_KEY_UNICAST)、2(代码中定义为BIT(1),表示需要为组播数据传输使用动态WEP Key,对应宏为EAPOL_FLAG_REQUIRE_KEY_BROADCAST)以及3(单播和组播都使用动态WEP Key,对应宏为DEFAULT_EAPOL_FLAGS)
- eap_workaround:身份认证方法多种多样,而有些Authenticator服务器(缩写为AS)并不严格遵守规范。该变量表示碰到这种情况时,WPAS是否可以采取“绕”(workaround本意是“变通”)过去的方式来对待这些AS。由于这种不严格的情况非常普遍,所以该值默认是1,
- fragment_size:该变量和EAPOL消息分片大小有关。默认的DEFAULT_FRAGMENT_SIZE大小为1398,表示EAPOL消息只要不超过这个大小,就不用对其进行分片。
“ADD_NETWORK”命令比较简单,它最终将返回给客户端对应的无线网络配置的编号。在本例中,它是0。
下面来看客户端通过“SET_NETWORK”为该无线网络配置项设置参数的处理过程。
============================略略略略略===================
4.6 本章总结和参考资料一览
4.6.1 本章总结
本章对wpa_supplicant进行了一番剖析,涉及如下几个重点内容:
- WPAS的启动:在这条分析路线中,读者能感受到WPAS中多种多样的数据结构以及之间较为复杂的关系。同时,我们对那些极具背景含义的成员变量也进行了深入介绍。
- EAPOL/EAP模块:这一节中,我们先向读者介绍了理论知识,然后结合代码分析了WPAS如何将理论知识转化成代码。从笔者角度来看,状态机的代码比看状态切换图要复杂得多。读者不妨自己亲身体验一下。
- 扫描、关联及4-Way Handshake分析:我们通过一个实例讲解了这一流程涉及的重要函数和相关知识点。该流程实际上包含的代码远比书中给出来的多,原因是WPAS支持的许多其他功能(如WPS、P2P、802.11R、802.11W等)都在这一流程中有涉及。根据笔者自己的学习经验,只有先搞清楚一个最简单的流程,后面才能循序渐进地把其他功能的分析加进来。
提示:本章编撰时,市面上搭载Android 4.2的手机较少。所以此处的分析目标是Android 4.1中的wpa_supplicant。不过笔者比较了它和Android 4.2中的wpa_supplicant。虽然4.2中的WPAS变化较大,但主要体现在一些新增功能和Bug修改上。读者如果搞清楚了本章所介绍的内容,再转去研究4.2中的WPAS所需要的学习成本不会太大。
4.6.2 参考资料一览
概述
[1] http://en.wikipedia.org/wiki/Extensible_Authentication_Protocol
维基百科关于EAP各种方法的一个简单介绍。
[2] http://hostap.epitest.fi/wpa_supplicant/devel/
wpa_supplicant官方开发文档。读者可以简单浏览一下。
wpa_ssid结构体介绍
[3] 802.11-2012 附录M.4 “Suggested pass-phrase-to-PSK mapping”
该节介绍了passphrase转换成PSK的方法,甚至还有伪代码实现。感兴趣的读者不妨结合WPAS中的代码来研究它。
[4] 802.11-2012 第8.4.2.27.2节“Cipher suites”
[5] 802.11-2012 第8.4.2.27.3节“AKM suites”
上述两小节分别介绍了Cipher和AKM suites的情况。注意,其中定义的取值定义是指在RSN IE中的取值,和代码中定义的宏不是一回事。
[6] 802.11-2012 第12章“Fast BSS Transition”
官方文档。不过难度较大,建议读者阅读“Secure Roaming in 802.11 Networks”一书后再去看它。提醒,此书是笔者目前阅读到的关于Wi-Fi Roaming相关知识介绍最完整的一本。
[7] Real 802.11 Security:Wi-Fi Protected Access and 802.11i 第6章“How IEEE 802.11 WEP Works and Why It Doesn't”
关于WEP介绍的章节。另外,对安全感兴趣的读者请仔细阅读此书。
[8] http://www.codealias.info/technotes/opportunistic_pmk_pre-caching
关于Opportunistic PMK Caching的简单介绍。
[9] Secure Roaming in 802.11 Networks第8章“Opportunistic Key Caching”一节
相比[8]而言,这一节对OKC有更为详尽的介绍。
wpa_supplicant结构体介绍
[10] 802.11无线网络权威指南(第二版)第七章“802.11:RSN、TKIP与CCMP”,P171-P172
[11] 802.11-2012 第11.4.2.4节“TKIP countermeasures procedures”
上述两个参考资料介绍了TKIP countermeasures的处理方式。请读者先阅读[10]。
[12] http://www.cisco.com/en/US/docs/solutions/Enterprise/Mobility/vowlan/41dg/vowlan_ch5.html
[13] Secure Roaming in 802.11 Networks第5.2.5节“Background Scanning”
Background Scan技术的介绍。
[14] http://network.chinabyte.com/359/12453859.shtml
[15] http://www.docin.com/p-365323002.html
和GAS以及802.11u相关的一些介绍。
wpa_supplicant_init_iface分析之三
[16] http://www.mjmwired.net/kernel/Documentation/rfkill.txt
[17] http://lwn.net/Articles/335382/
这两篇资料介绍了rfkill相关的信息。感兴趣的读者不妨仔细阅读它们。
[18] http://wenku.baidu.com/view/c74758d280eb6294dd886c53.html
RFC2863 3.1.13“IfAdminStatus and IfOperStatus”一节描述了IfOperStatus的取值情况及相关说明。
[19] http://wireless.kernel.org/en/developers/Documentation/nl80211/kerneldoc
linux wireless kernel官方网站中nl80211内核部分的一些解释。
EAP模块分析
[20] http://tools.ietf.org/pdf/rfc4137.pdf
RFC4137文档的PDF版。相比TXT版而言,它用图来描述状态机的状态切换。
EAPOL模块分析
[21] 802.1X 2004版
WPAS中的802.1X实现是基于802.1X 2004版。相比2010版而言,笔者觉得2004版的内容更具条理性。尤其是其关于EAPOL各状态机的描述非常清晰。
EAPOL-Key交换流程分析
[22] Real 802.11 Security:Wi-Fi Protected Access and 802.11i 第10章“WPA and RSN Key Hierarchy”
[23] 802.11-2012 第11.6“Keys and key distribution”
这两篇参考资料对Pairwise Key和Group Key以及4-Way Handshake、Group Key Handshake都有详细的介绍。
[24] wireless.kernel.org/en/users/Documentation/WoWLAN
[25] msdn.microsoft.com/en-us/library/windows/hardware/ff571052(v=vs.85).aspx
这两篇文章对WoWLAN有一番介绍。读者可简单阅读它们。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论