深入理解Wi-Fi P2P

本章主要内容:

  • 介绍Wi-Fi P2P相关知识;
  • 介绍AndroidWifiP2pServicewpa_supplicant的相关代码。

7.1  概述

承接第6章介绍的WSC,本章将继续介绍Wi-Fi AllianceWi-Fi联盟)推出的另外一项重要技术规范Wi-Fi P2P。该规范的商品名为Wi-Fi Direct,它支持多个Wi-Fi设备在没有AP的情况下相互连接。

Android平台的Wi-Fi相关模块中,P2P的功能点主要集中在:

  • Android Framework中的WifiP2pService,其功能和WifiService类似,用于处理和P2P相关的工作。
  • wpa_supplicant中的P2P模块。

WSC一样,本章的分析拟采用如下方法:

  • 首先将介绍P2P所涉及的基础知识。
  • 然后再分析和P2P相关的模块,包括SettingsWifiP2pService以及WPAS

下面,先来认识一下P2P

7.2  P2P基础知识介绍

WFA定义的P2P协议文档全名为“Wi-Fi Peer-to-PeerP2P Technical Specification”,目前的版本为1.1,全长160页。P2P技术使得多个Wi-Fi设备在没有AP的情况下也能构成一个网络(P2P Network,也被称之为P2P Group)并相互通信。

