diff options
33 files changed, 562 insertions, 84 deletions
diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 5f842810..caf51a94 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -2445,8 +2445,9 @@ static int hostapd_config_fill(struct hostapd_config *conf, wpa_printf(MSG_ERROR, "Line %d: tries to enable ACS but CONFIG_ACS disabled", line); return 1; -#endif /* CONFIG_ACS */ +#else /* CONFIG_ACS */ conf->channel = 0; +#endif /* CONFIG_ACS */ } else conf->channel = atoi(pos); } else if (os_strcmp(buf, "chanlist") == 0) { @@ -3074,8 +3075,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, char *end; \ \ conf->_val = strtod(pos, &end); \ - if (*end || conf->_val < 0.0d || \ - conf->_val > 1.0d) { \ + if (*end || conf->_val < 0.0 || \ + conf->_val > 1.0) { \ wpa_printf(MSG_ERROR, \ "Line %d: Invalid value '%s'", \ line, pos); \ diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 0a143d39..9680817b 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -174,11 +174,11 @@ struct hostapd_config * hostapd_config_defaults(void) conf->ap_table_expiration_time = 60; #ifdef CONFIG_TESTING_OPTIONS - conf->ignore_probe_probability = 0.0d; - conf->ignore_auth_probability = 0.0d; - conf->ignore_assoc_probability = 0.0d; - conf->ignore_reassoc_probability = 0.0d; - conf->corrupt_gtk_rekey_mic_probability = 0.0d; + conf->ignore_probe_probability = 0.0; + conf->ignore_auth_probability = 0.0; + conf->ignore_assoc_probability = 0.0; + conf->ignore_reassoc_probability = 0.0; + conf->corrupt_gtk_rekey_mic_probability = 0.0; #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_ACS diff --git a/src/ap/beacon.c b/src/ap/beacon.c index 56df3182..27525dc6 100644 --- a/src/ap/beacon.c +++ b/src/ap/beacon.c @@ -645,7 +645,7 @@ void handle_probe_req(struct hostapd_data *hapd, * with AP configuration */ #ifdef CONFIG_TESTING_OPTIONS - if (hapd->iconf->ignore_probe_probability > 0.0d && + if (hapd->iconf->ignore_probe_probability > 0.0 && drand48() < hapd->iconf->ignore_probe_probability) { wpa_printf(MSG_INFO, "TESTING: ignoring probe request from " MACSTR, @@ -951,6 +951,9 @@ void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params) int ieee802_11_set_beacon(struct hostapd_data *hapd) { struct wpa_driver_ap_params params; + struct hostapd_freq_params freq; + struct hostapd_iface *iface = hapd->iface; + struct hostapd_config *iconf = iface->conf; struct wpabuf *beacon, *proberesp, *assocresp; int res, ret = -1; @@ -972,6 +975,17 @@ int ieee802_11_set_beacon(struct hostapd_data *hapd) params.proberesp_ies = proberesp; params.assocresp_ies = assocresp; + if (iface->current_mode && + hostapd_set_freq_params(&freq, iconf->hw_mode, iface->freq, + iconf->channel, iconf->ieee80211n, + iconf->ieee80211ac, + iconf->secondary_channel, + iconf->vht_oper_chwidth, + iconf->vht_oper_centr_freq_seg0_idx, + iconf->vht_oper_centr_freq_seg1_idx, + iface->current_mode->vht_capab) == 0) + params.freq = &freq; + res = hostapd_drv_set_ap(hapd, ¶ms); hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp); if (res) diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c index 97609334..ccbbab5b 100644 --- a/src/ap/ctrl_iface_ap.c +++ b/src/ap/ctrl_iface_ap.c @@ -405,6 +405,7 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, "num_sta_ht_no_gf=%d\n" "num_sta_no_ht=%d\n" "num_sta_ht_20_mhz=%d\n" + "num_sta_ht40_intolerant=%d\n" "olbc_ht=%d\n" "ht_op_mode=0x%x\n", hostapd_state_text(iface->state), @@ -417,6 +418,7 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, iface->num_sta_ht_no_gf, iface->num_sta_no_ht, iface->num_sta_ht_20mhz, + iface->num_sta_ht40_intolerant, iface->olbc_ht, iface->ht_op_mode); if (ret < 0 || (size_t) ret >= buflen - len) diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index a8c24ebd..fb095efb 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -9,6 +9,7 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/eloop.h" #include "radius/radius.h" #include "drivers/driver.h" #include "common/ieee802_11_defs.h" @@ -30,6 +31,7 @@ #include "ap_config.h" #include "hw_features.h" #include "dfs.h" +#include "beacon.h" int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, @@ -121,6 +123,24 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, } #endif /* CONFIG_P2P */ +#ifdef CONFIG_IEEE80211N +#ifdef NEED_AP_MLME + if (elems.ht_capabilities && + elems.ht_capabilities_len >= + sizeof(struct ieee80211_ht_capabilities) && + (hapd->iface->conf->ht_capab & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) { + struct ieee80211_ht_capabilities *ht_cap = + (struct ieee80211_ht_capabilities *) + elems.ht_capabilities; + + if (le_to_host16(ht_cap->ht_capabilities_info) & + HT_CAP_INFO_40MHZ_INTOLERANT) + ht40_intolerant_add(hapd->iface, sta); + } +#endif /* NEED_AP_MLME */ +#endif /* CONFIG_IEEE80211N */ + #ifdef CONFIG_INTERWORKING if (elems.ext_capab && elems.ext_capab_len > 4) { if (elems.ext_capab[4] & 0x01) @@ -981,6 +1001,8 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; #endif /* NEED_AP_MLME */ case EVENT_RX_MGMT: + if (!data->rx_mgmt.frame) + break; #ifdef NEED_AP_MLME if (hostapd_mgmt_rx(hapd, &data->rx_mgmt) > 0) break; diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index 614a5bf4..391d7746 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -33,6 +33,7 @@ #include "p2p_hostapd.h" #include "gas_serv.h" #include "dfs.h" +#include "ieee802_11.h" static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason); @@ -1352,6 +1353,12 @@ void hostapd_interface_deinit(struct hostapd_iface *iface) if (iface == NULL) return; +#ifdef CONFIG_IEEE80211N +#ifdef NEED_AP_MLME + hostapd_stop_setup_timers(iface); + eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL); +#endif /* NEED_AP_MLME */ +#endif /* CONFIG_IEEE80211N */ eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); iface->wait_channel_update = 0; diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index 090544d5..bd85c54a 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -327,6 +327,9 @@ struct hostapd_iface { /* Number of HT associated stations 20 MHz */ int num_sta_ht_20mhz; + /* Number of HT40 intolerant stations */ + int num_sta_ht40_intolerant; + /* Overlapping BSS information */ int olbc_ht; @@ -351,11 +354,16 @@ struct hostapd_iface { unsigned int dfs_cac_ms; struct os_reltime dfs_cac_start; + /* Latched with the actual secondary channel information and will be + * used while juggling between HT20 and HT40 modes. */ + int secondary_ch; + #ifdef CONFIG_ACS unsigned int acs_num_completed_scans; #endif /* CONFIG_ACS */ void (*scan_cb)(struct hostapd_iface *iface); + int num_ht40_scan_tries; }; /* hostapd.c */ diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c index d47a366b..b3618345 100644 --- a/src/ap/hw_features.c +++ b/src/ap/hw_features.c @@ -19,6 +19,8 @@ #include "ap_config.h" #include "ap_drv_ops.h" #include "acs.h" +#include "ieee802_11.h" +#include "beacon.h" #include "hw_features.h" @@ -414,6 +416,7 @@ static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface, int pri = bss->freq; int sec = pri; int sec_chan, pri_chan; + struct ieee802_11_elems elems; ieee80211n_get_pri_sec_chan(bss, &pri_chan, &sec_chan); @@ -445,7 +448,23 @@ static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface, } } - /* TODO: 40 MHz intolerant */ + ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, + 0); + if (elems.ht_capabilities && + elems.ht_capabilities_len >= + sizeof(struct ieee80211_ht_capabilities)) { + struct ieee80211_ht_capabilities *ht_cap = + (struct ieee80211_ht_capabilities *) + elems.ht_capabilities; + + if (le_to_host16(ht_cap->ht_capabilities_info) & + HT_CAP_INFO_40MHZ_INTOLERANT) { + wpa_printf(MSG_DEBUG, + "40 MHz Intolerant is set on channel %d in BSS " + MACSTR, pri, MAC2STR(bss->bssid)); + return 0; + } + } } return 1; @@ -475,6 +494,7 @@ static void ieee80211n_check_scan(struct hostapd_iface *iface) oper40 = ieee80211n_check_40mhz_2g4(iface, scan_res); wpa_scan_results_free(scan_res); + iface->secondary_ch = iface->conf->secondary_channel; if (!oper40) { wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on " "channel pri=%d sec=%d based on overlapping BSSes", @@ -482,9 +502,21 @@ static void ieee80211n_check_scan(struct hostapd_iface *iface) iface->conf->channel + iface->conf->secondary_channel * 4); iface->conf->secondary_channel = 0; + if (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX) { + /* + * TODO: Could consider scheduling another scan to check + * if channel width can be changed if no coex reports + * are received from associating stations. + */ + } } res = ieee80211n_allowed_ht40_channel_pair(iface); + if (!res) { + iface->conf->secondary_channel = 0; + wpa_printf(MSG_INFO, "Fallback to 20 MHz"); + } + hostapd_setup_interface_complete(iface, !res); } @@ -570,9 +602,55 @@ static void ieee80211n_scan_channels_5g(struct hostapd_iface *iface, } +static void ap_ht40_scan_retry(void *eloop_data, void *user_data) +{ +#define HT2040_COEX_SCAN_RETRY 15 + struct hostapd_iface *iface = eloop_data; + struct wpa_driver_scan_params params; + int ret; + + os_memset(¶ms, 0, sizeof(params)); + if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) + ieee80211n_scan_channels_2g4(iface, ¶ms); + else + ieee80211n_scan_channels_5g(iface, ¶ms); + + ret = hostapd_driver_scan(iface->bss[0], ¶ms); + iface->num_ht40_scan_tries++; + os_free(params.freqs); + + if (ret == -EBUSY && + iface->num_ht40_scan_tries < HT2040_COEX_SCAN_RETRY) { + wpa_printf(MSG_ERROR, + "Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again (attempt %d)", + ret, strerror(-ret), iface->num_ht40_scan_tries); + eloop_register_timeout(1, 0, ap_ht40_scan_retry, iface, NULL); + return; + } + + if (ret == 0) { + iface->scan_cb = ieee80211n_check_scan; + return; + } + + wpa_printf(MSG_DEBUG, + "Failed to request a scan in device, bringing up in HT20 mode"); + iface->conf->secondary_channel = 0; + iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; + hostapd_setup_interface_complete(iface, 0); +} + + +void hostapd_stop_setup_timers(struct hostapd_iface *iface) +{ + eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL); +} + + static int ieee80211n_check_40mhz(struct hostapd_iface *iface) { struct wpa_driver_scan_params params; + int ret; if (!iface->conf->secondary_channel) return 0; /* HT40 not used */ @@ -585,13 +663,26 @@ static int ieee80211n_check_40mhz(struct hostapd_iface *iface) ieee80211n_scan_channels_2g4(iface, ¶ms); else ieee80211n_scan_channels_5g(iface, ¶ms); - if (hostapd_driver_scan(iface->bss[0], ¶ms) < 0) { - wpa_printf(MSG_ERROR, "Failed to request a scan of " - "neighboring BSSes"); - os_free(params.freqs); + + ret = hostapd_driver_scan(iface->bss[0], ¶ms); + os_free(params.freqs); + + if (ret == -EBUSY) { + wpa_printf(MSG_ERROR, + "Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again", + ret, strerror(-ret)); + iface->num_ht40_scan_tries = 1; + eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL); + eloop_register_timeout(1, 0, ap_ht40_scan_retry, iface, NULL); + return 1; + } + + if (ret < 0) { + wpa_printf(MSG_ERROR, + "Failed to request a scan of neighboring BSSes ret=%d (%s)", + ret, strerror(-ret)); return -1; } - os_free(params.freqs); iface->scan_cb = ieee80211n_check_scan; return 1; diff --git a/src/ap/hw_features.h b/src/ap/hw_features.h index 783ae5e1..0f67ab8e 100644 --- a/src/ap/hw_features.h +++ b/src/ap/hw_features.h @@ -23,6 +23,7 @@ int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq); int hostapd_check_ht_capab(struct hostapd_iface *iface); int hostapd_prepare_rates(struct hostapd_iface *iface, struct hostapd_hw_modes *mode); +void hostapd_stop_setup_timers(struct hostapd_iface *iface); #else /* NEED_AP_MLME */ static inline void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, @@ -61,6 +62,10 @@ static inline int hostapd_prepare_rates(struct hostapd_iface *iface, return 0; } +static inline void hostapd_stop_setup_timers(struct hostapd_iface *iface) +{ +} + #endif /* NEED_AP_MLME */ #endif /* HW_FEATURES_H */ diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 14fb5675..ca8db8fb 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -62,7 +62,6 @@ u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) } *pos++ = num; - count = 0; for (i = 0, count = 0; i < hapd->iface->num_rates && count < num; i++) { count++; @@ -105,7 +104,6 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid) *pos++ = WLAN_EID_EXT_SUPP_RATES; *pos++ = num; - count = 0; for (i = 0, count = 0; i < hapd->iface->num_rates && count < num + 8; i++) { count++; @@ -565,7 +563,7 @@ static void handle_auth(struct hostapd_data *hapd, } #ifdef CONFIG_TESTING_OPTIONS - if (hapd->iconf->ignore_auth_probability > 0.0d && + if (hapd->iconf->ignore_auth_probability > 0.0 && drand48() < hapd->iconf->ignore_auth_probability) { wpa_printf(MSG_INFO, "TESTING: ignoring auth frame from " MACSTR, @@ -1291,7 +1289,7 @@ static void handle_assoc(struct hostapd_data *hapd, #ifdef CONFIG_TESTING_OPTIONS if (reassoc) { - if (hapd->iconf->ignore_reassoc_probability > 0.0d && + if (hapd->iconf->ignore_reassoc_probability > 0.0 && drand48() < hapd->iconf->ignore_reassoc_probability) { wpa_printf(MSG_INFO, "TESTING: ignoring reassoc request from " @@ -1299,7 +1297,7 @@ static void handle_assoc(struct hostapd_data *hapd, return; } } else { - if (hapd->iconf->ignore_assoc_probability > 0.0d && + if (hapd->iconf->ignore_assoc_probability > 0.0 && drand48() < hapd->iconf->ignore_assoc_probability) { wpa_printf(MSG_INFO, "TESTING: ignoring assoc request from " @@ -1628,7 +1626,8 @@ static int handle_action(struct hostapd_data *hapd, switch (mgmt->u.action.category) { #ifdef CONFIG_IEEE80211R case WLAN_ACTION_FT: - if (wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action, + if (!sta || + wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action, len - IEEE80211_HDRLEN)) break; return 1; @@ -1647,6 +1646,15 @@ static int handle_action(struct hostapd_data *hapd, #endif /* CONFIG_WNM */ case WLAN_ACTION_PUBLIC: case WLAN_ACTION_PROTECTED_DUAL: +#ifdef CONFIG_IEEE80211N + if (mgmt->u.action.u.public_action.action == + WLAN_PA_20_40_BSS_COEX) { + wpa_printf(MSG_DEBUG, + "HT20/40 coex mgmt frame received from STA " + MACSTR, MAC2STR(mgmt->sa)); + hostapd_2040_coex_action(hapd, mgmt, len); + } +#endif /* CONFIG_IEEE80211N */ if (hapd->public_action_cb) { hapd->public_action_cb(hapd->public_action_cb_ctx, (u8 *) mgmt, len, diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h index 809b4ca6..cf0d3f2b 100644 --- a/src/ap/ieee802_11.h +++ b/src/ap/ieee802_11.h @@ -14,11 +14,14 @@ struct hostapd_data; struct sta_info; struct hostapd_frame_info; struct ieee80211_ht_capabilities; +struct ieee80211_mgmt; int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, struct hostapd_frame_info *fi); void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len, u16 stype, int ok); +void hostapd_2040_coex_action(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len); #ifdef NEED_AP_MLME int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen); int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, @@ -39,6 +42,7 @@ static inline int ieee802_11_get_mib_sta(struct hostapd_data *hapd, #endif /* NEED_AP_MLME */ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, int probe); +void ap_ht2040_timeout(void *eloop_data, void *user_data); u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid); @@ -59,6 +63,8 @@ void hostapd_get_vht_capab(struct hostapd_data *hapd, u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ht_capab, size_t ht_capab_len); void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta); +void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta); +void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta); u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *vht_capab, size_t vht_capab_len); u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta, diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c index 1d64748f..c0a7cd41 100644 --- a/src/ap/ieee802_11_ht.c +++ b/src/ap/ieee802_11_ht.c @@ -10,12 +10,15 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "hostapd.h" #include "ap_config.h" #include "sta_info.h" #include "beacon.h" #include "ieee802_11.h" +#include "hw_features.h" +#include "ap_drv_ops.h" u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid) @@ -172,6 +175,117 @@ int hostapd_ht_operation_update(struct hostapd_iface *iface) } +static int is_40_allowed(struct hostapd_iface *iface, int channel) +{ + int pri_freq, sec_freq; + int affected_start, affected_end; + int pri = 2407 + 5 * channel; + + if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G) + return 1; + + pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel); + + if (iface->conf->secondary_channel > 0) + sec_freq = pri_freq + 20; + else + sec_freq = pri_freq - 20; + + affected_start = (pri_freq + sec_freq) / 2 - 25; + affected_end = (pri_freq + sec_freq) / 2 + 25; + if ((pri < affected_start || pri > affected_end)) + return 1; /* not within affected channel range */ + + wpa_printf(MSG_ERROR, "40 MHz affected channel range: [%d,%d] MHz", + affected_start, affected_end); + wpa_printf(MSG_ERROR, "Neighboring BSS: freq=%d", pri); + return 0; +} + + +void hostapd_2040_coex_action(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len) +{ + struct hostapd_iface *iface = hapd->iface; + struct ieee80211_2040_bss_coex_ie *bc_ie; + struct ieee80211_2040_intol_chan_report *ic_report; + int is_ht_allowed = 1; + int i; + const u8 *data = (const u8 *) &mgmt->u.action.u.public_action.action; + size_t hdr_len; + + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "hostapd_public_action - action=%d", + mgmt->u.action.u.public_action.action); + + if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) + return; + + hdr_len = data - (u8 *) mgmt; + if (hdr_len > len) + return; + data++; + + bc_ie = (struct ieee80211_2040_bss_coex_ie *) &data[0]; + ic_report = (struct ieee80211_2040_intol_chan_report *) + (&data[0] + sizeof(*bc_ie)); + + if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ) { + hostapd_logger(hapd, mgmt->sa, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "20 MHz BSS width request bit is set in BSS coexistence information field"); + is_ht_allowed = 0; + } + + if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_40MHZ_INTOL) { + hostapd_logger(hapd, mgmt->sa, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "40 MHz intolerant bit is set in BSS coexistence information field"); + is_ht_allowed = 0; + } + + if (ic_report && + (ic_report->element_id == WLAN_EID_20_40_BSS_INTOLERANT)) { + /* Go through the channel report to find any BSS there in the + * affected channel range */ + for (i = 0; i < ic_report->length - 1; i++) { + if (is_40_allowed(iface, ic_report->variable[i])) + continue; + hostapd_logger(hapd, mgmt->sa, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "20_40_INTOLERANT channel %d reported", + ic_report->variable[i]); + is_ht_allowed = 0; + break; + } + } + + if (!is_ht_allowed && + (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) { + if (iface->conf->secondary_channel) { + hostapd_logger(hapd, mgmt->sa, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Switching to 20 MHz operation"); + iface->conf->secondary_channel = 0; + ieee802_11_set_beacons(iface); + } + if (!iface->num_sta_ht40_intolerant) { + unsigned int delay_time; + delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR * + iface->conf->obss_interval; + eloop_cancel_timeout(ap_ht2040_timeout, hapd->iface, + NULL); + eloop_register_timeout(delay_time, 0, ap_ht2040_timeout, + hapd->iface, NULL); + } + } +} + + u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ht_capab, size_t ht_capab_len) { @@ -200,6 +314,52 @@ u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, } +void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta) +{ + if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G) + return; + + wpa_printf(MSG_INFO, "HT: Forty MHz Intolerant is set by STA " MACSTR + " in Association Request", MAC2STR(sta->addr)); + + if (sta->ht40_intolerant_set) + return; + + sta->ht40_intolerant_set = 1; + iface->num_sta_ht40_intolerant++; + eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL); + + if (iface->conf->secondary_channel && + (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) { + iface->conf->secondary_channel = 0; + ieee802_11_set_beacons(iface); + } +} + + +void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta) +{ + if (!sta->ht40_intolerant_set) + return; + + sta->ht40_intolerant_set = 0; + iface->num_sta_ht40_intolerant--; + + if (iface->num_sta_ht40_intolerant == 0 && + (iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && + (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) { + unsigned int delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR * + iface->conf->obss_interval; + wpa_printf(MSG_DEBUG, + "HT: Start 20->40 MHz transition timer (%d seconds)", + delay_time); + eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL); + eloop_register_timeout(delay_time, 0, ap_ht2040_timeout, + iface, NULL); + } +} + + static void update_sta_ht(struct hostapd_data *hapd, struct sta_info *sta) { u16 ht_capab; @@ -227,6 +387,9 @@ static void update_sta_ht(struct hostapd_data *hapd, struct sta_info *sta) __func__, MAC2STR(sta->addr), hapd->iface->num_sta_ht_20mhz); } + + if (ht_capab & HT_CAP_INFO_40MHZ_INTOLERANT) + ht40_intolerant_add(hapd->iface, sta); } @@ -288,3 +451,14 @@ void hostapd_get_ht_capab(struct hostapd_data *hapd, neg_ht_cap->ht_capabilities_info = host_to_le16(cap); } + + +void ap_ht2040_timeout(void *eloop_data, void *user_data) +{ + struct hostapd_iface *iface = eloop_data; + + wpa_printf(MSG_INFO, "Switching to 40 MHz operation"); + + iface->conf->secondary_channel = iface->secondary_ch; + ieee802_11_set_beacons(iface); +} diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index f5417de0..60f07682 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -206,6 +206,10 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) hapd->iface->num_sta_ht_20mhz--; } +#ifdef CONFIG_IEEE80211N + ht40_intolerant_remove(hapd->iface, sta); +#endif /* CONFIG_IEEE80211N */ + #ifdef CONFIG_P2P if (sta->no_p2p_set) { sta->no_p2p_set = 0; diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index 2dbdeb18..03db98f6 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -54,6 +54,7 @@ struct sta_info { unsigned int no_short_preamble_set:1; unsigned int no_ht_gf_set:1; unsigned int no_ht_set:1; + unsigned int ht40_intolerant_set:1; unsigned int ht_20mhz_set:1; unsigned int no_p2p_set:1; unsigned int qos_map_enabled:1; diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 77e78584..d2126103 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -1421,7 +1421,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, key->key_mic); #ifdef CONFIG_TESTING_OPTIONS if (!pairwise && - wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0d && + wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0 && drand48() < wpa_auth->conf.corrupt_gtk_rekey_mic_probability) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c index e957c6e9..1e4defcf 100644 --- a/src/ap/wpa_auth_ie.c +++ b/src/ap/wpa_auth_ie.c @@ -675,7 +675,7 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, break; } } - if (sm->pmksa) { + if (sm->pmksa && pmkid) { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, "PMKID found from PMKSA cache " "eap_type=%d vlan_id=%d", diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index cb70130d..8fe2e4a6 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -646,6 +646,7 @@ struct ieee80211_vht_operation { #define ERP_INFO_USE_PROTECTION BIT(1) #define ERP_INFO_BARKER_PREAMBLE_MODE BIT(2) +#define OVERLAPPING_BSS_TRANS_DELAY_FACTOR 5 /* HT Capabilities Info field within HT Capabilities element */ #define HT_CAP_INFO_LDPC_CODING_CAP ((u16) BIT(0)) diff --git a/src/common/qca-vendor-attr.h b/src/common/qca-vendor-attr.h new file mode 100644 index 00000000..6f51803e --- /dev/null +++ b/src/common/qca-vendor-attr.h @@ -0,0 +1,28 @@ +/* + * Qualcomm Atheros vendor specific attribute definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef QCA_VENDOR_ATTR_H +#define QCA_VENDOR_ATTR_H + +/* + * This file defines some of the attributes used with Qualcomm Atheros OUI + * 00:13:74 in a way that is not suitable for qca-vendor.h, e.g., due to + * compiler dependencies. + */ + +struct qca_avoid_freq_range { + u32 start_freq; + u32 end_freq; +} __attribute__ ((packed)); + +struct qca_avoid_freq_list { + u32 count; + struct qca_avoid_freq_range range[0]; +} __attribute__ ((packed)); + +#endif /* QCA_VENDOR_ATTR_H */ diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h index 0e292e67..1bc981b0 100644 --- a/src/common/qca-vendor.h +++ b/src/common/qca-vendor.h @@ -51,25 +51,18 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY = 10, QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY = 11, QCA_NL80211_VENDOR_SUBCMD_NAN = 12, + QCA_NL80211_VENDOR_SUBMCD_STATS_EXT = 13, }; -struct qca_avoid_freq_range { - u32 start_freq; - u32 end_freq; -} STRUCT_PACKED; - -struct qca_avoid_freq_list { - u32 count; - struct qca_avoid_freq_range range[0]; -} STRUCT_PACKED; - enum qca_wlan_vendor_attr { QCA_WLAN_VENDOR_ATTR_INVALID = 0, /* used by QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY */ QCA_WLAN_VENDOR_ATTR_DFS = 1, /* used by QCA_NL80211_VENDOR_SUBCMD_NAN */ QCA_WLAN_VENDOR_ATTR_NAN = 2, + /* used by QCA_NL80211_VENDOR_SUBCMD_STATS_EXT */ + QCA_WLAN_VENDOR_ATTR_STATS_EXT = 3, /* keep last */ QCA_WLAN_VENDOR_ATTR_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1, diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 13bf7183..00565a7b 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -868,6 +868,11 @@ struct wpa_driver_ap_params { * osen - Whether OSEN security is enabled */ int osen; + + /** + * freq - Channel parameters for dynamic bandwidth changes + */ + struct hostapd_freq_params *freq; }; /** @@ -921,7 +926,8 @@ struct wpa_driver_capa { #define WPA_DRIVER_FLAGS_AP 0x00000040 /* Driver needs static WEP key setup after association has been completed */ #define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE 0x00000080 -/* unused: 0x00000100 */ +/* Driver supports dynamic HT 20/40 MHz channel changes during BSS lifetime */ +#define WPA_DRIVER_FLAGS_HT_2040_COEX 0x00000100 /* Driver supports concurrent P2P operations */ #define WPA_DRIVER_FLAGS_P2P_CONCURRENT 0x00000200 /* @@ -2164,7 +2170,7 @@ struct wpa_driver_ops { * @session_timeout: Session timeout for the station * Returns: 0 on success, -1 on failure */ - int (*set_radius_acl_auth)(void *priv, const u8 *mac, int accepted, + int (*set_radius_acl_auth)(void *priv, const u8 *mac, int accepted, u32 session_timeout); /** @@ -2228,7 +2234,7 @@ struct wpa_driver_ops { * Returns: 0 on success, -1 on failure */ int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val, - const char *bridge_ifname, char *ifname_wds); + const char *bridge_ifname, char *ifname_wds); /** * send_action - Transmit an Action frame @@ -2516,7 +2522,7 @@ struct wpa_driver_ops { * signal_poll - Get current connection information * @priv: Private driver interface data * @signal_info: Connection info structure - */ + */ int (*signal_poll)(void *priv, struct wpa_signal_info *signal_info); /** diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 1300703e..b0c8a35f 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -29,6 +29,7 @@ #include "eloop.h" #include "utils/list.h" #include "common/qca-vendor.h" +#include "common/qca-vendor-attr.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "l2_packet/l2_packet.h" @@ -235,6 +236,7 @@ struct i802_bss { u8 addr[ETH_ALEN]; int freq; + int bandwidth; int if_dynamic; void *ctx; @@ -385,8 +387,8 @@ static int wpa_driver_nl80211_if_remove(struct i802_bss *bss, enum wpa_driver_if_type type, const char *ifname); -static int wpa_driver_nl80211_set_freq(struct i802_bss *bss, - struct hostapd_freq_params *freq); +static int nl80211_set_channel(struct i802_bss *bss, + struct hostapd_freq_params *freq, int set_chan); static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, int ifindex, int disabled); @@ -536,22 +538,22 @@ static enum chan_width convert2width(int width) static int is_ap_interface(enum nl80211_iftype nlmode) { - return (nlmode == NL80211_IFTYPE_AP || - nlmode == NL80211_IFTYPE_P2P_GO); + return nlmode == NL80211_IFTYPE_AP || + nlmode == NL80211_IFTYPE_P2P_GO; } static int is_sta_interface(enum nl80211_iftype nlmode) { - return (nlmode == NL80211_IFTYPE_STATION || - nlmode == NL80211_IFTYPE_P2P_CLIENT); + return nlmode == NL80211_IFTYPE_STATION || + nlmode == NL80211_IFTYPE_P2P_CLIENT; } static int is_p2p_net_interface(enum nl80211_iftype nlmode) { - return (nlmode == NL80211_IFTYPE_P2P_CLIENT || - nlmode == NL80211_IFTYPE_P2P_GO); + return nlmode == NL80211_IFTYPE_P2P_CLIENT || + nlmode == NL80211_IFTYPE_P2P_GO; } @@ -3656,6 +3658,9 @@ static void wiphy_info_feature_flags(struct wiphy_info_data *info, if (flags & NL80211_FEATURE_NEED_OBSS_SCAN) capa->flags |= WPA_DRIVER_FLAGS_OBSS_SCAN; + + if (flags & NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE) + capa->flags |= WPA_DRIVER_FLAGS_HT_2040_COEX; } @@ -4522,7 +4527,7 @@ static int nl80211_mgmt_subscribe_ap(struct i802_bss *bss) * it isn't per interface ... maybe just dump the scan * results periodically for OLBC? */ -// WLAN_FC_STYPE_BEACON, + /* WLAN_FC_STYPE_BEACON, */ }; unsigned int i; @@ -7299,6 +7304,30 @@ static int wpa_driver_nl80211_set_ap(void *priv, nl80211_set_bss(bss, params->cts_protect, params->preamble, params->short_slot_time, params->ht_opmode, params->isolate, params->basic_rates); + if (beacon_set && params->freq && + params->freq->bandwidth != bss->bandwidth) { + wpa_printf(MSG_DEBUG, + "nl80211: Update BSS %s bandwidth: %d -> %d", + bss->ifname, bss->bandwidth, + params->freq->bandwidth); + ret = nl80211_set_channel(bss, params->freq, 1); + if (ret) { + wpa_printf(MSG_DEBUG, + "nl80211: Frequency set failed: %d (%s)", + ret, strerror(-ret)); + } else { + wpa_printf(MSG_DEBUG, + "nl80211: Frequency set succeeded for ht2040 coex"); + bss->bandwidth = params->freq->bandwidth; + } + } else if (!beacon_set) { + /* + * cfg80211 updates the driver on frequence change in AP + * mode only at the point when beaconing is started, so + * set the initial value here. + */ + bss->bandwidth = params->freq->bandwidth; + } } return ret; nla_put_failure: @@ -7363,8 +7392,8 @@ nla_put_failure: } -static int wpa_driver_nl80211_set_freq(struct i802_bss *bss, - struct hostapd_freq_params *freq) +static int nl80211_set_channel(struct i802_bss *bss, + struct hostapd_freq_params *freq, int set_chan) { struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; @@ -7378,7 +7407,8 @@ static int wpa_driver_nl80211_set_freq(struct i802_bss *bss, if (!msg) return -1; - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY); + nl80211_cmd(drv, msg, 0, set_chan ? NL80211_CMD_SET_CHANNEL : + NL80211_CMD_SET_WIPHY); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); if (nl80211_put_freq_params(msg, freq) < 0) @@ -7841,7 +7871,7 @@ static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx) return; } - if (ieee80211_radiotap_iterator_init(&iter, (void*)buf, len, NULL)) { + if (ieee80211_radiotap_iterator_init(&iter, (void *) buf, len, NULL)) { wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame"); return; } @@ -8395,7 +8425,7 @@ static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv, return -1; } - if (wpa_driver_nl80211_set_freq(drv->first_bss, &freq)) { + if (nl80211_set_channel(drv->first_bss, &freq, 0)) { if (old_mode != nlmode) wpa_driver_nl80211_set_mode(drv->first_bss, old_mode); nl80211_remove_monitor_interface(drv); @@ -9121,7 +9151,7 @@ static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized) static int i802_set_freq(void *priv, struct hostapd_freq_params *freq) { struct i802_bss *bss = priv; - return wpa_driver_nl80211_set_freq(bss, freq); + return nl80211_set_channel(bss, freq, 0); } @@ -9620,7 +9650,7 @@ static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val, - const char *bridge_ifname, char *ifname_wds) + const char *bridge_ifname, char *ifname_wds) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; diff --git a/src/drivers/driver_test.c b/src/drivers/driver_test.c index 1b13d3d2..3608b522 100644 --- a/src/drivers/driver_test.c +++ b/src/drivers/driver_test.c @@ -1906,7 +1906,7 @@ static void wpa_driver_test_scan_cmd(struct wpa_driver_test_data *drv, /* data: optional [ STA-addr | ' ' | IEs(hex) ] */ - if (!drv->ibss) + if (bss == NULL || !drv->ibss) return; pos = buf; diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h index 1ba9d626..406010d4 100644 --- a/src/drivers/nl80211_copy.h +++ b/src/drivers/nl80211_copy.h @@ -1579,6 +1579,10 @@ enum nl80211_commands { * @NL80211_ATTR_TDLS_PEER_CAPABILITY: flags for TDLS peer capabilities, u32. * As specified in the &enum nl80211_tdls_peer_capability. * + * @NL80211_ATTR_IFACE_SOCKET_OWNER: flag attribute, if set during interface + * creation then the new interface will be owned by the netlink socket + * that created it and will be destroyed when the socket is closed + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1914,6 +1918,8 @@ enum nl80211_attrs { NL80211_ATTR_TDLS_PEER_CAPABILITY, + NL80211_ATTR_IFACE_SOCKET_OWNER, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -2336,9 +2342,34 @@ enum nl80211_band_attr { * using this channel as the primary or any of the secondary channels * isn't possible * @NL80211_FREQUENCY_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds. + * @NL80211_FREQUENCY_ATTR_INDOOR_ONLY: Only indoor use is permitted on this + * channel. A channel that has the INDOOR_ONLY attribute can only be + * used when there is a clear assessment that the device is operating in + * an indoor surroundings, i.e., it is connected to AC power (and not + * through portable DC inverters) or is under the control of a master + * that is acting as an AP and is connected to AC power. + * @NL80211_FREQUENCY_ATTR_GO_CONCURRENT: GO operation is allowed on this + * channel if it's connected concurrently to a BSS on the same channel on + * the 2 GHz band or to a channel in the same UNII band (on the 5 GHz + * band), and IEEE80211_CHAN_RADAR is not set. Instantiating a GO on a + * channel that has the GO_CONCURRENT attribute set can be done when there + * is a clear assessment that the device is operating under the guidance of + * an authorized master, i.e., setting up a GO while the device is also + * connected to an AP with DFS and radar detection on the UNII band (it is + * up to user-space, i.e., wpa_supplicant to perform the required + * verifications) + * @NL80211_FREQUENCY_ATTR_NO_20MHZ: 20 MHz operation is not allowed + * on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_NO_10MHZ: 10 MHz operation is not allowed + * on this channel in current regulatory domain. * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number * currently defined * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use + * + * See https://apps.fcc.gov/eas/comments/GetPublishedDocument.html?id=327&tn=528122 + * for more information on the FCC description of the relaxations allowed + * by NL80211_FREQUENCY_ATTR_INDOOR_ONLY and + * NL80211_FREQUENCY_ATTR_GO_CONCURRENT. */ enum nl80211_frequency_attr { __NL80211_FREQUENCY_ATTR_INVALID, @@ -2355,6 +2386,10 @@ enum nl80211_frequency_attr { NL80211_FREQUENCY_ATTR_NO_80MHZ, NL80211_FREQUENCY_ATTR_NO_160MHZ, NL80211_FREQUENCY_ATTR_DFS_CAC_TIME, + NL80211_FREQUENCY_ATTR_INDOOR_ONLY, + NL80211_FREQUENCY_ATTR_GO_CONCURRENT, + NL80211_FREQUENCY_ATTR_NO_20MHZ, + NL80211_FREQUENCY_ATTR_NO_10MHZ, /* keep last */ __NL80211_FREQUENCY_ATTR_AFTER_LAST, @@ -2573,10 +2608,13 @@ enum nl80211_dfs_regions { * present has been registered with the wireless core that * has listed NL80211_FEATURE_CELL_BASE_REG_HINTS as a * supported feature. + * @NL80211_USER_REG_HINT_INDOOR: a user sent an hint indicating that the + * platform is operating in an indoor environment. */ enum nl80211_user_reg_hint_type { NL80211_USER_REG_HINT_USER = 0, NL80211_USER_REG_HINT_CELL_BASE = 1, + NL80211_USER_REG_HINT_INDOOR = 2, }; /** @@ -3891,6 +3929,9 @@ enum nl80211_ap_sme_features { * interface. An active monitor interface behaves like a normal monitor * interface, but gets added to the driver. It ensures that incoming * unicast packets directed at the configured interface address get ACKed. + * @NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE: This driver supports dynamic + * channel bandwidth change (e.g., HT 20 <-> 40 MHz channel) during the + * lifetime of a BSS. */ enum nl80211_feature_flags { NL80211_FEATURE_SK_TX_STATUS = 1 << 0, @@ -3911,6 +3952,7 @@ enum nl80211_feature_flags { NL80211_FEATURE_FULL_AP_CLIENT_STATE = 1 << 15, NL80211_FEATURE_USERSPACE_MPM = 1 << 16, NL80211_FEATURE_ACTIVE_MONITOR = 1 << 17, + NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE = 1 << 18, }; /** diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index bcc7e644..b30ea56f 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -4210,7 +4210,7 @@ p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next) dev = dl_list_first(&dev->list, struct p2p_device, list); - if (&dev->list == &p2p->devices) + if (!dev || &dev->list == &p2p->devices) return NULL; } while (dev->flags & P2P_DEV_PROBE_REQ_ONLY); } @@ -4222,7 +4222,7 @@ p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next) dev = dl_list_first(&dev->list, struct p2p_device, list); - if (&dev->list == &p2p->devices) + if (!dev || &dev->list == &p2p->devices) return NULL; } } @@ -4463,12 +4463,24 @@ static struct wpabuf * p2p_build_nfc_handover(struct p2p_data *p2p, p2p_buf_add_device_info(buf, p2p, NULL); if (p2p->num_groups > 0) { + int freq = p2p_group_get_freq(p2p->groups[0]); role = P2P_GO_IN_A_GROUP; - p2p_freq_to_channel(p2p_group_get_freq(p2p->groups[0]), - &op_class, &channel); + if (p2p_freq_to_channel(freq, &op_class, &channel) < 0) { + p2p_dbg(p2p, + "Unknown GO operating frequency %d MHz for NFC handover", + freq); + wpabuf_free(buf); + return NULL; + } } else if (client_freq > 0) { role = P2P_CLIENT_IN_A_GROUP; - p2p_freq_to_channel(client_freq, &op_class, &channel); + if (p2p_freq_to_channel(client_freq, &op_class, &channel) < 0) { + p2p_dbg(p2p, + "Unknown client operating frequency %d MHz for NFC handover", + client_freq); + wpabuf_free(buf); + return NULL; + } } p2p_buf_add_oob_go_neg_channel(buf, p2p->cfg->country, op_class, diff --git a/src/utils/trace.c b/src/utils/trace.c index 94440f2f..6044f5f7 100644 --- a/src/utils/trace.c +++ b/src/utils/trace.c @@ -18,11 +18,9 @@ static struct dl_list active_references = #ifdef WPA_TRACE_BFD #include <bfd.h> -#ifdef __linux__ -#include <demangle.h> -#else /* __linux__ */ -#include <libiberty/demangle.h> -#endif /* __linux__ */ + +#define DMGL_PARAMS (1 << 0) +#define DMGL_ANSI (1 << 1) static char *prg_fname = NULL; static bfd *cached_abfd = NULL; diff --git a/src/wps/http_server.c b/src/wps/http_server.c index 06c8bee2..ac088c42 100644 --- a/src/wps/http_server.c +++ b/src/wps/http_server.c @@ -244,7 +244,13 @@ struct http_server * http_server_init(struct in_addr *addr, int port, if (srv->fd < 0) goto fail; - setsockopt(srv->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (setsockopt(srv->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) + { + wpa_printf(MSG_DEBUG, + "HTTP: setsockopt(SO_REUSEADDR) failed: %s", + strerror(errno)); + /* try to continue anyway */ + } if (fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0) goto fail; diff --git a/src/wps/httpread.c b/src/wps/httpread.c index b51d9757..6d2d11c4 100644 --- a/src/wps/httpread.c +++ b/src/wps/httpread.c @@ -617,7 +617,6 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) * We do NOT support trailers except to skip them -- * this is supported (generally) by the http spec. */ - bbp = h->body + h->body_nbytes; for (;;) { int c; if (nread <= 0) diff --git a/src/wps/wps_upnp_ssdp.c b/src/wps/wps_upnp_ssdp.c index 416961cc..098571ce 100644 --- a/src/wps/wps_upnp_ssdp.c +++ b/src/wps/wps_upnp_ssdp.c @@ -134,6 +134,8 @@ next_advertisement(struct upnp_wps_device_sm *sm, *islast = 0; iface = dl_list_first(&sm->interfaces, struct upnp_wps_device_interface, list); + if (!iface) + return NULL; uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string)); msg = wpabuf_alloc(800); /* more than big enough */ if (msg == NULL) @@ -587,6 +589,8 @@ static void ssdp_parse_msearch(struct upnp_wps_device_sm *sm, &sm->interfaces, struct upnp_wps_device_interface, list); + if (!iface) + continue; data += os_strlen("uuid:"); uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string)); diff --git a/src/wps/wps_upnp_web.c b/src/wps/wps_upnp_web.c index 54c3658a..b1cf571d 100644 --- a/src/wps/wps_upnp_web.c +++ b/src/wps/wps_upnp_web.c @@ -179,15 +179,12 @@ static const char *wps_device_xml_postfix = /* format_wps_device_xml -- produce content of "file" wps_device.xml * (UPNP_WPS_DEVICE_XML_FILE) */ -static void format_wps_device_xml(struct upnp_wps_device_sm *sm, +static void format_wps_device_xml(struct upnp_wps_device_interface *iface, + struct upnp_wps_device_sm *sm, struct wpabuf *buf) { const char *s; char uuid_string[80]; - struct upnp_wps_device_interface *iface; - - iface = dl_list_first(&sm->interfaces, - struct upnp_wps_device_interface, list); wpabuf_put_str(buf, wps_device_xml_prefix); @@ -319,6 +316,10 @@ static void web_connection_parse_get(struct upnp_wps_device_sm *sm, iface = dl_list_first(&sm->interfaces, struct upnp_wps_device_interface, list); + if (iface == NULL) { + http_request_deinit(hreq); + return; + } /* * It is not required that filenames be case insensitive but it is @@ -391,7 +392,7 @@ static void web_connection_parse_get(struct upnp_wps_device_sm *sm, switch (req) { case GET_DEVICE_XML_FILE: - format_wps_device_xml(sm, buf); + format_wps_device_xml(iface, sm, buf); break; case GET_SCPD_XML_FILE: wpabuf_put_str(buf, wps_scpd_xml); @@ -419,13 +420,14 @@ web_process_get_device_info(struct upnp_wps_device_sm *sm, iface = dl_list_first(&sm->interfaces, struct upnp_wps_device_interface, list); - peer = &iface->peer; wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo"); - if (iface->ctx->ap_pin == NULL) + if (!iface || iface->ctx->ap_pin == NULL) return HTTP_INTERNAL_SERVER_ERROR; + peer = &iface->peer; + /* * Request for DeviceInfo, i.e., M1 TLVs. This is a start of WPS * registration over UPnP with the AP acting as an Enrollee. It should @@ -473,6 +475,8 @@ web_process_put_message(struct upnp_wps_device_sm *sm, char *data, iface = dl_list_first(&sm->interfaces, struct upnp_wps_device_interface, list); + if (!iface) + return HTTP_INTERNAL_SERVER_ERROR; /* * PutMessage is used by external UPnP-based Registrar to perform WPS diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c index 482fc64f..f99a8a71 100644 --- a/wpa_supplicant/bss.c +++ b/wpa_supplicant/bss.c @@ -674,7 +674,8 @@ void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s, wpa_s->last_scan_res_size = siz; } - wpa_s->last_scan_res[wpa_s->last_scan_res_used++] = bss; + if (wpa_s->last_scan_res) + wpa_s->last_scan_res[wpa_s->last_scan_res_used++] = bss; } diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index 8d59f496..38429467 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -352,8 +352,8 @@ struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp) FILE *f; char buf[512], *pos; int errors = 0, line = 0; - struct wpa_ssid *ssid, *tail = NULL, *head = NULL; - struct wpa_cred *cred, *cred_tail = NULL, *cred_head = NULL; + struct wpa_ssid *ssid, *tail, *head; + struct wpa_cred *cred, *cred_tail, *cred_head; struct wpa_config *config; int id = 0; int cred_id = 0; @@ -369,8 +369,12 @@ struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp) "structure"); return NULL; } - head = config->ssid; - cred_head = config->cred; + tail = head = config->ssid; + while (tail && tail->next) + tail = tail->next; + cred_tail = cred_head = config->cred; + while (cred_tail && cred_tail->next) + cred_tail = cred_tail->next; wpa_printf(MSG_DEBUG, "Reading configuration file '%s'", name); f = fopen(name, "r"); diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 7eedd837..9b7323bb 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -3366,6 +3366,14 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, } #endif /* CONFIG_P2P */ + if (wpa_s->wpa_state >= WPA_AUTHENTICATING) { + /* + * Indicate disconnection to keep ctrl_iface events + * consistent. + */ + wpa_supplicant_event_disassoc( + wpa_s, WLAN_REASON_DEAUTH_LEAVING, 1); + } wpa_supplicant_mark_disassoc(wpa_s); radio_remove_works(wpa_s, NULL, 0); diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index 49b2cd28..522d277a 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -6781,7 +6781,7 @@ void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr, { struct wpa_ssid *ssid = wpa_s->current_ssid; struct wpa_ssid *persistent; - struct psk_list_entry *p; + struct psk_list_entry *p, *last; if (psk_len != sizeof(p->psk)) return; @@ -6841,10 +6841,9 @@ void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr, } os_memcpy(p->psk, psk, psk_len); - if (dl_list_len(&persistent->psk_list) > P2P_MAX_STORED_CLIENTS) { - struct psk_list_entry *last; - last = dl_list_last(&persistent->psk_list, - struct psk_list_entry, list); + if (dl_list_len(&persistent->psk_list) > P2P_MAX_STORED_CLIENTS && + (last = dl_list_last(&persistent->psk_list, + struct psk_list_entry, list))) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove oldest PSK entry for " MACSTR " (p2p=%u) to make room for a new one", MAC2STR(last->addr), last->p2p); |