XR806是一款使用ARMv8-M的Wi-Fi BLE Combo MCU。本文使用XR806开发板以及基于FreeRTOS的XR806 SDK实现了自定义发送802.11 Beacon帧,并进行了无线抓包分析以及扫描测试来验证帧的发送结果。
环境配置过程
环境搭建可以参考官方文档开发环境搭建。本测试中使用的开发环境为Ubuntu 22.04。需要注意的是,在下载ARM Toolchain时,由于网站更新,文档中链接已不可用,当前可用的下载链接为https://developer.arm.com/downloads/-/gnu-rm,在其中找到对应的gcc-arm-none-eabi-8-2019-q3-update版本下载即可。
配置好环境后选取某个Demo或者Example进行编译,在Ubuntu下,使用的烧录工具为SDK中tools
目录下的phoenixMC
可执行程序,示例的烧录命令为
./phoenixMC -c /dev/ttyUSB0 -i ../out/xr_system.img
其他参数信息可以使用-h
获取。烧录好后可以连接串口查看输出,控制台输出可能会出现换行不对齐的情况,此时需要进行换行修正。在本次测试中使用的串口工具为picocom
,需要将输出的\n
转为\r\n
示例的连接串口命令为
picocom -b 115200 --imap lfcrlf /dev/ttyUSB0
在烧录时需要让开发板进入升级模式,如果当前程序支持upgrade
命令,可以直接发送upgrade
命令。若不支持,需要短接开发板上的两个触点或者使用Windows下的烧录工具,在工具上勾选硬件复位烧写模式。
测试过程
通过浏览XR806的SDK可以发现,在wlan.h
中提供有APIint wlan_send_raw_frame(struct netif *netif, int type, uint8_t *buffer, int len);
。该API可以支持发送自定义的802.11帧,只需要提供网络接口,帧类型,数据帧和长度即可。受esp32-80211-tx启发,在本测试中基于该API发送自定义的Beacon帧,达到同时存在多个AP的假象。在IBSS网络架构中,AP每隔一段时间就会向外界发出一个Beacon帧用来宣告自己802.11网络的存在。平时Wi-Fi的被动扫描也是基于Beacon帧进行。
使用API构造Beacon
进一步浏览SDK可以发现,SDK中提供了wlan_construct_beacon
的API,这简化了我们构造Beacon的过程。只要提供beacon的部分字段信息即可,如SA,DA,BSSID,Channel等。具体代码如下:
#include <stdio.h>
#include <string.h>
#include "net/wlan/wlan.h"
#include "net/wlan/wlan_defs.h"
#include "net/wlan/wlan_ext_req.h"
#include "net/wlan/wlan_frame.h"
#include "common/framework/net_ctrl.h"
#include "common/framework/platform_init.h"
#include "lwip/inet.h"
#define CMD_WLAN_NETIF wlan_netif_get(WLAN_MODE_NONE)
#define BEACON_FRAME_LEN 256
static uint8_t beacon_frame_buf[BEACON_FRAME_LEN];
typedef struct {
uint8_t *data;
uint32_t len;
} frame_data;
static uint8_t beacon_addr[6];
static char beacon_ssid[32];
static uint32_t beacon_len;
static frame_data beacon_frame;
char *ssids[] = {
"1 Hello Wireless World",
"2 from Allwinner XR806",
"3 running on FreeRTOS",
"4 for Jishu Community"
};
uint8_t bssid[4][6] = {
{0xba, 0xde, 0xaf, 0xfe, 0x00, 0x06},
{0xba, 0xde, 0xaf, 0xfe, 0x00, 0x07},
{0xba, 0xde, 0xaf, 0xfe, 0x00, 0x08},
{0xba, 0xde, 0xaf, 0xfe, 0x00, 0x09},
};
#define TOTAL_LINES (sizeof(ssids) / sizeof(char *))
uint8_t line = 0;
static void beacon_frame_create(void)
{
wlan_ap_config_t config;
memset(&config, 0, sizeof(config));
config.field = WLAN_AP_FIELD_SSID;
if (wlan_ap_get_config(&config) != 0) {
printf("get config failed\n");
return;
}
printf("ssid:%s,ssid_len: %d\n", ssids[line], strlen(ssids[line]));
memcpy(beacon_ssid, ssids[line], strlen(ssids[line]));
memcpy(beacon_addr, bssid[line], IEEE80211_ADDR_LEN);
beacon_len = wlan_construct_beacon(beacon_frame_buf, BEACON_FRAME_LEN, beacon_addr, NULL, beacon_addr,
(uint8_t *)beacon_ssid, strlen(ssids[line]), 1);
if (++line >= TOTAL_LINES)
{
line = 0;
}
beacon_frame.data = beacon_frame_buf;
beacon_frame.len = beacon_len;
printf("beacon_len %d\n", beacon_len);
int ret = 0;
ret = wlan_send_raw_frame(CMD_WLAN_NETIF, IEEE80211_FC_STYPE_AUTH, beacon_frame.data, beacon_frame.len);
printf("Send beacon frame: %d\n", ret);
}
int main(void)
{
platform_init();
net_switch_mode(WLAN_MODE_HOSTAP);
while(1)
{
OS_MSleep(100 / TOTAL_LINES);
beacon_frame_create();
}
return 0;
}
代码的基本逻辑为:首先预定义好需要发送的SSID及其对应的BSSID,然后在主函数中先初始化AP模式,然后每100ms发送一次所有的Beacon。发送Beacon前需要填充好Beacon帧的内容。
测试效果如下:
在代码中我们使用的SSID列表为
"1 Hello Wireless World",
"2 from Allwinner XR806",
"3 running on FreeRTOS",
"4 for Jishu Community"
上电后程序启动,使用手机扫描Wi-Fi便可以查看到这些AP信息
其中AP-XRADIO为默认的AP名称。
不使用API构造Beacon
我们也可以不使用相关API,而直接填充内容。为了演示,在这里将参考项目的实现迁移过来,具体代码如下:
#include <stdio.h>
#include <string.h>
#include "net/wlan/wlan.h"
#include "net/wlan/wlan_defs.h"
#include "net/wlan/wlan_ext_req.h"
#include "net/wlan/wlan_frame.h"
#include "common/framework/net_ctrl.h"
#include "common/framework/platform_init.h"
#include "lwip/inet.h"
#define CMD_WLAN_NETIF wlan_netif_get(WLAN_MODE_NONE)
uint8_t beacon_raw[] = {
0x80, 0x00, // 0-1: Frame Control
0x00, 0x00, // 2-3: Duration
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 4-9: Destination address (broadcast)
0xba, 0xde, 0xaf, 0xfe, 0x00, 0x06, // 10-15: Source address
0xba, 0xde, 0xaf, 0xfe, 0x00, 0x06, // 16-21: BSSID
0x00, 0x00, // 22-23: Sequence / fragment number
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 24-31: Timestamp (GETS OVERWRITTEN TO 0 BY HARDWARE)
0x64, 0x00, // 32-33: Beacon interval
0x31, 0x04, // 34-35: Capability info
0x00, 0x00, /* FILL CONTENT HERE */ // 36-38: SSID parameter set, 0x00:length:content
0x01, 0x08, 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, // 39-48: Supported rates
0x03, 0x01, 0x01, // 49-51: DS Parameter set, current channel 1 (= 0x01),
0x05, 0x04, 0x01, 0x02, 0x00, 0x00, // 52-57: Traffic Indication Map
};
char *rick_ssids[] = {
"01 Never gonna give you up",
"02 Never gonna let you down",
"03 Never gonna run around",
"04 and desert you",
"05 Never gonna make you cry",
"06 Never gonna say goodbye",
"07 Never gonna tell a lie",
"08 and hurt you"
};
#define BEACON_SSID_OFFSET 38
#define SRCADDR_OFFSET 10
#define BSSID_OFFSET 16
#define SEQNUM_OFFSET 22
#define TOTAL_LINES (sizeof(rick_ssids) / sizeof(char *))
int main(void)
{
platform_init();
net_switch_mode(WLAN_MODE_HOSTAP);
uint8_t line = 0;
// Keep track of beacon sequence numbers on a per-songline-basis
uint16_t seqnum[TOTAL_LINES] = { 0 };
int ret = 0;
while (1)
{
OS_MSleep(100 / TOTAL_LINES);
// Insert line of Rick Astley's "Never Gonna Give You Up" into beacon packet
printf("%i %i %s\r\n", strlen(rick_ssids[line]), TOTAL_LINES, rick_ssids[line]);
uint8_t beacon_rick[200];
memcpy(beacon_rick, beacon_raw, BEACON_SSID_OFFSET - 1);
beacon_rick[BEACON_SSID_OFFSET - 1] = strlen(rick_ssids[line]);
memcpy(&beacon_rick[BEACON_SSID_OFFSET], rick_ssids[line], strlen(rick_ssids[line]));
memcpy(&beacon_rick[BEACON_SSID_OFFSET + strlen(rick_ssids[line])], &beacon_raw[BEACON_SSID_OFFSET], sizeof(beacon_raw) - BEACON_SSID_OFFSET);
// Last byte of source address / BSSID will be line number - emulate multiple APs broadcasting one song line each
beacon_rick[SRCADDR_OFFSET + 5] = line;
beacon_rick[BSSID_OFFSET + 5] = line;
// Update sequence number
beacon_rick[SEQNUM_OFFSET] = (seqnum[line] & 0x0f) << 4;
beacon_rick[SEQNUM_OFFSET + 1] = (seqnum[line] & 0xff0) >> 4;
seqnum[line]++;
if (seqnum[line] > 0xfff)
seqnum[line] = 0;
// esp_wifi_80211_tx(WIFI_IF_AP, beacon_rick, sizeof(beacon_raw) + strlen(rick_ssids[line]), false);
ret = wlan_send_raw_frame(CMD_WLAN_NETIF, IEEE80211_FC_STYPE_AUTH, beacon_rick, sizeof(beacon_raw) + strlen(rick_ssids[line]));
printf("Send beacon: %d\n", ret);
if (++line >= TOTAL_LINES)
line = 0;
}
return 0;
}
测试效果如下:
使用Netspot工具获取无线AP列表
可以看到我们定义的SSID列表(Never gonna give you up:)),同时和默认的AP名称AP-XRADIO。
对XR806的Beacon进行无线抓包分析,如图所示
可以看出XR806所支持的速率和其他特性。
总结
本次测试发送了自定义的Beacon帧,实际上XR806还支持发送其他类型的帧,后续可以进一步探索。最后感谢极术社区赞助的开发板,以及社区工作人员和社群中各位小伙伴的答疑解惑^-^。