Wi-Fi P2P技术是Wi-Fi Display(也称之为Miracast,详情请参考作者的一篇博文http://blog.csdn.net/innost/article/details/8474683)的基础。在Miracast应用场景中,一台支持P2P的智能手机可直接连接上一台支持P2P的智能电视,智能手机随后将自己的屏幕,或者媒体资源传送给电视机去显示或播放。显然,借助P2P技术,Wi-Fi设备之间的直接相连将极大拓展Wi-Fi技术的使用场景。

注意:根据笔者自己的判断,随着支持越来越多的设备支持P2PMiracast,智能终端设备之间的多屏共享和互动功能将很快得以实现。另外,恰逢本章撰写之际,Google发布了Android 4.3。在这次发布盛会上,Google推出了ChromeCast设备。目前,ChromeCast的技术实现细节还不清楚,据说有可能是Google自己定义的Google cast协议(可参考developers.google.com/cast)。

下面先简单介绍一下P2P的架构。

7.2.1  P2P架构介绍[1]

P2P架构中定义了三个组件,笔者将其称之为一个设备,两种角色。这三个组件分别是:

  • P2P Device:它是P2P架构中角色的实体,读者可把它当做一个Wi-Fi设备。
  • P2P Group OwnerGroup Owner(简称GO)是一种角色,其作用类似于Infrastructure BSS中的AP
  • P2P Client:另外一种角色,其作用类似于Infrastructure BSS中的STA

相信对本书的读者对上面这三个组件的概念并不陌生。实际上,P2P技术模仿了Infrastructure BSS网络结构:

  • 在组建P2P Group(即P2P Network)之前,智能终端都是一个一个的P2P Device
  • 当这些P2P Device设备之间完成P2P协商后,那么其中将有一个并且只能有一个[1]Device来扮演GO的角色(即充当AP),而其他Device来扮演Client的角色。

最终构成的这个P2P Group组织结构如图7-1所示:

深入理解Wi-Fi P2P_第1张图片

7-1  P2P Group示意图

7-1展示了一个典型P2P Group的构成,其中:

  • 和一个Infrastructure BSS类似,一个P2P Group中只能有一个GO。一个GO可以支持1个或多个(即图中的1:nClients连接。
  • 由于GO的功能类似于AP,所以周围那些不支持P2P功能的STA也能发现并关联到GO。这些STA被称之为Legacy Clients

注意:“不支持P2P功能”更准确的定义是指不能处理P2P协议。在P2P网络中,GO等同于AP,所以Legacy Clients也能搜索到GO并关联上它。不过,由于Legacy Clients不能处理P2P协议,所以P2P一些特有功能在这些Legacy Clients中无法实现。

通过上述介绍读者会进一步发现P2P GroupInfrastructure BSS的相似性:

  • P2P Device在构建P2P Group时,它将首先通过WSC来获取安全信息。
  • 然后,Client将利用协商好的安全设置信息去关联[2]GO(即P2P Group中的AP)。

这部分内容和Infrastructure BSSSTA利用WSC先协商安全信息然后再关联至AP的流程完全一样。正是这种相似性,使得P2P能充分利用现有的一些技术规范。图7-2所示为P2P及其依赖的技术项:

深入理解Wi-Fi P2P_第2张图片

7-2  P2P及其依赖的技术项

由图7-2可知:

  • 为了保证一定的传输速率,P2P要求P2P Device必须支持802.11g及以上的规范。其中,安全部分必须支持WPA2。由于P2P技术一个主要的应用场景就是设备之间共享媒体数据(例如前面提到的Miracast应用场景),所以P2P Device还必须支持WMMWi-Fi Multimedia的缩写,它是一种源自802.11eQoS服务,主要针对实时视音频数据的传输)。
  • P2P Client关联到GO之前,需要先通过WSC来协商安全信息,所以WSC也是P2P的依赖技术项。
  • 在上述技术基础上,P2P规范定义了一些特有的技术项,图7-2列出了其中三种必须实现的技术项,它们分别是P2P DiscoveryP2P Group Operation以及P2P PowerManagerment。除了这三个必选技术项外,P2P规范还定义了一个可选技术项,名为Managed P2P Device Operation(该技术项定义了如何在企业级环境中由对应的IT部门来统一配置和管理P2P设备)。

在如图7-2所示的技术项中,P2P DiscoveryP2P所特有的,也是其核心。本章将主要围绕它进行介绍。首先来看P2P Discovery

提示:

1P2P Group Operation讲得是GO如何管理一个Group,也就是GO的工作职责。这部分内容请读者自行学习参考资料[2]一节。

2P2P PowerManagementP2P设备的电源管理有关,用于节省不必要的电力损耗。由于篇幅关系,本章不拟讨论它。请感兴趣的读者自行学习参考资料[3]

7.2.2  P2P Discovery介绍[4]

P2P Discovery的目的很简单,就是使得多个P2P Device能够互相发现并构建一个Group。根据规范,它包括四个主要技术子项:

  • Device Discovery:用于P2P设备搜索周围其他支持P2P的设备。
  • Service Discovery:该Device Discovery基础上,P2P还支持搜索指定的服务。这部分功能属于可选项,笔者觉得它和第二章2.2.5.1Apple Bonjour技术介绍”中提到的Bonjour类似。
  • Group Formation:用于决定两个P2P Device谁来扮演GO,谁来扮演Client
  • P2P Invitation:用于激活一个Persistent Group(见下文解释),或者用于邀请一个Client加入一个当前已存在的Group

提示:GroupPersistent(永久性) GroupTemporary(临时性) Group两种。我们举二个简单例子来说明二者的区别:

Temporary Group:当你有份文件要传给一个同事时,双方打开手机的Wi-Fi P2P功能,建立一个Group,然后传输文件,最后关闭Wi-Fi P2P。在这个过程中,GOClient的角色分配由Group Formation来决定,这一次的GO可能是你的设备,下一次则可能是其他人的设备。对于这种Group,在建立Group过程中所涉及的安全配置信息以及和Group相关的信息(以后我们会见到它)都是临时的,即下一次再组建Group时,这些安全配置信息都将发生变化。

Persistent Group:在这种Group中,GO由指定设备来扮演,而且安全配置信息及Group相关信息一旦生成,后续就不会再发生变化(除非用户重新设置)。Persistent Group中的GO多见于固定用途的设备,例如打印机等。如此,除了第一次通过P2P连接到打印机时相对麻烦一点(需要利用WSC协商安全配置信息)外,后续使用的话,由于P2P设备将保存这些安全信息,所以下一次再使用打印机时就能利用这些信息直接和打印机进行关联了。

由于篇幅关系,本章将仅介绍上述四个知识点中最为基础的Device DiscoveryGroup Formation,而Service DiscoveryP2P Invitation的内容请读者学习完本章后再仔细研读P2P规范。

1.  P2P Device Discovery介绍

P2P Device Discovery虽然也是利用802.11中的Probe RequestProbe Response帧来搜索周围的P2P设备,但其步骤却比Infrastructure BSS中的无线网络搜索要复杂。举一个简单的例子,一个P2P Device除了自己要发送Probe Request帧外,还得接收来自其他设备的Probe Request帧并回复Probe Response帧。而在Infrastructure BSS中,只有AP会发送Probe Response帧。

为了加快搜索速度,P2PDevice Discovery定义了两个状态和两个阶段。

1  Device Discovery工作流程介绍

P2P Device Discovery的工作流程包含两个状态和两个阶段。先来看两个状态,它们分别是:

  • Search State:在该状态中,P2P Device将在2.4GHz1,6,11频段上分别发送Probe Request帧。这几个频段被称为Social Channels。为了区别非P2PProbe Request帧,P2P Device Discovery要求必须在Probe Request帧中包含P2P IE
  • Listen State:在该状态下,P2P Device将随机选择在1,6,11频段中的一个频段(被选中的频段被称为Listen Channel)监听Probe Request帧并回复Probe Response帧。值得指出的是,Listen Channel一旦选择好后,在整个P2P Discovery阶段就不能更改。另外,在这个阶段中,P2P Device只处理那些包含了P2P IE信息的Probe Request帧。

再来看两个阶段,它们分别是:

  • Scan Phase:扫描阶段。这一阶段和前面章节介绍的无线网络扫描一样,P2P Device会在各个频段上发送Probe Request帧(主动扫描)。P2P Device在这一阶段中不会处理来自其他设备的Probe Request帧。这一阶段过后,P2P Device将进入下一个阶段,即Find Phase
  • Find Phase:虽然从中文翻译来看,ScanFind意思比较接近,但P2PFind Phase却和Scan Phase大不相同。在这一阶段中,P2P Device将在Search StateListen State之间来回切换。Search State中,P2P Device将发送Probe Request帧,而Listen State中,它将接收其他设备的Probe Request帧并回复Probe Response帧。

7-3所示为P2P Device Discovery的流程示意图。

深入理解Wi-Fi P2P_第3张图片

7-3  P2P Device Discovery流程示意图

7-3所示为两个P2P DeviceDiscovery流程,其中:

  • Discovery启动后,Device首先进入Scan Phase。在这一阶段,P2P设备在其支持的所有频段上都会发送Probe Request帧。
  • Scan Phase完成后,Device进入Find Phase。在这一阶段中,Device将在ListenSearch State中切换。根据前面的介绍,每一个设备的Listen ChannelDiscovery开始前就已确定。例如,图7-3Device 1Listen Channel1,而Device 2Listen Channel6
  • Find Phase中,P2P规范对Device处于Listen State的时间也有所规定,其时间是100TU的整数倍,倍数值是一个随机数,位于minDiscoverableIntervalmaxDiscoverableInterval之间。这两个值默认为13,而厂商可以修改。选择随机倍数的原因是为了防止两个Device进入所谓的Lock-Step怪圈,即两个Device同时进入Listen State,等待相同的时间后又同时进入Search State。如此,双方都无法处理对方的Probe Request信息(Search State中,Device只发送Probe Request)。图7-3中,Device 1第一次在Listen State中待了2100TU,而第二次在Listen State中待了1100TU
  • Device处于Find Phase中的Search State时,它将在1,6,11频段上发送Probe Request帧。注意,只有当两个设备处于同一频段时,一方发送的帧才能被对方接收到。

提示:P2P规范对两个状态及两个阶段的描述非常细致,甚至于对每个状态能干什么和不能干什么都有详细说明。不过,从如何快速掌握P2P框架的角度来看,笔者觉得这些内容过于啰嗦。

了解了Device Discovery的大体工作流程后,下面我们将通过实例来看看P2P使用到的Probe RequestProbe Response帧。


[1]假设这设备将只组成一个P2P Network

[2]注意,此处的关联指得是RSNA,其工作流程包括包括4-WayHandshake

========================略略略略略略略略==========

7.4  wpa_supplicant中的P2P介绍

在第55.2.3中“WifiNative介绍”一节中曾介绍了wpa_supplicant的启动,在那一节中,读者会发现wpa_supplicant进程由WifiStateMachine启动。在Android官方代码中,虽然Java层有WifiServiceWifiP2pService两个几乎完全不同的Wifi服务,但二者都只和Native层的唯一一个wpa_supplicant进程交互。简单点说,Android原生代码中,一个wpa_supplicant进程将同时支持WifiServiceWifiP2pService

上述这种设计方法使得wpa_supplicant负担较重,所以,一些手机厂商会为WifiServiceWifiP2pService各创建一个wpa_supplicant进程,使得它们能各司其职而互不干扰。以笔者的Galaxy Note2为例,它的,WifiService将和wpa_supplicant进程交互,而WifiP2pService将和一个名为p2p_supplicant(经过笔者测试,p2p_supplicant实际上就是wpa_supplicant,只不过名字不同罢了)的进程交互。

7-26所示为Galaxy Note2 init配置文件中关于p2p_supplicant服务的示意图:

7-26  Galaxy Note2p2p_supplicant服务配置项

由图7-26可知:

  • init配置文件定义了一个名为p2p_supplicant的服务,该服务启动的进程为p2p_supplicant。根据笔者的测试,p2p_supplicant其实就是wpa_supplicant,只不过换了一个名字而已。
  • p2p_supplicant使用的配置文件名为/data/misc/wifi/p2p_supplicant.conf文件,其内容如图7-27所示。

深入理解Wi-Fi P2P_第4张图片

7-27  p2p_supplicant.conf内容

提示:关于init配置文件中wpa_supplicant服务的说明,请读者参考第44.3wpa_supplicant初始化流程分析”一节。

7-27中,p2p_supplicant对于的ctrl_iface路径为/data/misc/wifi/sockets。所以,如果要使用wpa_clip2p_supplicant交互的话,必须指定正确的ctrl_iface路径。图7-28所示为笔者用wpa_cli测试p2p_supplicant时的示例截图:

深入理解Wi-Fi P2P_第5张图片

7-28  wpa_clip2p_supplicant交互

下面来分析wpa_supplicant中和P2P相关的代码。

注意:以Galaxy Note2为例,p2p_supplicant就是wpa_supplicant,只是编译时打开了P2P相关的选项。下面的分析将以wpa_supplicant中和P2P相关的代码及工作流程为主。

7.4.1  P2P模块初始化

首先来看WPASP2P相关模块的初始化。该初始化工作我们在第44.3.4中“wpa_supplicant_init_iface分析之五”曾提到过,其对应的函数wpas_p2p_init,马上来看它

[p2p_supplicant.c::wpas_p2p_init]

int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)

