diff --git a/src/common/defs.h b/src/common/defs.h index 2efb98577..e1bbd5092 100644 --- a/src/common/defs.h +++ b/src/common/defs.h @@ -308,6 +308,17 @@ enum wpa_ctrl_req_type { /* Maximum number of EAP methods to store for EAP server user information */ #define EAP_MAX_METHODS 8 +/** + * enum ht_mode - channel width and offset + */ +enum ht_mode { + CHAN_UNDEFINED = 0, + CHAN_NO_HT, + CHAN_HT20, + CHAN_HT40PLUS, + CHAN_HT40MINUS, +}; + enum mesh_plink_state { PLINK_LISTEN = 1, PLINK_OPEN_SENT, diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 3476e8e31..5be123b2c 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -945,6 +945,7 @@ struct wpa_driver_mesh_join_params { const u8 *ies; int ie_len; int freq; + enum ht_mode ht_mode; struct wpa_driver_mesh_bss_params conf; #define WPA_DRIVER_MESH_FLAG_USER_MPM 0x00000001 #define WPA_DRIVER_MESH_FLAG_DRIVER_MPM 0x00000002 diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index cf4e540b1..05d8ff54c 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -8732,12 +8732,38 @@ wpa_driver_nl80211_join_mesh(void *priv, nl80211_cmd(drv, msg, 0, NL80211_CMD_JOIN_MESH); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - /* XXX: need chtype too in case we want HT */ if (params->freq) { wpa_printf(MSG_DEBUG, " * freq=%d", params->freq); NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq); } + if (params->ht_mode) { + unsigned int ht_value; + char *ht_mode = ""; + + switch (params->ht_mode) { + default: + case CHAN_NO_HT: + ht_value = NL80211_CHAN_NO_HT; + ht_mode = "NOHT"; + break; + case CHAN_HT20: + ht_value = NL80211_CHAN_HT20; + ht_mode = "HT20"; + break; + case CHAN_HT40PLUS: + ht_value = NL80211_CHAN_HT40PLUS; + ht_mode = "HT40+"; + break; + case CHAN_HT40MINUS: + ht_value = NL80211_CHAN_HT40MINUS; + ht_mode = "HT40-"; + break; + } + wpa_printf(MSG_DEBUG, " * ht_mode=%s", ht_mode); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, ht_value); + } + if (params->basic_rates) { u8 rates[NL80211_MAX_SUPP_RATES]; u8 rates_len = 0; diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c index 02ea60997..f3acbc111 100644 --- a/wpa_supplicant/ap.c +++ b/wpa_supplicant/ap.c @@ -75,24 +75,10 @@ no_vht: #endif /* CONFIG_IEEE80211N */ -static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid, - struct hostapd_config *conf) +void wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + struct hostapd_config *conf) { - struct hostapd_bss_config *bss = conf->bss[0]; - - conf->driver = wpa_s->driver; - - os_strlcpy(bss->iface, wpa_s->ifname, sizeof(bss->iface)); - - conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency, - &conf->channel); - if (conf->hw_mode == NUM_HOSTAPD_MODES) { - wpa_printf(MSG_ERROR, "Unsupported AP mode frequency: %d MHz", - ssid->frequency); - return -1; - } - /* TODO: enable HT40 if driver supports it; * drop to 11b if driver does not support 11g */ @@ -155,6 +141,28 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, } } #endif /* CONFIG_IEEE80211N */ +} + + +static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + struct hostapd_config *conf) +{ + struct hostapd_bss_config *bss = conf->bss[0]; + + conf->driver = wpa_s->driver; + + os_strlcpy(bss->iface, wpa_s->ifname, sizeof(bss->iface)); + + conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency, + &conf->channel); + if (conf->hw_mode == NUM_HOSTAPD_MODES) { + wpa_printf(MSG_ERROR, "Unsupported AP mode frequency: %d MHz", + ssid->frequency); + return -1; + } + + wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf); #ifdef CONFIG_P2P if (conf->hw_mode == HOSTAPD_MODE_IEEE80211G && diff --git a/wpa_supplicant/ap.h b/wpa_supplicant/ap.h index 8aa5ffa29..4d80c7a7c 100644 --- a/wpa_supplicant/ap.h +++ b/wpa_supplicant/ap.h @@ -75,4 +75,9 @@ int wpas_ap_wps_nfc_report_handover(struct wpa_supplicant *wpa_s, int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id, const struct wpabuf *pw, const u8 *pubkey_hash); +struct hostapd_config; +void wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + struct hostapd_config *conf); + #endif /* AP_H */ diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index 637cdf11c..9e261ba75 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -1588,6 +1588,66 @@ static char * wpa_config_write_psk_list(const struct parse_data *data, #endif /* CONFIG_P2P */ + +#ifdef CONFIG_MESH + +static int wpa_config_parse_mesh_ht_mode(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + int htval = 0; + + if (os_strcmp(value, "NOHT") == 0) + htval = CHAN_NO_HT; + else if (os_strcmp(value, "HT20") == 0) + htval = CHAN_HT20; + else if (os_strcmp(value, "HT40-") == 0) + htval = CHAN_HT40MINUS; + else if (os_strcmp(value, "HT40+") == 0) + htval = CHAN_HT40PLUS; + else { + wpa_printf(MSG_ERROR, + "Line %d: no ht_mode configured.", line); + return -1; + } + + wpa_printf(MSG_MSGDUMP, "mesh_ht_mode: 0x%x", htval); + ssid->mesh_ht_mode = htval; + return 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_mesh_ht_mode(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + char *val; + + switch (ssid->mesh_ht_mode) { + default: + val = NULL; + break; + case CHAN_NO_HT: + val = "NOHT"; + break; + case CHAN_HT20: + val = "HT20"; + break; + case CHAN_HT40MINUS: + val = "HT40-"; + break; + case CHAN_HT40PLUS: + val = "HT40+"; + break; + } + return val ? os_strdup(val) : NULL; +} + +#endif /* NO_CONFIG_WRITE */ + +#endif /* CONFIG_MESH */ + + /* Helper macros for network block parser */ #ifdef OFFSET @@ -1757,6 +1817,9 @@ static const struct parse_data ssid_fields[] = { { INT_RANGE(peerkey, 0, 1) }, { INT_RANGE(mixed_cell, 0, 1) }, { INT_RANGE(frequency, 0, 65000) }, +#ifdef CONFIG_MESH + { FUNC(mesh_ht_mode) }, +#endif /* CONFIG_MESH */ { INT(wpa_ptk_rekey) }, { STR(bgscan) }, { INT_RANGE(ignore_broadcast_ssid, 0, 2) }, @@ -2235,6 +2298,9 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid) ssid->eap.fragment_size = DEFAULT_FRAGMENT_SIZE; ssid->eap.sim_num = DEFAULT_USER_SELECTED_SIM; #endif /* IEEE8021X_EAPOL */ +#ifdef CONFIG_MESH + ssid->mesh_ht_mode = DEFAULT_MESH_HT_MODE; +#endif /* CONFIG_MESH */ #ifdef CONFIG_HT_OVERRIDES ssid->disable_ht = DEFAULT_DISABLE_HT; ssid->disable_ht40 = DEFAULT_DISABLE_HT40; diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index d58707eea..852140f94 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -743,6 +743,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) INT(update_identifier); #endif /* CONFIG_HS20 */ write_int(f, "mac_addr", ssid->mac_addr, -1); + STR(mesh_ht_mode); #undef STR #undef INT diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h index a4910d0e5..85d976c39 100644 --- a/wpa_supplicant/config_ssid.h +++ b/wpa_supplicant/config_ssid.h @@ -27,6 +27,7 @@ #define DEFAULT_FRAGMENT_SIZE 1398 #define DEFAULT_BG_SCAN_PERIOD -1 +#define DEFAULT_MESH_HT_MODE CHAN_UNDEFINED /* undefined */ #define DEFAULT_DISABLE_HT 0 #define DEFAULT_DISABLE_HT40 0 #define DEFAULT_DISABLE_SGI 0 @@ -403,6 +404,15 @@ struct wpa_ssid { */ int frequency; + /** + * mesh_ht_mode - definition of HT mode in mesh mode + * + * Use the given HT mode for mesh networks. The driver will + * adapt to other stations if neccesary, but advertise the + * configured HT mode (HT20/HT40-/HT40+/NOHT). + */ + int mesh_ht_mode; + int ht40; int vht; diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c index 6439d645c..4a6924be3 100644 --- a/wpa_supplicant/mesh.c +++ b/wpa_supplicant/mesh.c @@ -21,6 +21,7 @@ #include "wpa_supplicant_i.h" #include "driver_i.h" #include "notify.h" +#include "ap.h" #include "mesh_mpm.h" #include "mesh_rsn.h" #include "mesh.h" @@ -241,6 +242,8 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, goto out_free; } + wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf); + return 0; out_free: wpa_supplicant_mesh_deinit(wpa_s); @@ -295,6 +298,9 @@ int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s, params.meshid = ssid->ssid; params.meshid_len = ssid->ssid_len; params.freq = ssid->frequency; +#ifdef CONFIG_IEEE80211N + params.ht_mode = ssid->mesh_ht_mode; +#endif /* CONFIG_IEEE80211N */ if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) { params.flags |= WPA_DRIVER_MESH_FLAG_SAE_AUTH; diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c index e440d4ffb..322c96c0d 100644 --- a/wpa_supplicant/mesh_mpm.c +++ b/wpa_supplicant/mesh_mpm.c @@ -213,6 +213,9 @@ static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s, struct hostapd_data *bss = ifmsh->bss[0]; struct mesh_conf *conf = ifmsh->mconf; u8 supp_rates[2 + 2 + 32]; +#ifdef CONFIG_IEEE80211N + u8 ht_capa_oper[2 + 26 + 2 + 22]; +#endif /* CONFIG_IEEE80211N */ u8 *pos, *cat; u8 ie_len, add_plid = 0; int ret; @@ -243,6 +246,7 @@ static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s, /* capability info */ wpabuf_put_le16(buf, ampe ? IEEE80211_CAP_PRIVACY : 0); + /* aid */ if (type == PLINK_CONFIRM) wpabuf_put_le16(buf, sta->peer_lid); @@ -309,7 +313,14 @@ static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s, mesh_rsn_get_pmkid(wpa_s->mesh_rsn, sta, wpabuf_put(buf, PMKID_LEN)); - /* TODO HT IEs */ +#ifdef CONFIG_IEEE80211N + if (type != PLINK_CLOSE && + wpa_s->current_ssid->mesh_ht_mode != CHAN_NO_HT) { + pos = hostapd_eid_ht_capabilities(bss, ht_capa_oper); + pos = hostapd_eid_ht_operation(bss, pos); + wpabuf_put_data(buf, ht_capa_oper, pos - ht_capa_oper); + } +#endif /* CONFIG_IEEE80211N */ if (ampe && mesh_rsn_protect_frame(wpa_s->mesh_rsn, sta, cat, buf)) { wpa_msg(wpa_s, MSG_INFO, @@ -520,6 +531,12 @@ void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr, mesh_mpm_init_link(wpa_s, sta); +#ifdef CONFIG_IEEE80211N + copy_sta_ht_capab(data, sta, elems->ht_capabilities, + elems->ht_capabilities_len); + update_ht_state(data, sta); +#endif /* CONFIG_IEEE80211N */ + /* insert into driver */ os_memset(¶ms, 0, sizeof(params)); params.supp_rates = sta->supported_rates; @@ -528,7 +545,7 @@ void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr, params.plink_state = sta->plink_state; params.aid = sta->peer_lid; params.listen_interval = 100; - /* TODO: HT capabilities */ + params.ht_capabilities = sta->ht_capabilities; params.flags |= WPA_STA_WMM; params.flags_mask |= WPA_STA_AUTHENTICATED; if (conf->security == MESH_CONF_SEC_NONE) {