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 Control0x00, 0x00, // 2-3: Duration0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 4-9: Destination address (broadcast)0xba, 0xde, 0xaf, 0xfe, 0x00, 0x06, // 10-15: Source address0xba, 0xde, 0xaf, 0xfe, 0x00, 0x06, // 16-21: BSSID0x00, 0x00, // 22-23: Sequence / fragment number0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 24-31: Timestamp (GETS OVERWRITTEN TO 0 BY HARDWARE)0x64, 0x00, // 32-33: Beacon interval0x31, 0x04, // 34-35: Capability info0x00, 0x00, /* FILL CONTENT HERE */ // 36-38: SSID parameter set, 0x00:length:content0x01, 0x08, 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, // 39-48: Supported rates0x03, 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-basisuint16_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 packetprintf("%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 eachbeacon_rick[SRCADDR_OFFSET + 5] = line;beacon_rick[BSSID_OFFSET + 5] = line;// Update sequence numberbeacon_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还支持发送其他类型的帧,后续可以进一步探索。最后感谢极术社区赞助的开发板,以及社区工作人员和社群中各位小伙伴的答疑解惑-。