{

    struct p2p_config p2p; //p2p变量指向一个p2p_config对象,代表P2P模块的配置信息

    unsigned int r;  int i;

    //①WPA_DRIVER_FLAGS_P2P_CAPABLE:代表Wifi驱动对P2P支持的能力,详情见下文解释

    if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE))  return 0;

 

    if (global->p2p)  return 0;

    //如果wifi driver能完成P2P功能,就不用劳驾WPAS了

    if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {......}

 

    //②初始化并设置p2p_config对象

    os_memset(&p2p, 0, sizeof(p2p));

    p2p.msg_ctx = wpa_s;  p2p.cb_ctx = wpa_s;

    p2p.p2p_scan = wpas_p2p_scan; //P2P对应的扫描函数

    .......//设置一些回调函数

    p2p.get_noa = wpas_get_noa; p2p.go_connected = wpas_go_connected;

    //设置P2P Device address。

    os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN);

    os_memcpy(p2p.dev_addr, wpa_s->global->p2p_dev_addr, ETH_ALEN);

   //设置P2P模块配置信息,包括device name,model name,uuid等

    p2p.dev_name = wpa_s->conf->device_name;

    p2p.manufacturer = wpa_s->conf->manufacturer;

    p2p.model_name = wpa_s->conf->model_name;

    p2p.model_number = wpa_s->conf->model_number;

    p2p.serial_number = wpa_s->conf->serial_number;

    if (wpa_s->wps) {

        os_memcpy(p2p.uuid, wpa_s->wps->uuid, 16);

        p2p.config_methods = wpa_s->wps->config_methods;

    }

    //设置Operation Channel信息和listen channel信息

    if (wpa_s->conf->p2p_listen_reg_class &&

        wpa_s->conf->p2p_listen_channel) {

        p2p.reg_class = wpa_s->conf->p2p_listen_reg_class;

        p2p.channel = wpa_s->conf->p2p_listen_channel;

    } else {......//设置默认值}

   ......

     //设置国家码

    if (wpa_s->conf->country[0] && wpa_s->conf->country[1]) {

        os_memcpy(p2p.country, wpa_s->conf->country, 2);

        p2p.country[2] = 0x04;

    } else//配置文件中没有设置国家,所以取值为"XX\x04"

        os_memcpy(p2p.country, "XX\x04", 3);//读者可回顾图7-7

     //判断wifi 驱动是否支持配置文件中设置的operationg channel和listen channel

    if (wpas_p2p_setup_channels(wpa_s, &p2p.channels)) {......}

 

    os_memcpy(p2p.pri_dev_type, wpa_s->conf->device_type,WPS_DEV_TYPE_LEN);

 

    p2p.num_sec_dev_types = wpa_s->conf->num_sec_device_types;

    os_memcpy(p2p.sec_dev_type, wpa_s->conf->sec_device_type,

             p2p.num_sec_dev_types * WPS_DEV_TYPE_LEN);

    //是否支持concurrent operation

    p2p.concurrent_operations = !!(wpa_s->drv_flags&WPA_DRIVER_FLAGS_P2P_CONCURRENT);

 

    p2p.max_peers = 100;//最多能保存100个对端P2P Device信息

    //配置文件中没有设置p2p_ssid_postfix,但P2pStateMachine在initializeP2pSettings函数中

   //将设置P2P SSID后缀。以笔者的Galaxy Note2为例,其P2P SSID后缀为“Android_4aa9”

    if (wpa_s->conf->p2p_ssid_postfix) {......}

 

    p2p.p2p_intra_bss = wpa_s->conf->p2p_intra_bss;

    //③global->p2p指向一个p2p_data结构体,它是WPAS中P2P模块的代表

    global->p2p = p2p_init(&p2p);

    ......

    for (i = 0; i < MAX_WPS_VENDOR_EXT; i++) {//拷贝vendor厂商特定的WSC属性信息

        if (wpa_s->conf->wps_vendor_ext[i] == NULL)  continue;

        p2p_add_wps_vendor_extension(global->p2p, wpa_s->conf->wps_vendor_ext[i]);

    }

 

    return 0;

}

