高通QSDK 添加vendor ie的方法
概述
无线子系统支持add、remove和update Vendor IE,在Bencon、ProbeReq、ProbeResp、 AssocReq和 AssocResp 无线帧中使用。无线底层驱动必须提供这些功能的API和CLI工具。
主要的结构体如下:

基本命令
wlanconfig athX vendorie add len <oui+pcap_data in bytes> oui <eg:xxxxxx> pcap_data <eg:xxxxxxxx> ftype_map <eg:xx>
wlanconfig athX vendorie update len <oui+pcap_data in bytes> oui <eg:xxxxxx> pcap_data <eg:xxxxxxxx> ftype_map <eg:xx>
wlanconfig athX vendorie remove len <oui+pcap_data in bytes> oui <eg:xxxxxx> pcap_data <eg:xx> # xx 为0xff 清除所有类型,否则为其他类型
wlanconfig athX vendorie list
wlanconfig athX vendorie list len <oui in bytes> oui <eg:xxxxxx>
wlanconfig athX addie ftype <frame type> len < data len> data <data>
# ftype: 0/2/4('0'-Beacon,'2'-Probe Resp,'4'-Assoc Resp)
# len: Data length(IE length + 2)
# data: Data(in hex)
下面介绍CLI工具wlanconfig 对该功能的支持。
增加:
wlanconfig athX vendorie add len <oui+pcap_data 字节和> oui <57494d> pcap_data <11223344> ftype_map <12>使用范例:
wlanconfig ath2 vendorie add 7 oui 57494d pcap_data 11223344 ftype_map 12
参数解释如下:
len:oui+pcap_data的长度和,最大128字节,比如上面一共7个字节(oui 3字节+pcap_data 4字节)oui:格式如下:例如WIM三个字符串的十六进制为0x57(W)、0x49(I)、0x4d(M),那么oui的值为57494d 注意:目前oui只支持3个字节长度,由于上面结构体大小决定。pcap_data:自己想增加的数据,格式如下11223344 均为十六进制ftype_map:帧类型: 十六进制12:beacon & assoc res
05:probe request & assoc res
注意:该值映射关系如下:参见代码:
qca/src/qca-wifi/umac/include/ieee80211_mlme_app_ie.h设置的ftype_map会和下面的宏做&操作,只要置位,就会选中该帧,加入该ie#define IEEE80211_VENDORIE_INCLUDE_IN_BEACON 0x10 #define IEEE80211_VENDORIE_INCLUDE_IN_ASSOC_REQ 0x01 #define IEEE80211_VENDORIE_INCLUDE_IN_ASSOC_RES 0x02 #define IEEE80211_VENDORIE_INCLUDE_IN_PROBE_REQ 0x04 #define IEEE80211_VENDORIE_INCLUDE_IN_PROBE_RES 0x08
空口抓包如下:

