From cc9a2575cabd2adb975df3677c6df5829f4279ad Mon Sep 17 00:00:00 2001 From: "Kanchanapally, Vidyullatha" Date: Tue, 12 Apr 2016 13:31:42 +0530 Subject: [PATCH] nl80211: Use extended capabilities per interface type This adds the necessary changes to support extraction and use of the extended capabilities specified per interface type (a recent cfg80211/nl80211 extension). If that information is available, per-interface values will be used to override the global per-radio value. Signed-off-by: Jouni Malinen --- hostapd/main.c | 9 ++++ src/ap/ap_drv_ops.c | 14 ++++++ src/ap/ap_drv_ops.h | 2 + src/drivers/driver.h | 13 ++++++ src/drivers/driver_nl80211.c | 41 +++++++++++++++- src/drivers/driver_nl80211.h | 9 ++++ src/drivers/driver_nl80211_capa.c | 77 +++++++++++++++++++++++++++++++ wpa_supplicant/ap.c | 5 ++ wpa_supplicant/driver_i.h | 11 +++++ wpa_supplicant/scan.c | 7 +++ wpa_supplicant/sme.c | 5 ++ wpa_supplicant/wpa_supplicant.c | 5 ++ 12 files changed, 197 insertions(+), 1 deletion(-) diff --git a/hostapd/main.c b/hostapd/main.c index 1d9e63e3b..5f3f83bf9 100644 --- a/hostapd/main.c +++ b/hostapd/main.c @@ -217,11 +217,20 @@ static int hostapd_driver_init(struct hostapd_iface *iface) iface->drv_flags = capa.flags; iface->smps_modes = capa.smps_modes; iface->probe_resp_offloads = capa.probe_resp_offloads; + /* + * Use default extended capa values from per-radio information + */ iface->extended_capa = capa.extended_capa; iface->extended_capa_mask = capa.extended_capa_mask; iface->extended_capa_len = capa.extended_capa_len; iface->drv_max_acl_mac_addrs = capa.max_acl_mac_addrs; + /* + * Override extended capa with per-interface type (AP), if + * available from the driver. + */ + hostapd_get_ext_capa(iface); + triggs = wpa_get_wowlan_triggers(conf->wowlan_triggers, &capa); if (triggs && hapd->driver->set_wowlan) { if (hapd->driver->set_wowlan(hapd->drv_priv, triggs)) diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c index dca6b8b01..e4fd0c51d 100644 --- a/src/ap/ap_drv_ops.c +++ b/src/ap/ap_drv_ops.c @@ -750,6 +750,20 @@ static void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd, } +void hostapd_get_ext_capa(struct hostapd_iface *iface) +{ + struct hostapd_data *hapd = iface->bss[0]; + + if (!hapd->driver || !hapd->driver->get_ext_capab) + return; + + hapd->driver->get_ext_capab(hapd->drv_priv, WPA_IF_AP_BSS, + &iface->extended_capa, + &iface->extended_capa_mask, + &iface->extended_capa_len); +} + + int hostapd_drv_do_acs(struct hostapd_data *hapd) { struct drv_acs_params params; diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h index 6ea1dab0d..73ac31848 100644 --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -124,6 +124,8 @@ int hostapd_drv_wnm_oper(struct hostapd_data *hapd, int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set, u8 qos_map_set_len); +void hostapd_get_ext_capa(struct hostapd_iface *iface); + static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd, int enabled) { diff --git a/src/drivers/driver.h b/src/drivers/driver.h index b709b0e66..edb6978da 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -3560,6 +3560,19 @@ struct wpa_driver_ops { * Returns: 0 on success or -1 on failure */ int (*configure_data_frame_filters)(void *priv, u32 filter_flags); + + /** + * get_ext_capab - Get extended capabilities for the specified interface + * @priv: Private driver interface data + * @type: Interface type for which to get extended capabilities + * @ext_capab: Extended capabilities fetched + * @ext_capab_mask: Extended capabilities mask + * @ext_capab_len: Length of the extended capabilities + * Returns: 0 on success or -1 on failure + */ + int (*get_ext_capab)(void *priv, enum wpa_driver_if_type type, + const u8 **ext_capab, const u8 **ext_capab_mask, + unsigned int *ext_capab_len); }; diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 12a417b42..d3367eed1 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -2412,6 +2412,7 @@ static int wpa_driver_nl80211_del_beacon(struct wpa_driver_nl80211_data *drv) static void wpa_driver_nl80211_deinit(struct i802_bss *bss) { struct wpa_driver_nl80211_data *drv = bss->drv; + unsigned int i; wpa_printf(MSG_INFO, "nl80211: deinit ifname=%s disabled_11b_rates=%d", bss->ifname, drv->disabled_11b_rates); @@ -2508,6 +2509,10 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss) os_free(drv->extended_capa); os_free(drv->extended_capa_mask); + for (i = 0; i < drv->num_iface_ext_capa; i++) { + os_free(drv->iface_ext_capa[i].ext_capa); + os_free(drv->iface_ext_capa[i].ext_capa_mask); + } os_free(drv->first_bss); os_free(drv); } @@ -4162,7 +4167,7 @@ void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, int ifidx) } -static const char * nl80211_iftype_str(enum nl80211_iftype mode) +const char * nl80211_iftype_str(enum nl80211_iftype mode) { switch (mode) { case NL80211_IFTYPE_ADHOC: @@ -9202,6 +9207,39 @@ static int nl80211_configure_data_frame_filters(void *priv, u32 filter_flags) } +static int nl80211_get_ext_capab(void *priv, enum wpa_driver_if_type type, + const u8 **ext_capa, const u8 **ext_capa_mask, + unsigned int *ext_capa_len) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + enum nl80211_iftype nlmode; + unsigned int i; + + if (!ext_capa || !ext_capa_mask || !ext_capa_len) + return -1; + + nlmode = wpa_driver_nl80211_if_type(type); + + /* By default, use the per-radio values */ + *ext_capa = drv->extended_capa; + *ext_capa_mask = drv->extended_capa_mask; + *ext_capa_len = drv->extended_capa_len; + + /* Replace the default value if a per-interface type value exists */ + for (i = 0; i < drv->num_iface_ext_capa; i++) { + if (nlmode == drv->iface_ext_capa[i].iftype) { + *ext_capa = drv->iface_ext_capa[i].ext_capa; + *ext_capa_mask = drv->iface_ext_capa[i].ext_capa_mask; + *ext_capa_len = drv->iface_ext_capa[i].ext_capa_len; + break; + } + } + + return 0; +} + + const struct wpa_driver_ops wpa_driver_nl80211_ops = { .name = "nl80211", .desc = "Linux nl80211/cfg80211", @@ -9319,4 +9357,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .set_prob_oper_freq = nl80211_set_prob_oper_freq, #endif /* CONFIG_DRIVER_NL80211_QCA */ .configure_data_frame_filters = nl80211_configure_data_frame_filters, + .get_ext_capab = nl80211_get_ext_capab, }; diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h index b0d2b6d74..283dfd99b 100644 --- a/src/drivers/driver_nl80211.h +++ b/src/drivers/driver_nl80211.h @@ -96,6 +96,13 @@ struct wpa_driver_nl80211_data { struct wpa_driver_capa capa; u8 *extended_capa, *extended_capa_mask; unsigned int extended_capa_len; + struct drv_nl80211_ext_capa { + enum nl80211_iftype iftype; + u8 *ext_capa, *ext_capa_mask; + unsigned int ext_capa_len; + } iface_ext_capa[NL80211_IFTYPE_MAX]; + unsigned int num_iface_ext_capa; + int has_capability; int operstate; @@ -251,6 +258,8 @@ nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags); int process_global_event(struct nl_msg *msg, void *arg); int process_bss_event(struct nl_msg *msg, void *arg); +const char * nl80211_iftype_str(enum nl80211_iftype mode); + #ifdef ANDROID int android_nl_socket_set_nonblocking(struct nl_handle *handle); int android_pno_start(struct i802_bss *bss, diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c index fc0f95a75..e1b4b6455 100644 --- a/src/drivers/driver_nl80211_capa.c +++ b/src/drivers/driver_nl80211_capa.c @@ -486,6 +486,74 @@ static void wiphy_info_wowlan_triggers(struct wpa_driver_capa *capa, } +static void wiphy_info_extended_capab(struct wpa_driver_nl80211_data *drv, + struct nlattr *tb) +{ + int rem = 0, i; + struct nlattr *tb1[NL80211_ATTR_MAX + 1], *attr; + + if (!tb || drv->num_iface_ext_capa == NL80211_IFTYPE_MAX) + return; + + nla_for_each_nested(attr, tb, rem) { + unsigned int len; + struct drv_nl80211_ext_capa *capa; + + nla_parse(tb1, NL80211_ATTR_MAX, nla_data(attr), + nla_len(attr), NULL); + + if (!tb1[NL80211_ATTR_IFTYPE] || + !tb1[NL80211_ATTR_EXT_CAPA] || + !tb1[NL80211_ATTR_EXT_CAPA_MASK]) + continue; + + capa = &drv->iface_ext_capa[drv->num_iface_ext_capa]; + capa->iftype = nla_get_u32(tb1[NL80211_ATTR_IFTYPE]); + wpa_printf(MSG_DEBUG, + "nl80211: Driver-advertised extended capabilities for interface type %s", + nl80211_iftype_str(capa->iftype)); + + len = nla_len(tb1[NL80211_ATTR_EXT_CAPA]); + capa->ext_capa = os_malloc(len); + if (!capa->ext_capa) + goto err; + + os_memcpy(capa->ext_capa, nla_data(tb1[NL80211_ATTR_EXT_CAPA]), + len); + capa->ext_capa_len = len; + wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities", + capa->ext_capa, capa->ext_capa_len); + + len = nla_len(tb1[NL80211_ATTR_EXT_CAPA_MASK]); + capa->ext_capa_mask = os_malloc(len); + if (!capa->ext_capa_mask) + goto err; + + os_memcpy(capa->ext_capa_mask, + nla_data(tb1[NL80211_ATTR_EXT_CAPA_MASK]), len); + wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities mask", + capa->ext_capa_mask, capa->ext_capa_len); + + drv->num_iface_ext_capa++; + if (drv->num_iface_ext_capa == NL80211_IFTYPE_MAX) + break; + } + + return; + +err: + /* Cleanup allocated memory on error */ + for (i = 0; i < NL80211_IFTYPE_MAX; i++) { + os_free(drv->iface_ext_capa[i].ext_capa); + drv->iface_ext_capa[i].ext_capa = NULL; + os_free(drv->iface_ext_capa[i].ext_capa_mask); + drv->iface_ext_capa[i].ext_capa_mask = NULL; + drv->iface_ext_capa[i].ext_capa_len = 0; + } + drv->num_iface_ext_capa = 0; +} + + static int wiphy_info_handler(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; @@ -576,6 +644,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) nla_len(tb[NL80211_ATTR_EXT_CAPA])); drv->extended_capa_len = nla_len(tb[NL80211_ATTR_EXT_CAPA]); + wpa_hexdump(MSG_DEBUG, + "nl80211: Driver-advertised extended capabilities (default)", + drv->extended_capa, drv->extended_capa_len); } drv->extended_capa_mask = os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK])); @@ -583,6 +654,10 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) os_memcpy(drv->extended_capa_mask, nla_data(tb[NL80211_ATTR_EXT_CAPA_MASK]), nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK])); + wpa_hexdump(MSG_DEBUG, + "nl80211: Driver-advertised extended capabilities mask (default)", + drv->extended_capa_mask, + drv->extended_capa_len); } else { os_free(drv->extended_capa); drv->extended_capa = NULL; @@ -590,6 +665,8 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) } } + wiphy_info_extended_capab(drv, tb[NL80211_ATTR_IFTYPE_EXT_CAPA]); + if (tb[NL80211_ATTR_VENDOR_DATA]) { struct nlattr *nl; int rem; diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c index 1ba2ab34d..1e00f35b5 100644 --- a/wpa_supplicant/ap.c +++ b/wpa_supplicant/ap.c @@ -660,6 +660,11 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, if (ieee80211_is_dfs(params.freq.freq)) params.freq.freq = 0; /* set channel after CAC */ + if (params.p2p) + wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_GO); + else + wpa_drv_get_ext_capa(wpa_s, WPA_IF_AP_BSS); + if (wpa_drv_associate(wpa_s, ¶ms) < 0) { wpa_msg(wpa_s, MSG_INFO, "Failed to start AP functionality"); return -1; diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h index 7a213b64a..1d0f96f70 100644 --- a/wpa_supplicant/driver_i.h +++ b/wpa_supplicant/driver_i.h @@ -926,4 +926,15 @@ static inline int wpa_drv_configure_frame_filters(struct wpa_supplicant *wpa_s, filters); } +static inline int wpa_drv_get_ext_capa(struct wpa_supplicant *wpa_s, + enum wpa_driver_if_type type) +{ + if (!wpa_s->driver->get_ext_capab) + return -1; + return wpa_s->driver->get_ext_capab(wpa_s->drv_priv, type, + &wpa_s->extended_capa, + &wpa_s->extended_capa_mask, + &wpa_s->extended_capa_len); +} + #endif /* DRIVER_I_H */ diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index 787d4e653..6ade9afaa 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -436,6 +436,13 @@ static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s) enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO; #endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT) + wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT); + else +#endif /* CONFIG_P2P */ + wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION); + ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab, sizeof(ext_capab)); if (ext_capab_len > 0 && diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index 2fbb2c65d..3a8f5b1aa 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -455,6 +455,11 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_MBO */ + if (params.p2p) + wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT); + else + wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION); + ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab, sizeof(ext_capab)); if (ext_capab_len > 0) { diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index aa785bde5..9acd47229 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -2291,6 +2291,11 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) * element in all cases, it is justifiable to skip it to avoid * interoperability issues. */ + if (ssid->p2p_group) + wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT); + else + wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION); + if (!bss || wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB)) { u8 ext_capab[18]; int ext_capab_len;