由上述代码可知,wpas_p2p_init的工作非常简单,主要内容包括:

  • 初始化一个p2p_config对象,然后根据p2p_supplicant.conf文件的信息来设置其中的内容,同时还需要为P2P模块设置一些回调函数。
  • 调用p2p_init函数以初始化P2P模块。

下面来介绍上述代码中涉及到的一些知识。

1.  Driver Flags和重要数据结构介绍

先来看上述代码中提到的drv_flags变量。WPAS中,Wifi驱动对P2P功能的支持情况就是由它来表达的。笔者的Galaxy Note2中该变量取值为0x2EAC0,其表达的含义如下:

[-->driver.h]

#define WPA_DRIVER_FLAGS_AP        0x00000040  //Wifi driver支持AP。它使得P2P设备能扮演GO

/*

  标志标明association成功后,kernel driver需要设置WEP key

  这个标志出现的原因是由于kernel API发生了变动,使得只能在关联成功后才能设置key

*/

#define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE    0x00000080

#define WPA_DRIVER_FLAGS_P2P_CONCURRENT    0x00000200 //Wifi驱动支持STA和P2P的并发运行

#define WPA_DRIVER_FLAGS_P2P_CAPABLE    0x00000800 //Wifi驱动支持P2P

/*

  7.2.2.1.2“Probe Request帧设置”一节曾提到过说P2P包含Device Address和Interface Address

  两种类型的地址。在实际实现过程中,这两个地址分别代表两个virtual interface。显然,P2P中第一个和一直

  存在的是拥有Device Address的vitural interface。下面这个标志表示该virtual interface可以参与

  P2P管理(除P2P Group Operation之外的工作)工作以及非P2P相关的工作(例如利用这个virtual

  interface 加入到一个BSS)

*/

