From 15badebd473c91e6dfd7853bedf402d594212c14 Mon Sep 17 00:00:00 2001 From: Chet Lanctot Date: Fri, 5 Dec 2014 16:48:23 -0800 Subject: [PATCH] nl80211: Add QCA vendor specific query of device/driver features This commit introduces a QCA vendor command that allows interrogation of the vendor-specific features supported by the device/driver. Currently the only defined feature is the ability to offload key management. Signed-off-by: Jouni Malinen --- src/common/qca-vendor.h | 28 ++++++++++- src/drivers/driver.h | 2 + src/drivers/driver_nl80211.c | 5 +- src/drivers/driver_nl80211.h | 3 +- src/drivers/driver_nl80211_capa.c | 79 ++++++++++++++++++++++++++++-- src/drivers/driver_nl80211_event.c | 3 +- 6 files changed, 108 insertions(+), 12 deletions(-) diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h index 2f1bdcefc..ec1be863d 100644 --- a/src/common/qca-vendor.h +++ b/src/common/qca-vendor.h @@ -53,15 +53,22 @@ enum qca_radiotap_vendor_ids { * * @QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY: Set key operation that can be * used to configure PMK to the driver even when not connected. This can - * be used to request offloading of key management operations. + * be used to request offloading of key management operations. Only used + * if device supports QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD. + * * @QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH: An extended version of * NL80211_CMD_ROAM event with optional attributes including information * from offloaded key management operation. Uses - * enum qca_wlan_vendor_attr_roam_auth attributes. + * enum qca_wlan_vendor_attr_roam_auth attributes. Only used + * if device supports QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD. * * @QCA_NL80211_VENDOR_SUBCMD_DO_ACS: ACS command/event which is used to * invoke the ACS function in device and pass selected channels to * hostapd. + * + * @QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES: Command to get the features + * supported by the driver. enum qca_wlan_vendor_features defines + * the possible features. */ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0, @@ -107,6 +114,7 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_APFIND = 52, /* 53 - reserved for QCA */ QCA_NL80211_VENDOR_SUBCMD_DO_ACS = 54, + QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES = 55, }; @@ -124,6 +132,8 @@ enum qca_wlan_vendor_attr { * by enum qca_roaming_policy. */ QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY = 5, QCA_WLAN_VENDOR_ATTR_MAC_ADDR = 6, + /* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */ + QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS = 7, /* keep last */ QCA_WLAN_VENDOR_ATTR_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1, @@ -170,4 +180,18 @@ enum qca_wlan_vendor_acs_hw_mode { QCA_ACS_MODE_IEEE80211AD, }; +/** + * enum qca_wlan_vendor_features - Vendor device/driver feature flags + * + * @QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD: Device supports key + * management offload, a mechanism where the station's firmware + * does the exchange with the AP to establish the temporal keys + * after roaming, rather than having the user space wpa_supplicant do it. + * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits + */ +enum qca_wlan_vendor_features { + QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD = 0, + NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */ +}; + #endif /* QCA_VENDOR_H */ diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 9eea524c2..54050db16 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -1075,6 +1075,8 @@ struct wpa_driver_capa { #define WPA_DRIVER_FLAGS_MESH 0x0000000100000000ULL /* Driver support ACS offload */ #define WPA_DRIVER_FLAGS_ACS_OFFLOAD 0x0000000200000000ULL +/* Driver supports key management offload */ +#define WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD 0x0000000400000000ULL u64 flags; #define WPA_DRIVER_SMPS_MODE_STATIC 0x00000001 diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index d3728332d..321034785 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -2347,7 +2347,7 @@ static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv, struct nl_msg *msg; int ret; - if (!drv->key_mgmt_set_key_vendor_cmd_avail) + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) return 0; if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || @@ -2397,7 +2397,8 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss, } #endif /* CONFIG_TDLS */ - if (alg == WPA_ALG_PMK && drv->key_mgmt_set_key_vendor_cmd_avail) { + if (alg == WPA_ALG_PMK && + (drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) { wpa_printf(MSG_DEBUG, "%s: calling issue_key_mgmt_set_key", __func__); ret = issue_key_mgmt_set_key(drv, key, key_len); diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h index 00fe6b719..4a364f71b 100644 --- a/src/drivers/driver_nl80211.h +++ b/src/drivers/driver_nl80211.h @@ -143,8 +143,7 @@ struct wpa_driver_nl80211_data { unsigned int have_low_prio_scan:1; unsigned int force_connect_cmd:1; unsigned int addr_changed:1; - unsigned int key_mgmt_set_key_vendor_cmd_avail:1; - unsigned int roam_auth_vendor_event_avail:1; + unsigned int get_features_vendor_cmd_avail:1; u64 remain_on_chan_cookie; u64 send_action_cookie; diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c index 198ef1d32..657f38c49 100644 --- a/src/drivers/driver_nl80211_capa.c +++ b/src/drivers/driver_nl80211_capa.c @@ -534,8 +534,8 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) case QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY: drv->dfs_vendor_cmd_avail = 1; break; - case QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY: - drv->key_mgmt_set_key_vendor_cmd_avail = 1; + case QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES: + drv->get_features_vendor_cmd_avail = 1; break; case QCA_NL80211_VENDOR_SUBCMD_DO_ACS: drv->capa.flags |= WPA_DRIVER_FLAGS_ACS_OFFLOAD; @@ -558,9 +558,6 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) continue; } vinfo = nla_data(nl); - if (vinfo->subcmd == - QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH) - drv->roam_auth_vendor_event_avail = 1; wpa_printf(MSG_DEBUG, "nl80211: Supported vendor event: vendor_id=0x%x subcmd=%u", vinfo->vendor_id, vinfo->subcmd); } @@ -689,6 +686,77 @@ static void qca_nl80211_check_dfs_capa(struct wpa_driver_nl80211_data *drv) } +struct features_info { + u8 *flags; + size_t flags_len; +}; + + +static int features_info_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct features_info *info = arg; + struct nlattr *nl_vend, *attr; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + nl_vend = tb[NL80211_ATTR_VENDOR_DATA]; + if (nl_vend) { + struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1]; + + nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX, + nla_data(nl_vend), nla_len(nl_vend), NULL); + + attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS]; + if (attr) { + info->flags = nla_data(attr); + info->flags_len = nla_len(attr); + } + } + + return NL_SKIP; +} + + +static int check_feature(enum qca_wlan_vendor_features feature, + struct features_info *info) +{ + size_t index = feature / 8; + + return (index < info->flags_len) && + (info->flags[index] & BIT(feature % 8)); +} + + +static void qca_nl80211_get_features(struct wpa_driver_nl80211_data *drv) +{ + struct nl_msg *msg; + struct features_info info; + int ret; + + if (!drv->get_features_vendor_cmd_avail) + return; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES)) { + nlmsg_free(msg); + return; + } + + os_memset(&info, 0, sizeof(info)); + ret = send_and_recv_msgs(drv, msg, features_info_handler, &info); + if (ret || !info.flags) + return; + + if (check_feature(QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD, &info)) + drv->capa.flags |= WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD; +} + + int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) { struct wiphy_info_data info; @@ -766,6 +834,7 @@ int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; qca_nl80211_check_dfs_capa(drv); + qca_nl80211_get_features(drv); return 0; } diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c index 2226e4c86..f4221740c 100644 --- a/src/drivers/driver_nl80211_event.c +++ b/src/drivers/driver_nl80211_event.c @@ -1690,7 +1690,8 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s", cmd, nl80211_command_to_string(cmd), bss->ifname); - if (cmd == NL80211_CMD_ROAM && drv->roam_auth_vendor_event_avail) { + if (cmd == NL80211_CMD_ROAM && + (drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) { /* * Device will use roam+auth vendor event to indicate * roaming, so ignore the regular roam event.