本文为《深入理解Android Wi-Fi、NFC和GPS卷》读书笔记,Android源码为Android 5.1
Linux平台上目前常用的专门针对无线网络设备编程的API有两套
最早的一套API由HP公司员工 Jean Tourrilhes于1997年开发,全称为 Linux Wireless Extensions。一般缩写为 wex 或 wext。 这套API使得用户空间的程序能通过 ioctl 函数来控制无线网卡驱动。//该结构体专门用于往socket句柄传递 ioctrl 控制参数
struct iwreq
{
union
{
char ifrn_name[IFNAMSIZ]; /* if name, e.g. "eth0" 用于指定要操作的网卡设备名,如 wlan0*/
} ifr_ifrn;
/* Data part (defined just above) */
union iwreq_data u; //用于存储具体的参数信息
};
union iwreq_data
{
/* Config - generic */
char name[IFNAMSIZ];
/* Name : used to verify the presence of wireless extensions.
* Name of the protocol/provider... */
struct iw_point essid; /* Extended network name */
struct iw_param nwid; /* network id (or domain - the cell) */
struct iw_freq freq; /* frequency or channel :
* 0-1000 = channel
* > 1000 = frequency in Hz */
struct iw_param sens; /* signal level threshold */
struct iw_param bitrate; /* default bit rate */
struct iw_param txpower; /* default transmit power */
struct iw_param rts; /* RTS threshold threshold */
struct iw_param frag; /* Fragmentation threshold */
__u32 mode; /* Operation mode */
struct iw_param retry; /* Retry limits & lifetime */
struct iw_point encoding; /* Encoding stuff : tokens */
struct iw_param power; /* PM duration/timeout */
struct iw_quality qual; /* Quality part of statistics */
struct sockaddr ap_addr; /* Access point address */
struct sockaddr addr; /* Destination address (hw/mac) */
struct iw_param param; /* Other small parameters */
struct iw_point data; /* Other large parameters */
};
当参数信息的长度超过16字节时,就只能通过 iw_point 指向另外一块内存区域,而参数就存储在那个区域中。这就是我们常用的指针方式。
struct iw_point
{
void __user *pointer; /* Pointer to the data (in user space) */
__u16 length; /* number of fields or size in bytes */
__u16 flags; /* Optional params */
};
当参数信息不超过16字节时,可以把信息存储在 iw_param 中。
struct iw_param
{
__s32 value; /* The value of the parameter itself */
__u8 fixed; /* Hardware should not use auto select */
__u8 disabled; /* Disable the feature */
__u16 flags; /* Various specifc flags (if any) */
};
用于存储频率或信道值。
struct iw_freq
{
__s32 m; /* Mantissa */
__s16 e; /* Exponent */
__u8 i; /* List index (when in range struct) 该值表示此频率对象在 channel_list 数组中的索引 */
__u8 flags; /* Flags (fixed/auto) 固定/自动 */
};
当参数字节超过16的时候, wext 还定义了和功能相关的参数类型:
struct iw_scan_req
{
__u8 scan_type; /* IW_SCAN_TYPE_{ACTIVE,PASSIVE} 主动/被动扫描*/
__u8 essid_len;
__u8 num_channels; /* num entries in channel_list; 信道个数
* 0 = scan all allowed channels 0 扫描所有可允许的信道 */
__u8 flags; /* reserved as padding; use zero, this may
* be used in the future for adding flags
* to request different scan behavior */
struct sockaddr bssid; /* ff:ff:ff:ff:ff:ff for broadcast BSSID or
* individual address of a specific BSS
* bssid用于指明 BSS的地址。 如果全为 FF 则为广播 BSSID,即 wildcard bssid */
/*
* Use this ESSID if IW_SCAN_THIS_ESSID flag is used instead of using
* the current ESSID. This allows scan requests for specific ESSID
* without having to change the current ESSID and potentially breaking
* the current association.
*/
__u8 essid[IW_ESSID_MAX_SIZE];
/*
* Optional parameters for changing the default scanning behavior.
* These are based on the MLME-SCAN.request from IEEE Std 802.11.
* TU is 1.024 ms. If these are set to 0, driver is expected to use
* reasonable default values. min_channel_time defines the time that
* will be used to wait for the first reply on each channel. If no
* replies are received, next channel will be scanned after this. If
* replies are received, total time waited on the channel is defined by
* max_channel_time.
*/
__u32 min_channel_time; /* in TU 表示扫描过程中在每个信道等待到第一个回复的时间。如果在此时间内没有等到回复,跳到下一个信道等待。如果等到一个回复,则一共在该信道等待的最大时间为 max_channel_time 。所有时间单位均为TU(Time Units),即1024ms。*/
__u32 max_channel_time; /* in TU */
struct iw_freq channel_list[IW_MAX_FREQUENCIES];//IW_MAX_FREQUENCIES = 32
};
wext API使用实例
int wpa_driver_wext_scan(void *priv, struct wpa_driver_scan_params *params)
{
struct wpa_driver_wext_data *drv = priv;
struct iwreq iwr; //定义一个 iwreq 对象
int ret = 0, timeout;
struct iw_scan_req req; //定义一个 iw_scan_req 对象
const u8 *ssid = params->ssids[0].ssid;
size_t ssid_len = params->ssids[0].ssid_len;
if (ssid_len > IW_ESSID_MAX_SIZE) {
wpa_printf(MSG_DEBUG, "%s: too long SSID (%lu)",
__FUNCTION__, (unsigned long) ssid_len);
return -1;
}
os_memset(&iwr, 0, sizeof(iwr));
//为 iwr 的 ifr_name 传递需操作的网卡设备名
os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
if (ssid && ssid_len) {
os_memset(&req, 0, sizeof(req));
//设置 iw_scan_req 的信息
req.essid_len = ssid_len;
req.bssid.sa_family = ARPHRD_ETHER;
//设置 bssid 的 MAC 地址全为 0XFF, 代表这是一个 wildcard BSSID搜索
os_memset(req.bssid.sa_data, 0xff, ETH_ALEN);
os_memcpy(req.essid, ssid, ssid_len);
//通过 data 域指向这个 iw_scan_req 对象
iwr.u.data.pointer = (caddr_t) &req;
iwr.u.data.length = sizeof(req);
//IW_SCAN_THIS_ESSID 表示只扫描指定ESSID的无线网络
iwr.u.data.flags = IW_SCAN_THIS_ESSID;
}
//ioctl_sock指向一个 socket 句柄
//SIOCSIWSCAN 用于通知驱动进行无线网络扫描
if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) {
perror("ioctl[SIOCSIWSCAN]");
ret = -1;
}
/* Not all drivers generate "scan completed" wireless event, so try to
* read results after a timeout. */
timeout = 10;
if (drv->scan_complete_events) {
/*
* The driver seems to deliver SIOCGIWSCAN events to notify
* when scan is complete, so use longer timeout to avoid race
* conditions with scanning and following association request.
*/
timeout = 30;
}
wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d "
"seconds", ret, timeout);
eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx);
eloop_register_timeout(timeout, 0, wpa_driver_wext_scan_timeout, drv,
drv->ctx);
return ret;
}
netlink 编程
struct sockaddr_nl {
//nl_family 取值必须为 AF_NETLINK 或 PF_NETLINK
__kernel_sa_family_t nl_family;
unsigned short nl_pad; //必须为0,无用
__u32 nl_pid; //看起来是存储进程pid的,但实际上它只是用于标示一个 netlink socket。所以用户空间只要保证进程内该值的唯一性即可。如果该值为0,表示通信的目标是 kernel。
//每一个 netlink 协议都支持最多32个多播组,加入多播组的成员都能接收到对应的多播消息。
//采用多播的方式能减少消息发送/接收的次数
//nl_groups 为 0,表示只处理单播消息
__u32 nl_groups;
};
nl80211 的核心就是通过 netlink 机制向 Kernel 中的无线网卡驱动发送特定的消息:
static int wpa_driver_nl80211_scan(struct i802_bss *bss,
struct wpa_driver_scan_params *params)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
int ret = -1, timeout;
struct nl_msg *msg = NULL;
wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: scan request");
drv->scan_for_auth = 0;
//创建 nl80211 消息,其中 NL80211_CMD_TRIGGER_SCAN 是 nl80211 定义的命令,用于触发网络扫描
msg = nl80211_scan_common(drv, NL80211_CMD_TRIGGER_SCAN, params,
bss->wdev_id_set ? &bss->wdev_id : NULL);
if (!msg)
return -1;
if (params->p2p_probe) {
struct nlattr *rates;
wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates");
rates = nla_nest_start(msg, NL80211_ATTR_SCAN_SUPP_RATES);
if (rates == NULL)
goto nla_put_failure;
/*
* Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates
* by masking out everything else apart from the OFDM rates 6,
* 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS rates. All 5 GHz
* rates are left enabled.
*/
NLA_PUT(msg, NL80211_BAND_2GHZ, 8,
"\x0c\x12\x18\x24\x30\x48\x60\x6c");
nla_nest_end(msg, rates);
NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE);
}
//发送 netlink 消息
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
msg = NULL;
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d "
"(%s)", ret, strerror(-ret));
if (drv->hostapd && is_ap_interface(drv->nlmode)) {
enum nl80211_iftype old_mode = drv->nlmode;
/*
* mac80211 does not allow scan requests in AP mode, so
* try to do this in station mode.
*/
if (wpa_driver_nl80211_set_mode(
bss, NL80211_IFTYPE_STATION))
goto nla_put_failure;
if (wpa_driver_nl80211_scan(bss, params)) {
wpa_driver_nl80211_set_mode(bss, drv->nlmode);
goto nla_put_failure;
}
/* Restore AP mode when processing scan results */
drv->ap_scan_as_station = old_mode;
ret = 0;
} else
goto nla_put_failure;
}
drv->scan_state = SCAN_REQUESTED;
/* Not all drivers generate "scan completed" wireless event, so try to
* read results after a timeout. */
timeout = 10;
if (drv->scan_complete_events) {
/*
* The driver seems to deliver events to notify when scan is
* complete, so use longer timeout to avoid race conditions
* with scanning and following association request.
*/
timeout = 30;
}
wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d "
"seconds", ret, timeout);
eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout,
drv, drv->ctx);
nla_put_failure:
nlmsg_free(msg);
return ret;
}
上面代码中的nl80211_scan_common
static struct nl_msg *
nl80211_scan_common(struct wpa_driver_nl80211_data *drv, u8 cmd,
struct wpa_driver_scan_params *params, u64 *wdev_id)
{
struct nl_msg *msg;
size_t i;
u32 scan_flags = 0;
//分配一个 nl_msg 对象
msg = nlmsg_alloc();
if (!msg)
return NULL;
//nl80211_cmd 函数填充 nl_msg 信息
nl80211_cmd(drv, msg, 0, cmd);
if (!wdev_id)
//NL80211_ATTR_IFINDEX 代表此次操作所指定的网络设备编号。
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
else
NLA_PUT_U64(msg, NL80211_ATTR_WDEV, *wdev_id);
if (params->num_ssids) {
struct nlattr *ssids;
ssids = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
if (ssids == NULL)
goto fail;
for (i = 0; i < params->num_ssids; i++) {
wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID",
params->ssids[i].ssid,
params->ssids[i].ssid_len);
if (nla_put(msg, i + 1, params->ssids[i].ssid_len,
params->ssids[i].ssid) < 0)
goto fail;
}
nla_nest_end(msg, ssids);
}
if (params->extra_ies) {
wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs",
params->extra_ies, params->extra_ies_len);
if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len,
params->extra_ies) < 0)
goto fail;
}
if (params->freqs) {
struct nlattr *freqs;
freqs = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
if (freqs == NULL)
goto fail;
for (i = 0; params->freqs[i]; i++) {
wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u "
"MHz", params->freqs[i]);
if (nla_put_u32(msg, i + 1, params->freqs[i]) < 0)
goto fail;
}
nla_nest_end(msg, freqs);
}
os_free(drv->filter_ssids);
drv->filter_ssids = params->filter_ssids;
params->filter_ssids = NULL;
drv->num_filter_ssids = params->num_filter_ssids;
if (params->only_new_results) {
wpa_printf(MSG_DEBUG, "nl80211: Add NL80211_SCAN_FLAG_FLUSH");
scan_flags |= NL80211_SCAN_FLAG_FLUSH;
}
if (params->low_priority && drv->have_low_prio_scan) {
wpa_printf(MSG_DEBUG,
"nl80211: Add NL80211_SCAN_FLAG_LOW_PRIORITY");
scan_flags |= NL80211_SCAN_FLAG_LOW_PRIORITY;
}
if (scan_flags)
NLA_PUT_U32(msg, NL80211_ATTR_SCAN_FLAGS, scan_flags);
return msg;
fail:
nla_put_failure:
nlmsg_free(msg);
return NULL;
}
nl80211_copy.h定义了 802.11 相关的命令:
enum nl80211_commands {
/* don't change the order or add anything between, this is ABI! */
NL80211_CMD_UNSPEC,
NL80211_CMD_GET_WIPHY, /* can dump */
NL80211_CMD_SET_WIPHY,
NL80211_CMD_NEW_WIPHY,
NL80211_CMD_DEL_WIPHY,
NL80211_CMD_GET_INTERFACE, /* can dump */
NL80211_CMD_SET_INTERFACE,
NL80211_CMD_NEW_INTERFACE,
NL80211_CMD_DEL_INTERFACE,
NL80211_CMD_GET_KEY,
NL80211_CMD_SET_KEY,
NL80211_CMD_NEW_KEY,
NL80211_CMD_DEL_KEY,
NL80211_CMD_GET_BEACON,
NL80211_CMD_SET_BEACON,
NL80211_CMD_START_AP,
NL80211_CMD_NEW_BEACON = NL80211_CMD_START_AP,
NL80211_CMD_STOP_AP,
NL80211_CMD_DEL_BEACON = NL80211_CMD_STOP_AP,
NL80211_CMD_GET_STATION,
NL80211_CMD_SET_STATION,
NL80211_CMD_NEW_STATION,
NL80211_CMD_DEL_STATION,
NL80211_CMD_GET_MPATH,
NL80211_CMD_SET_MPATH,
NL80211_CMD_NEW_MPATH,
NL80211_CMD_DEL_MPATH,
NL80211_CMD_SET_BSS,
NL80211_CMD_SET_REG,
NL80211_CMD_REQ_SET_REG,
NL80211_CMD_GET_MESH_CONFIG,
NL80211_CMD_SET_MESH_CONFIG,
NL80211_CMD_SET_MGMT_EXTRA_IE /* reserved; not used */,
NL80211_CMD_GET_REG,
NL80211_CMD_GET_SCAN,
NL80211_CMD_TRIGGER_SCAN,
NL80211_CMD_NEW_SCAN_RESULTS,
NL80211_CMD_SCAN_ABORTED,
NL80211_CMD_REG_CHANGE,
NL80211_CMD_AUTHENTICATE,
NL80211_CMD_ASSOCIATE,
NL80211_CMD_DEAUTHENTICATE,
NL80211_CMD_DISASSOCIATE,
NL80211_CMD_MICHAEL_MIC_FAILURE,
NL80211_CMD_REG_BEACON_HINT,
NL80211_CMD_JOIN_IBSS,
NL80211_CMD_LEAVE_IBSS,
NL80211_CMD_TESTMODE,
NL80211_CMD_CONNECT,
NL80211_CMD_ROAM,
NL80211_CMD_DISCONNECT,
NL80211_CMD_SET_WIPHY_NETNS,
NL80211_CMD_GET_SURVEY,
NL80211_CMD_NEW_SURVEY_RESULTS,
NL80211_CMD_SET_PMKSA,
NL80211_CMD_DEL_PMKSA,
NL80211_CMD_FLUSH_PMKSA,
NL80211_CMD_REMAIN_ON_CHANNEL,
NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
NL80211_CMD_SET_TX_BITRATE_MASK,
NL80211_CMD_REGISTER_FRAME,
NL80211_CMD_REGISTER_ACTION = NL80211_CMD_REGISTER_FRAME,
NL80211_CMD_FRAME,
NL80211_CMD_ACTION = NL80211_CMD_FRAME,
NL80211_CMD_FRAME_TX_STATUS,
NL80211_CMD_ACTION_TX_STATUS = NL80211_CMD_FRAME_TX_STATUS,
NL80211_CMD_SET_POWER_SAVE,
NL80211_CMD_GET_POWER_SAVE,
NL80211_CMD_SET_CQM,
NL80211_CMD_NOTIFY_CQM,
NL80211_CMD_SET_CHANNEL,
NL80211_CMD_SET_WDS_PEER,
NL80211_CMD_FRAME_WAIT_CANCEL,
NL80211_CMD_JOIN_MESH,
NL80211_CMD_LEAVE_MESH,
NL80211_CMD_UNPROT_DEAUTHENTICATE,
NL80211_CMD_UNPROT_DISASSOCIATE,
NL80211_CMD_NEW_PEER_CANDIDATE,
NL80211_CMD_GET_WOWLAN,
NL80211_CMD_SET_WOWLAN,
NL80211_CMD_START_SCHED_SCAN,
NL80211_CMD_STOP_SCHED_SCAN,
NL80211_CMD_SCHED_SCAN_RESULTS,
NL80211_CMD_SCHED_SCAN_STOPPED,
NL80211_CMD_SET_REKEY_OFFLOAD,
NL80211_CMD_PMKSA_CANDIDATE,
NL80211_CMD_TDLS_OPER,
NL80211_CMD_TDLS_MGMT,
NL80211_CMD_UNEXPECTED_FRAME,
NL80211_CMD_PROBE_CLIENT,
NL80211_CMD_REGISTER_BEACONS,
NL80211_CMD_UNEXPECTED_4ADDR_FRAME,
NL80211_CMD_SET_NOACK_MAP,
NL80211_CMD_CH_SWITCH_NOTIFY,
NL80211_CMD_START_P2P_DEVICE,
NL80211_CMD_STOP_P2P_DEVICE,
NL80211_CMD_CONN_FAILED,
NL80211_CMD_SET_MCAST_RATE,
NL80211_CMD_SET_MAC_ACL,
NL80211_CMD_RADAR_DETECT,
NL80211_CMD_GET_PROTOCOL_FEATURES,
NL80211_CMD_UPDATE_FT_IES,
NL80211_CMD_FT_EVENT,
NL80211_CMD_CRIT_PROTOCOL_START,
NL80211_CMD_CRIT_PROTOCOL_STOP,
NL80211_CMD_GET_COALESCE,
NL80211_CMD_SET_COALESCE,
NL80211_CMD_CHANNEL_SWITCH,
NL80211_CMD_VENDOR,
NL80211_CMD_SET_QOS_MAP,
NL80211_CMD_ADD_TX_TS,
NL80211_CMD_DEL_TX_TS,
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
__NL80211_CMD_AFTER_LAST,
NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1
};
属性的取值:
enum nl80211_attrs {
/* don't change the order or add anything inbetween, this is ABI! */
NL80211_ATTR_UNSPEC,
NL80211_ATTR_WIPHY,
NL80211_ATTR_WIPHY_NAME,
NL80211_ATTR_IFINDEX,
NL80211_ATTR_IFNAME,
NL80211_ATTR_IFTYPE,
NL80211_ATTR_MAC,
NL80211_ATTR_KEY_DATA,
NL80211_ATTR_KEY_IDX,
NL80211_ATTR_KEY_CIPHER,
NL80211_ATTR_KEY_SEQ,
NL80211_ATTR_KEY_DEFAULT,
NL80211_ATTR_BEACON_INTERVAL,
NL80211_ATTR_DTIM_PERIOD,
NL80211_ATTR_BEACON_HEAD,
NL80211_ATTR_BEACON_TAIL,
NL80211_ATTR_STA_AID,
NL80211_ATTR_STA_FLAGS,
NL80211_ATTR_STA_LISTEN_INTERVAL,
NL80211_ATTR_STA_SUPPORTED_RATES,
NL80211_ATTR_STA_VLAN,
NL80211_ATTR_STA_INFO,
NL80211_ATTR_WIPHY_BANDS,
NL80211_ATTR_MNTR_FLAGS,
NL80211_ATTR_MESH_ID,
NL80211_ATTR_STA_PLINK_ACTION,
NL80211_ATTR_MPATH_NEXT_HOP,
NL80211_ATTR_MPATH_INFO,
NL80211_ATTR_BSS_CTS_PROT,
NL80211_ATTR_BSS_SHORT_PREAMBLE,
NL80211_ATTR_BSS_SHORT_SLOT_TIME,
NL80211_ATTR_HT_CAPABILITY,
NL80211_ATTR_SUPPORTED_IFTYPES,
NL80211_ATTR_REG_ALPHA2,
NL80211_ATTR_REG_RULES,
NL80211_ATTR_MESH_PARAMS,
NL80211_ATTR_BSS_BASIC_RATES,
NL80211_ATTR_WIPHY_TXQ_PARAMS,
NL80211_ATTR_WIPHY_FREQ,
NL80211_ATTR_WIPHY_CHANNEL_TYPE,
NL80211_ATTR_KEY_DEFAULT_MGMT,
NL80211_ATTR_MGMT_SUBTYPE,
NL80211_ATTR_IE,
NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
NL80211_ATTR_SCAN_FREQUENCIES,
NL80211_ATTR_SCAN_SSIDS,
NL80211_ATTR_GENERATION, /* replaces old SCAN_GENERATION */
NL80211_ATTR_BSS,
NL80211_ATTR_REG_INITIATOR,
NL80211_ATTR_REG_TYPE,
NL80211_ATTR_SUPPORTED_COMMANDS,
NL80211_ATTR_FRAME,
NL80211_ATTR_SSID,
NL80211_ATTR_AUTH_TYPE,
NL80211_ATTR_REASON_CODE,
NL80211_ATTR_KEY_TYPE,
NL80211_ATTR_MAX_SCAN_IE_LEN,
NL80211_ATTR_CIPHER_SUITES,
NL80211_ATTR_FREQ_BEFORE,
NL80211_ATTR_FREQ_AFTER,
NL80211_ATTR_FREQ_FIXED,
NL80211_ATTR_WIPHY_RETRY_SHORT,
NL80211_ATTR_WIPHY_RETRY_LONG,
NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
NL80211_ATTR_WIPHY_RTS_THRESHOLD,
NL80211_ATTR_TIMED_OUT,
NL80211_ATTR_USE_MFP,
NL80211_ATTR_STA_FLAGS2,
NL80211_ATTR_CONTROL_PORT,
NL80211_ATTR_TESTDATA,
NL80211_ATTR_PRIVACY,
NL80211_ATTR_DISCONNECTED_BY_AP,
NL80211_ATTR_STATUS_CODE,
NL80211_ATTR_CIPHER_SUITES_PAIRWISE,
NL80211_ATTR_CIPHER_SUITE_GROUP,
NL80211_ATTR_WPA_VERSIONS,
NL80211_ATTR_AKM_SUITES,
NL80211_ATTR_REQ_IE,
NL80211_ATTR_RESP_IE,
NL80211_ATTR_PREV_BSSID,
NL80211_ATTR_KEY,
NL80211_ATTR_KEYS,
NL80211_ATTR_PID,
NL80211_ATTR_4ADDR,
NL80211_ATTR_SURVEY_INFO,
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1
};