#define WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P  0x00002000

/*

 该标志主要针对associate操作。当关联操作失败后,如果driver支持该选项,则表明driver能处理失败之

 后的各种收尾工作(Key、timeout等工作)。否则,WPAS需要自己处理这些事情

*/

#define WPA_DRIVER_FLAGS_SANE_ERROR_CODES        0x00004000

//下面这个标志和off channel机制有关。读者可参考第4章4.3.4中“capability介绍”一节。当802.11

//MAC帧通过off channel发送的话,下面这个标志表示driver会反馈一个发送情况(TX Report)消息给WPAS

#define WPA_DRIVER_FLAGS_OFFCHANNEL_TX            0x00008000

//下面这两个标志表示kernel中的driver是否能反馈Deauthentication/Disassociation帧

//发送情况(TX Report)

#define WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS        0x00020000

下面来看wpas_p2p_init中出现的几个重要数据结构,首先是p2p_configp2p_data,它们的成员如图7-29所示:

深入理解Wi-Fi P2P_第6张图片

7-29  p2p_configp2p_data结构示意图

7-29展示了p2p_configp2p_data两个数据结构中一些重要的成员。其中:

  • p2p_config定义了20个回调函数。这些回调函数定义了P2P模块和外界交互的接口。在wpas_p2p_init中,这些回调函数均指向p2p_supplicant.c中对应的函数,例如p2p_scan指向wpas_p2p_scandev_lost指向wpas_dev_lost。另外,由于回调函数的参数比较复杂,所以图中均省略了参数信息。
  • p2p_data指向一个p2p_config对象。