更新:
wlanconfig athX vendorie update len <oui+pcap_data 字节和> oui <57494d> pcap_data <11223344> ftype_map <12>例如:
wlanconfig ath2 vendorie update len 9 oui 57494d pcap_data 112233445566 ftype_map 12
列出:
wlanconfig athX vendorie listroot@OpenWrt:~# wlanconfig ath2 vendorie list sizeof vendorie: 16 ---------------------------------------- Listing vendor IEs matching the following OUI type Vendor IE info Ioctl CMD id : 33 Total length = 110 Frame type : 1 ID : 30 Length : 36 OUI : 010000 Private capibility_data : 0f ac 04 04 00 00 0f ac 0a 00 0f ac 09 00 0f ac 04 00 0f ac 08 02 00 00 0f ac 02 00 0f ac 04 0c 00 Frame type : 1 ID : 36 Length : 3 OUI : 378000 Private capibility_data : Frame type : 1 ID : dd Length : 7 OUI : 57494d Private capibility_data : 11 22 33 44 Frame type : 2 ID : 30 Length : 36 OUI : 010000 Private capibility_data : 0f ac 04 04 00 00 0f ac 0a 00 0f ac 09 00 0f ac 04 00 0f ac 08 02 00 00 0f ac 02 00 0f ac 04 0c 00 Frame type : 2 ID : 36 Length : 3 OUI : 378000 Private capibility_data : Frame type : 4 ID : dd Length : 7 OUI : 57494d Private capibility_data : 11 22 33 44 ----------------------------------------
删除:
wlanconfig athX vendorie remove 4 <oui+pcap_data in bytes> oui <eg:xxxxxx> pcap_data <eg:xx>**注意:**这里最后 pcap_data eg:xx为一个字节数据,0xff 为所有类型,或者其他类型
代码分析
//wlanconfig ath2 vendorie remove len <oui+pcap_data in bytes> oui <eg:xxxxxx> pcap_data <eg:xx>
// argv[1] argv[2] argv[3] argv[4] argv[5] argv[6] argv[7] argv[8] argv[9]
int main(int argc, char *argv[])
{
ifname = argv[1];
cmd = argv[2];
if(streq(cmd, "vendorie")) {
handle_command = handle_command_vendorie;
}
handle_command(argc, argv, ifname, &sock_ctx); // 这里实际调用的是handle_command_vendorie,下面分析该函数
}
int handle_command_vendorie (int argc, char *argv[], const char *ifname,struct socket_context *sock_ctx)
{
if (argc == ARG_COUNT_VENDOR_IE_REMOVE && streq(argv[3], "remove")) {
if (streq(argv[4], "len") && streq(argv[6], "oui") && streq(argv[8], "pcap_data")){
return handle_vendorie (sock_ctx, ifname, IEEE80211_WLANCONFIG_VENDOR_IE_REMOVE,atoi(argv[5]), argv[7], argv[9], NULL);
// 下面分析该函数handle_vendorie
}
}
}
static int handle_vendorie(struct socket_context *sock_ctx, const char *ifname,
IEEE80211_WLANCONFIG_CMDTYPE cmdtype,
int len, //---------------------atoi(argv[5] = <oui+pcap_data in bytes>
char *in_oui, //---------------------argv[7] = <eg:xxxxxx>
char *in_cap_info, //---------------------argv[9] = <eg:xx>
char *in_ftype_map) //---------------------NULL
{
u_int8_t ie_buf[MAX_VENDOR_BUF_LEN + 12];
struct ieee80211_wlanconfig_vendorie *vie = (struct ieee80211_wlanconfig_vendorie *) &ie_buf;
// 这里有个细节,如果是remove命令,len必须为4
if (cmdtype == IEEE80211_WLANCONFIG_VENDOR_IE_LIST || cmdtype == IEEE80211_WLANCONFIG_VENDOR_IE_REMOVE) {
if (len != 4 && len != 3 && len != 0){
printf(" Invalid length ...Expected length is 3 or 0 for list command and 4 for remove command\n");
return -1;
}
if(len != 0)
{
if (strlen(in_oui) != VENDORIE_OUI_LEN*2) {
printf("Invalid OUI , OUI expected always 3 byte, format: xxxxxx)\n");
return -1;
}
strlcpy(oui, in_oui, ((VENDORIE_OUI_LEN * 2) + 1));
if (char2hex(oui) != 0) {
printf("Invalid OUI Hex String , Correct Len & OUI field \n");
return -1;
}
}
}
if(cmdtype == IEEE80211_WLANCONFIG_VENDOR_IE_REMOVE)
{
//从这里也可以看出,len必须为4
if (len != 4) {
printf(" Invalid length ...Expected length is 4\n");
return -1;
}
// 从这里看出len - VENDORIE_OUI_LEN) *2 = (4-3)*2= 2 = in_cap_info
// 所以 in_cap_info必须为2个字节,strlen(in_cap_info)=2 刚好,因为argv[9]是一个字节的字符串,还有结尾符号 /0
// in_cap_info取值怎么取呢?下面介绍
if (strlen(in_cap_info) != (len - VENDORIE_OUI_LEN) *2) {
printf("Invalid len , capability len =%d not matching with total len=%d (bytes) , format: xxxxxxxx)\n", (uint32_t)strlen(in_cap_info),len);
return -1;
}
cap_info = malloc((len - VENDORIE_OUI_LEN) *2 + 1);
strlcpy(cap_info, in_cap_info, (((len - VENDORIE_OUI_LEN) * 2) + 1));
if (char2hex(cap_info) != 0) {
printf("Invalid capability Hex String , Correct Len & Cap_Info field \n");
return -1;
}
}
switch (cmdtype) {
case IEEE80211_WLANCONFIG_VENDOR_IE_REMOVE:
vie->ie.id = IEEE80211_ELEMID_VENDOR;
memcpy(vie->ie.oui, oui,VENDORIE_OUI_LEN);
memcpy(vie->ie.cap_info, cap_info, (len - VENDORIE_OUI_LEN));
vie->ie.len = len;
vie->tot_len = len + 12;
break;
}
// 上面的vie 即ie_buf 填充完毕,发送到底层驱动, 底层驱动会调用wlan_cfg80211_set_wificonfiguration函数处理,下面分析该函数
ie_len = send_command(sock_ctx, ifname, &ie_buf, vie->tot_len,NULL, QCA_NL80211_VENDOR_SUBCMD_VENDORIE, IEEE80211_IOCTL_CONFIG_GENERIC);
// 日志
if (cmdtype == IEEE80211_WLANCONFIG_VENDOR_IE_REMOVE)
{
printf("\n----------------------------------------\n");
printf("Removing following vendor IEs matching OUI type and subtype\n");
printf("Vendor IE info Ioctl CMD id : %d \n",cmdtype);
printf("ID : %02x\n", vie->ie.id);
printf("OUI : %02x%02x%02x\n", vie->ie.oui[0],vie->ie.oui[1],vie->ie.oui[2]);
//这里也可以看出,cap_info[0]==0xff 或者其他的 subtypes类型值
// 故而pcap_data <eg:xx> 中的 xx 为0xff 或者其他的 单字节的 subtypes
if(vie->ie.cap_info[0] == 0xff)
{
printf("Subtype : all subtypes");
}
else
{
printf("Subtype : %02x",vie->ie.cap_info[0]);
}
printf("\n----------------------------------------\n");
}
}
static int wlan_cfg80211_set_wificonfiguration(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
#if ATH_SUPPORT_DYNAMIC_VENDOR_IE
switch(generic_params.command) {
case QCA_NL80211_VENDOR_SUBCMD_VENDORIE:
return_value = wlan_cfg80211_vendorie_params(wiphy, wdev, generic_params.data, generic_params.data_len);
{
// 下面分析该函数wlan_cfg80211_vendorie_params
}
break;
}
#endif
}
int wlan_cfg80211_vendorie_params(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
ret = ieee80211_ucfg_vendorie(vap, vie); // 下面分析该函数
}
ieee80211_ucfg_vendorie(wlan_if_t vap, struct ieee80211_wlanconfig_vendorie *vie)
{
int error;
switch (vie->cmdtype) {
case IEEE80211_WLANCONFIG_VENDOR_IE_ADD:
error = wlan_set_vendorie(vap, IEEE80211_VENDOR_IE_PARAM_ADD, vie); //
break;
case IEEE80211_WLANCONFIG_VENDOR_IE_UPDATE:
error = wlan_set_vendorie(vap, IEEE80211_VENDOR_IE_PARAM_UPDATE, vie);
break;
case IEEE80211_WLANCONFIG_VENDOR_IE_REMOVE:
error = wlan_set_vendorie(vap, IEEE80211_VENDOR_IE_PARAM_REMOVE, vie);//下面分析该函数
break;
default:
error = -ENXIO;
}
return error;
}
int wlan_set_vendorie(wlan_if_t vaphandle, enum ieee80211_vendor_ie_param param, void *vendor_ie)
{
switch(param) {
case IEEE80211_VENDOR_IE_PARAM_REMOVE:
if(vap->vie_handle != NULL) {
if (ieee80211vap_get_opmode(vap) == IEEE80211_M_HOSTAP)
{
wlan_mlme_app_ie_delete(vap->vie_handle, IEEE80211_FRAME_TYPE_BEACON, (u_int8_t*) &vie->ie);
wlan_mlme_app_ie_delete(vap->vie_handle, IEEE80211_FRAME_TYPE_ASSOCRESP, (u_int8_t*) &vie->ie);
wlan_mlme_app_ie_delete(vap->vie_handle, IEEE80211_FRAME_TYPE_PROBERESP, (u_int8_t*) &vie->ie);
}
if (ieee80211vap_get_opmode(vap) == IEEE80211_M_STA)
{
wlan_mlme_app_ie_delete(vap->vie_handle, IEEE80211_FRAME_TYPE_ASSOCREQ, (u_int8_t*) &vie->ie);
wlan_mlme_app_ie_delete(vap->vie_handle, IEEE80211_FRAME_TYPE_PROBEREQ, (u_int8_t*) &vie->ie);
}
} else {
qdf_nofl_info("Vendor IE is NUll , please add using wlanconfig command \n");
}
break;
}
}