下面来看另外几个重要数据结构的内容,如图7-30所示:

深入理解Wi-Fi P2P_第7张图片

7-30  p2p_device及其他数据结构示意图

7-30展示了五种数据结构,其中:

  • p2p_device代表一个P2P设备。其中一些诸如设备名,Device CapabilityBitmap等信息保存在一个类型为p2p_peer_info的对象中。
  • p2p_group代表一个P2P Group的信息,其内部包含一个p2p_group_config对象和一个p2p_group_member链表。p2p_group_config表示该Group的配置信息,p2p_group_member代表Group MemberP2P Client的信息。

提示: WPAS中定义了非常多的数据结构类型,这极大增加了初学者的学习难度。根据笔者自己的经验,建议读者在学习过程中先简单了解这些数据结构的名字及作用,然后在具体代码分析时再结合代码逻辑来了解这些数据结构及其内部各个成员变量的具体作用。

 

7.5  本章总结和参考资料说明

7.5.1  本章总结

本章对Wi-Fi P2P进行了详细介绍,主要内容包括:

  • P2P理论知识。从完整性来说,本章介绍的内容只是P2P规范中最基础的部分。但对于初学者而言,这部分的难度也不算小。就笔者自己的学习经历而言,这部分内容需要反复琢磨和研究,有时候还需要结合代码分析才能真正掌握其精髓。
  • 在学习完P2P理论知识后,我们对WifiP2pSettings以及WifiP2pService进行了介绍。这部分内容比较简单,读者可轻松掌握相关知识。
  • 最后,我们对WPAS中的P2P模块及运行机制进行了介绍。就所涉及到的知识而言,这些内容并不复杂,但由于P2P以及和wifi driver在具体实现时有诸多考虑(例如off channel的情况,TX Report的处理),所以其工作流程反倒显得比较繁琐。

最后,希望读者在本章的基础上,完成下列的一些任务:

  • 通读P2P规范,了解Group OperationInvitationDevice Discoverability以及P2P Power Management相关知识。
  • 继续研究wpas_start_wps_go代码,掌握WPASWSC Registrar以及AP的工作流程。

你可能感兴趣的:(p2p,Wi-Fi)