diff options
author | Cy Schubert <cy@FreeBSD.org> | 2021-12-02 23:00:32 +0000 |
---|---|---|
committer | Cy Schubert <cy@FreeBSD.org> | 2021-12-02 23:08:52 +0000 |
commit | 4b72b91a7132df1f77bbae194e1071ac621f1edb (patch) | |
tree | 29c815ad44a4d2a57d656d9f312a8e5561ece765 /contrib/wpa/wpa_supplicant | |
parent | 2dfc1f73552d5aa5d9f6fa3e642f00e87952551c (diff) |
wpa: Redo import wpa_supplicant/hostapd commit 14ab4a816
This is the November update to vendor/wpa committed upstream 2021-11-26.
MFC after: 1 month
Diffstat (limited to 'contrib/wpa/wpa_supplicant')
33 files changed, 3129 insertions, 203 deletions
diff --git a/contrib/wpa/wpa_supplicant/Android.mk b/contrib/wpa/wpa_supplicant/Android.mk index f539ce1348ec..0aacafd4ab96 100644 --- a/contrib/wpa/wpa_supplicant/Android.mk +++ b/contrib/wpa/wpa_supplicant/Android.mk @@ -366,12 +366,11 @@ CONFIG_AP=y ifdef CONFIG_P2P_STRICT L_CFLAGS += -DCONFIG_P2P_STRICT endif -endif - ifdef CONFIG_WIFI_DISPLAY L_CFLAGS += -DCONFIG_WIFI_DISPLAY OBJS += wifi_display.c endif +endif ifdef CONFIG_PASN L_CFLAGS += -DCONFIG_PASN diff --git a/contrib/wpa/wpa_supplicant/ChangeLog b/contrib/wpa/wpa_supplicant/ChangeLog index a06a93b22175..5ca82457ad1b 100644 --- a/contrib/wpa/wpa_supplicant/ChangeLog +++ b/contrib/wpa/wpa_supplicant/ChangeLog @@ -1864,7 +1864,8 @@ ChangeLog for wpa_supplicant generate, e.g., man pages * l2_packet_linux: use socket type SOCK_DGRAM instead of SOCK_RAW for PF_PACKET in order to prepare for network devices that do not use - Ethernet headers (e.g., network stack with native IEEE 802.11 frames) + Ethernet headers (e.g., network stack that includes IEEE 802.11 + header in the frames) * use receipt of EAPOL-Key frame as a lower layer success indication for EAP state machine to allow recovery from dropped EAP-Success frame diff --git a/contrib/wpa/wpa_supplicant/Makefile b/contrib/wpa/wpa_supplicant/Makefile index 271f2aab3118..ce1c8b2e3366 100644 --- a/contrib/wpa/wpa_supplicant/Makefile +++ b/contrib/wpa/wpa_supplicant/Makefile @@ -30,9 +30,9 @@ LIBS_p := $(LIBS) endif endif -export LIBDIR ?= /usr/local/lib/ -export INCDIR ?= /usr/local/include/ -export BINDIR ?= /usr/local/sbin/ +export LIBDIR ?= /usr/local/lib +export INCDIR ?= /usr/local/include +export BINDIR ?= /usr/local/sbin PKG_CONFIG ?= pkg-config CFLAGS += $(EXTRA_CFLAGS) @@ -389,12 +389,11 @@ CONFIG_AP=y ifdef CONFIG_P2P_STRICT CFLAGS += -DCONFIG_P2P_STRICT endif -endif - ifdef CONFIG_WIFI_DISPLAY CFLAGS += -DCONFIG_WIFI_DISPLAY OBJS += wifi_display.o endif +endif ifdef CONFIG_PASN CFLAGS += -DCONFIG_PASN @@ -1011,7 +1010,6 @@ ifdef CONFIG_PCSC # PC/SC interface for smartcards (USIM, GSM SIM) CFLAGS += -DPCSC_FUNCS -I/usr/include/PCSC OBJS += ../src/utils/pcsc_funcs.o -# -lpthread may not be needed depending on how pcsc-lite was configured ifdef CONFIG_NATIVE_WINDOWS #Once MinGW gets support for WinScard, -lwinscard could be used instead of the #dynamic symbol loading that is now used in pcsc_funcs.c @@ -1020,7 +1018,7 @@ else ifdef CONFIG_OSX LIBS += -framework PCSC else -LIBS += -lpcsclite -lpthread +LIBS += $(shell $(PKG_CONFIG) --libs libpcsclite) endif endif endif diff --git a/contrib/wpa/wpa_supplicant/README b/contrib/wpa/wpa_supplicant/README index 391912e9b6c5..05f15ff46bda 100644 --- a/contrib/wpa/wpa_supplicant/README +++ b/contrib/wpa/wpa_supplicant/README @@ -1077,3 +1077,87 @@ ext:test4@wlan0:0:0:9.679895 OK <3>EXT-RADIO-WORK-START 7 <3>EXT-RADIO-WORK-TIMEOUT 7 + + +DSCP policy procedures +---------------------- + +DSCP policy procedures defined in WFA QoS Management-R2 program +facilitates AP devices to configure DSCP settings for specific uplink +data streams. + +An AP may transmit a DSCP Policy Request frame containing zero or more +QoS Management IEs to an associated STA which supports DSCP policy +procedures. Each QoS Management element in a DSCP Policy Request frame +represents one DSCP policy, and shall include one DSCP Policy attribute +including a DSCP Policy ID, Request type, and a DSCP value. + +wpa_supplicant sends control interface event messages consisting details +of DSCP policies requested by the AP through a DSCP Policy Request frame +to external programs. The format of the control interface event messages +is as shown below: + +- Control interface event message format to indicate DSCP request start + + <3>CTRL-EVENT-DSCP-POLICY request_start [clear_all] [more] + + clear_all - AP requested to clear all DSCP policies configured earlier + more - AP may request to configure more DSCP policies with new DSCP + request + +- Control interface event message format to add new policy + + <3>CTRL-EVENT-DSCP-POLICY add <policy_id> <dscp_value> <ip_version=0|4|6> + [protocol] [source ip] [destination_ip]/[domain name] [source port] + [[<start_port> <end_port>]/destination port] + + ip_version = 0: Both IPv4 and IPv6 + = 4: IPv4 + = 6: IPv6 + protocol: Internet Protocol Numbers as per IETF RFCs + = 6: TCP + = 17: UDP + = 50: ESP + +- Control interface event message format to remove a particular policy, + identified by the policy_id attribute. + + <3>CTRL-EVENT-DSCP-POLICY remove <policy_id> + +- DSCP policy may get rejected due to invalid policy parameters. Ccontrol + interface event message format for rejected policy. + + <3>CTRL-EVENT-DSCP-POLICY reject <policy_id> + +- Control interface event message format to indicate end of DSCP request. + + <3>CTRL-EVENT-DSCP-POLICY request_end + +- External applications shall clear active DSCP policies upon receiving + "CTRL-EVENT-DISCONNECTED" or "CTRL-EVENT-DSCP-POLICY clear_all" events. + +- Control interface event message format to indicate wpa_supplicant started + a timer to wait until the unsolicited DSCP request from the AP. + + <3>CTRL-EVENT-DSCP-POLICY request_wait start + +- Control interface event message format to indicate timeout to receive the + unsolicited DSCP request. This event is expected only when an unsolicited + DSCP request is not received from the AP before timeout. + + <3>CTRL-EVENT-DSCP-POLICY request_wait end + +DSCP Response: +A QoS Management STA that enables DSCP Policy capability shall respond +with DSCP response on receipt of a successful DSCP request from its +associated AP. wpa_supplicant sends DSCP policy response based on the +control interface command received from the user is as below: + +DSCP_RESP <[reset]>/<[solicited] [policy_id=1 status=0...]> [more] + +DSCP Query: +DSCP Policy Query enables a STA to query its associated AP for DSCP +policies applicable to the STA. Currently, this includes support to send +a wildcard DSCP query or a DSCP query with a single domain name +attribute. The command format for the DSCP query command is as follows: +DSCP_QUERY <wildcard>/<domain_name=<string>> diff --git a/contrib/wpa/wpa_supplicant/ap.c b/contrib/wpa/wpa_supplicant/ap.c index cdf0ed5c7b5f..6a0a69e68ee6 100644 --- a/contrib/wpa/wpa_supplicant/ap.c +++ b/contrib/wpa/wpa_supplicant/ap.c @@ -84,6 +84,11 @@ static void wpas_conf_ap_vht(struct wpa_supplicant *wpa_s, /* Use the maximum oper channel width if it's given. */ if (ssid->max_oper_chwidth) hostapd_set_oper_chwidth(conf, ssid->max_oper_chwidth); + if (hostapd_get_oper_chwidth(conf)) + ieee80211_freq_to_channel_ext(ssid->frequency, 0, + hostapd_get_oper_chwidth(conf), + &conf->op_class, + &conf->channel); if (hostapd_get_oper_chwidth(conf) == CHANWIDTH_80P80MHZ) { ieee80211_freq_to_chan(ssid->vht_center_freq2, @@ -191,19 +196,79 @@ wpa_supplicant_find_hw_mode(struct wpa_supplicant *wpa_s, } +#ifdef CONFIG_P2P + +static int get_max_oper_chwidth_6ghz(int chwidth) +{ + switch (chwidth) { + case CHANWIDTH_USE_HT: + return 20; + case CHANWIDTH_40MHZ_6GHZ: + return 40; + case CHANWIDTH_80MHZ: + return 80; + case CHANWIDTH_80P80MHZ: + case CHANWIDTH_160MHZ: + return 160; + default: + return 0; + } +} + + +static void wpas_conf_ap_he_6ghz(struct wpa_supplicant *wpa_s, + struct hostapd_hw_modes *mode, + struct wpa_ssid *ssid, + struct hostapd_config *conf) +{ + bool is_chanwidth_40_80, is_chanwidth_160; + int he_chanwidth; + + he_chanwidth = + mode->he_capab[wpas_mode_to_ieee80211_mode( + ssid->mode)].phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX]; + is_chanwidth_40_80 = he_chanwidth & + HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G; + is_chanwidth_160 = he_chanwidth & + HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G; + + wpa_printf(MSG_DEBUG, + "Enable HE support (p2p_group=%d he_chwidth_cap=%d)", + ssid->p2p_group, he_chanwidth); + + if (mode->he_capab[wpas_mode_to_ieee80211_mode( + ssid->mode)].he_supported && + ssid->he) + conf->ieee80211ax = 1; + + if (is_chanwidth_40_80 && ssid->p2p_group && + get_max_oper_chwidth_6ghz(ssid->max_oper_chwidth) >= 40) { + conf->secondary_channel = + wpas_p2p_get_sec_channel_offset_40mhz( + wpa_s, mode, conf->channel); + wpa_printf(MSG_DEBUG, + "Secondary channel offset %d for P2P group", + conf->secondary_channel); + if (ssid->max_oper_chwidth == CHANWIDTH_40MHZ_6GHZ) + ssid->max_oper_chwidth = CHANWIDTH_USE_HT; + } + + if ((is_chanwidth_40_80 || is_chanwidth_160) && ssid->p2p_group && + get_max_oper_chwidth_6ghz(ssid->max_oper_chwidth) >= 80) + wpas_conf_ap_vht(wpa_s, ssid, conf, mode); +} + +#endif /* CONFIG_P2P */ + + int wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct hostapd_config *conf) { conf->hw_mode = ieee80211_freq_to_channel_ext(ssid->frequency, 0, - ssid->max_oper_chwidth, + CHANWIDTH_USE_HT, &conf->op_class, &conf->channel); - /* ssid->max_oper_chwidth is not valid in all cases, so fall back to the - * less specific mechanism, if needed, at least for now */ - if (conf->hw_mode == NUM_HOSTAPD_MODES) - 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); @@ -224,7 +289,8 @@ int wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s, "Determining HT/VHT options based on driver capabilities (freq=%u chan=%u)", ssid->frequency, conf->channel); - mode = wpa_supplicant_find_hw_mode(wpa_s, conf->hw_mode); + mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, + conf->hw_mode, is_6ghz_freq(ssid->frequency)); /* May drop to IEEE 802.11b if the driver does not support IEEE * 802.11g */ @@ -255,7 +321,12 @@ int wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s, no_ht = 1; } - if (!no_ht && mode && mode->ht_capab) { + if (mode && is_6ghz_freq(ssid->frequency) && + conf->hw_mode == HOSTAPD_MODE_IEEE80211A) { +#ifdef CONFIG_P2P + wpas_conf_ap_he_6ghz(wpa_s, mode, ssid, conf); +#endif /* CONFIG_P2P */ + } else if (!no_ht && mode && mode->ht_capab) { wpa_printf(MSG_DEBUG, "Enable HT support (p2p_group=%d 11a=%d ht40_hw_capab=%d ssid->ht40=%d)", ssid->p2p_group, @@ -279,8 +350,8 @@ int wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s, HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && ssid->ht40) { conf->secondary_channel = - wpas_p2p_get_ht40_mode(wpa_s, mode, - conf->channel); + wpas_p2p_get_sec_channel_offset_40mhz( + wpa_s, mode, conf->channel); wpa_printf(MSG_DEBUG, "HT secondary channel offset %d for P2P group", conf->secondary_channel); @@ -530,7 +601,10 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, bss->sae_passwords = pw; } - bss->sae_pwe = wpa_s->conf->sae_pwe; + if (ssid->sae_pwe != DEFAULT_SAE_PWE) + bss->sae_pwe = ssid->sae_pwe; + else + bss->sae_pwe = wpa_s->conf->sae_pwe; #endif /* CONFIG_SAE */ if (wpa_s->conf->go_interworking) { @@ -698,6 +772,10 @@ no_wps: bss->vendor_elements = wpabuf_dup(wpa_s->conf->ap_vendor_elements); } + if (wpa_s->conf->ap_assocresp_elements) { + bss->assocresp_elements = + wpabuf_dup(wpa_s->conf->ap_assocresp_elements); + } bss->ftm_responder = wpa_s->conf->ftm_responder; bss->ftm_initiator = wpa_s->conf->ftm_initiator; @@ -1748,6 +1826,32 @@ int wpas_ap_pmksa_cache_add_external(struct wpa_supplicant *wpa_s, char *cmd) #endif /* CONFIG_MESH */ #endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ + +int wpas_ap_update_beacon(struct wpa_supplicant *wpa_s) +{ + struct hostapd_data *hapd; + + if (!wpa_s->ap_iface) + return -1; + hapd = wpa_s->ap_iface->bss[0]; + + wpabuf_free(hapd->conf->assocresp_elements); + hapd->conf->assocresp_elements = NULL; + if (wpa_s->conf->ap_assocresp_elements) { + hapd->conf->assocresp_elements = + wpabuf_dup(wpa_s->conf->ap_assocresp_elements); + } + + wpabuf_free(hapd->conf->vendor_elements); + hapd->conf->vendor_elements = NULL; + if (wpa_s->conf->ap_vendor_elements) { + hapd->conf->vendor_elements = + wpabuf_dup(wpa_s->conf->ap_vendor_elements); + } + + return ieee802_11_set_beacon(hapd); +} + #endif /* CONFIG_CTRL_IFACE */ diff --git a/contrib/wpa/wpa_supplicant/ap.h b/contrib/wpa/wpa_supplicant/ap.h index 6c6e94cdf6a2..7bc1b781e3ac 100644 --- a/contrib/wpa/wpa_supplicant/ap.h +++ b/contrib/wpa/wpa_supplicant/ap.h @@ -88,6 +88,7 @@ void wpas_ap_pmksa_cache_flush(struct wpa_supplicant *wpa_s); int wpas_ap_pmksa_cache_list_mesh(struct wpa_supplicant *wpa_s, const u8 *addr, char *buf, size_t len); int wpas_ap_pmksa_cache_add_external(struct wpa_supplicant *wpa_s, char *cmd); +int wpas_ap_update_beacon(struct wpa_supplicant *wpa_s); void wpas_ap_event_dfs_radar_detected(struct wpa_supplicant *wpa_s, struct dfs_event *radar); diff --git a/contrib/wpa/wpa_supplicant/config.c b/contrib/wpa/wpa_supplicant/config.c index e8e9fd432618..c5177d915524 100644 --- a/contrib/wpa/wpa_supplicant/config.c +++ b/contrib/wpa/wpa_supplicant/config.c @@ -2951,6 +2951,7 @@ void wpa_config_free(struct wpa_config *config) os_free(config->ext_password_backend); os_free(config->sae_groups); wpabuf_free(config->ap_vendor_elements); + wpabuf_free(config->ap_assocresp_elements); os_free(config->osu_dir); os_free(config->bgscan); os_free(config->wowlan_triggers); @@ -3139,6 +3140,7 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid) #endif /* CONFIG_VHT_OVERRIDES */ ssid->proactive_key_caching = -1; ssid->ieee80211w = MGMT_FRAME_PROTECTION_DEFAULT; + ssid->sae_pwe = DEFAULT_SAE_PWE; #ifdef CONFIG_MACSEC ssid->mka_priority = DEFAULT_PRIO_NOT_KEY_SERVER; #endif /* CONFIG_MACSEC */ @@ -4909,33 +4911,46 @@ static int wpa_config_process_ap_vendor_elements( struct wpa_config *config, int line, const char *pos) { struct wpabuf *tmp; - int len = os_strlen(pos) / 2; - u8 *p; - if (!len) { + if (!*pos) { + wpabuf_free(config->ap_vendor_elements); + config->ap_vendor_elements = NULL; + return 0; + } + + tmp = wpabuf_parse_bin(pos); + if (!tmp) { wpa_printf(MSG_ERROR, "Line %d: invalid ap_vendor_elements", line); return -1; } + wpabuf_free(config->ap_vendor_elements); + config->ap_vendor_elements = tmp; - tmp = wpabuf_alloc(len); - if (tmp) { - p = wpabuf_put(tmp, len); + return 0; +} - if (hexstr2bin(pos, p, len)) { - wpa_printf(MSG_ERROR, "Line %d: invalid " - "ap_vendor_elements", line); - wpabuf_free(tmp); - return -1; - } - wpabuf_free(config->ap_vendor_elements); - config->ap_vendor_elements = tmp; - } else { - wpa_printf(MSG_ERROR, "Cannot allocate memory for " - "ap_vendor_elements"); +static int wpa_config_process_ap_assocresp_elements( + const struct global_parse_data *data, + struct wpa_config *config, int line, const char *pos) +{ + struct wpabuf *tmp; + + if (!*pos) { + wpabuf_free(config->ap_assocresp_elements); + config->ap_assocresp_elements = NULL; + return 0; + } + + tmp = wpabuf_parse_bin(pos); + if (!tmp) { + wpa_printf(MSG_ERROR, "Line %d: invalid ap_assocresp_elements", + line); return -1; } + wpabuf_free(config->ap_assocresp_elements); + config->ap_assocresp_elements = tmp; return 0; } @@ -5155,6 +5170,7 @@ static const struct global_parse_data global_fields[] = { { INT_RANGE(sae_pmkid_in_assoc, 0, 1), 0 }, { INT(dtim_period), 0 }, { INT(beacon_int), 0 }, + { FUNC(ap_assocresp_elements), 0 }, { FUNC(ap_vendor_elements), 0 }, { INT_RANGE(ignore_old_scan_res, 0, 1), 0 }, { FUNC(freq_list), 0 }, diff --git a/contrib/wpa/wpa_supplicant/config.h b/contrib/wpa/wpa_supplicant/config.h index 68679c6e380a..0320d9eebb57 100644 --- a/contrib/wpa/wpa_supplicant/config.h +++ b/contrib/wpa/wpa_supplicant/config.h @@ -1242,6 +1242,17 @@ struct wpa_config { struct wpabuf *ap_vendor_elements; /** + * ap_assocresp_elements: Vendor specific elements for (Re)Association + * Response frames + * + * This parameter can be used to define additional vendor specific + * elements for (Re)Association Response frames in AP/P2P GO mode. The + * format for these element(s) is a hexdump of the raw information + * elements (id+len+payload for one or more elements). + */ + struct wpabuf *ap_assocresp_elements; + + /** * ignore_old_scan_res - Ignore scan results older than request * * The driver may have a cache of scan results that makes it return diff --git a/contrib/wpa/wpa_supplicant/config_file.c b/contrib/wpa/wpa_supplicant/config_file.c index a535e3f08aad..54fb72d8c1f7 100644 --- a/contrib/wpa/wpa_supplicant/config_file.c +++ b/contrib/wpa/wpa_supplicant/config_file.c @@ -675,6 +675,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) INT(mem_only_psk); STR(sae_password); STR(sae_password_id); + write_int(f, "sae_pwe", ssid->sae_pwe, DEFAULT_SAE_PWE); write_proto(f, ssid); write_key_mgmt(f, ssid); INT_DEF(bg_scan_period, DEFAULT_BG_SCAN_PERIOD); @@ -1364,6 +1365,18 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) } } + if (config->ap_assocresp_elements) { + int i, len = wpabuf_len(config->ap_assocresp_elements); + const u8 *p = wpabuf_head_u8(config->ap_assocresp_elements); + + if (len > 0) { + fprintf(f, "ap_assocresp_elements="); + for (i = 0; i < len; i++) + fprintf(f, "%02x", *p++); + fprintf(f, "\n"); + } + } + if (config->ignore_old_scan_res) fprintf(f, "ignore_old_scan_res=%d\n", config->ignore_old_scan_res); diff --git a/contrib/wpa/wpa_supplicant/config_none.c b/contrib/wpa/wpa_supplicant/config_none.c index 2aac28fa3d17..0bc977e3961b 100644 --- a/contrib/wpa/wpa_supplicant/config_none.c +++ b/contrib/wpa/wpa_supplicant/config_none.c @@ -5,7 +5,7 @@ * This software may be distributed under the terms of the BSD license. * See README for more details. * - * This file implements dummy example of a configuration backend. None of the + * This file implements stub example of a configuration backend. None of the * functions are actually implemented so this can be used as a simple * compilation test or a starting point for a new configuration backend. */ diff --git a/contrib/wpa/wpa_supplicant/config_ssid.h b/contrib/wpa/wpa_supplicant/config_ssid.h index 3f7b31480765..339eead1c333 100644 --- a/contrib/wpa/wpa_supplicant/config_ssid.h +++ b/contrib/wpa/wpa_supplicant/config_ssid.h @@ -46,6 +46,9 @@ #define DEFAULT_USER_SELECTED_SIM 1 #define DEFAULT_MAX_OPER_CHWIDTH -1 +/* Consider global sae_pwe for SAE mechanism for PWE derivation */ +#define DEFAULT_SAE_PWE 4 + struct psk_list_entry { struct dl_list list; u8 addr[ETH_ALEN]; @@ -1156,6 +1159,19 @@ struct wpa_ssid { * configuration. */ bool was_recently_reconfigured; + + /** + * sae_pwe - SAE mechanism for PWE derivation + * + * Internally, special value 4 (DEFAULT_SAE_PWE) is used to indicate + * that the parameter is not set and the global sae_pwe value needs to + * be considered. + * + * 0 = hunting-and-pecking loop only + * 1 = hash-to-element only + * 2 = both hunting-and-pecking loop and hash-to-element enabled + */ + int sae_pwe; }; #endif /* CONFIG_SSID_H */ diff --git a/contrib/wpa/wpa_supplicant/ctrl_iface.c b/contrib/wpa/wpa_supplicant/ctrl_iface.c index 8a6a829b6665..9dc17f5eef85 100644 --- a/contrib/wpa/wpa_supplicant/ctrl_iface.c +++ b/contrib/wpa/wpa_supplicant/ctrl_iface.c @@ -568,10 +568,10 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, (wps_version_number & 0xf0) >> 4, wps_version_number & 0x0f); } - } else if (os_strcasecmp(cmd, "wps_testing_dummy_cred") == 0) { - wps_testing_dummy_cred = atoi(value); - wpa_printf(MSG_DEBUG, "WPS: Testing - dummy_cred=%d", - wps_testing_dummy_cred); + } else if (os_strcasecmp(cmd, "wps_testing_stub_cred") == 0) { + wps_testing_stub_cred = atoi(value); + wpa_printf(MSG_DEBUG, "WPS: Testing - stub_cred=%d", + wps_testing_stub_cred); } else if (os_strcasecmp(cmd, "wps_corrupt_pkhash") == 0) { wps_corrupt_pkhash = atoi(value); wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d", @@ -830,6 +830,10 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, wpa_s->sae_commit_override = wpabuf_parse_bin(value); } else if (os_strcasecmp(cmd, "driver_signal_override") == 0) { ret = wpas_ctrl_iface_set_dso(wpa_s, value); + } else if (os_strcasecmp(cmd, "disable_scs_support") == 0) { + wpa_s->disable_scs_support = !!atoi(value); + } else if (os_strcasecmp(cmd, "disable_mscs_support") == 0) { + wpa_s->disable_mscs_support = !!atoi(value); #ifdef CONFIG_DPP } else if (os_strcasecmp(cmd, "dpp_config_obj_override") == 0) { os_free(wpa_s->dpp_config_obj_override); @@ -918,6 +922,8 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, return -1; wnm_set_coloc_intf_elems(wpa_s, elems); #endif /* CONFIG_WNM */ + } else if (os_strcasecmp(cmd, "enable_dscp_policy_capa") == 0) { + wpa_s->enable_dscp_policy_capa = !!atoi(value); } else { value[-1] = '='; ret = wpa_config_process_global(wpa_s->conf, cmd, -1); @@ -5918,7 +5924,7 @@ static struct p2ps_provision * p2p_parse_asp_provision_cmd(const char *cmd) for (i = 0; p2ps_prov->cpt_priority[i]; i++) p2ps_prov->cpt_mask |= p2ps_prov->cpt_priority[i]; - /* force conncap with tstCap (no sanity checks) */ + /* force conncap with tstCap (no validity checks) */ pos = os_strstr(cmd, "tstCap="); if (pos) { role = strtol(pos + 7, NULL, 16); @@ -6122,6 +6128,9 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, if (max_oper_chwidth < 0) return -1; + if (allow_6ghz && chwidth == 40) + max_oper_chwidth = CHANWIDTH_40MHZ_6GHZ; + pos2 = os_strstr(pos, " ssid="); if (pos2) { char *end; @@ -6775,6 +6784,9 @@ static int p2p_ctrl_invite_persistent(struct wpa_supplicant *wpa_s, char *cmd) allow_6ghz = os_strstr(cmd, " allow_6ghz") != NULL; + if (allow_6ghz && chwidth == 40) + max_oper_chwidth = CHANWIDTH_40MHZ_6GHZ; + return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, freq2, ht40, vht, max_oper_chwidth, pref_freq, he, edmg, allow_6ghz); @@ -6918,6 +6930,13 @@ static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd) if (max_oper_chwidth < 0) return -1; + if (allow_6ghz && chwidth == 40) + max_oper_chwidth = CHANWIDTH_40MHZ_6GHZ; + + /* Allow DFS to be used for Autonomous GO */ + wpa_s->p2p_go_allow_dfs = !!(wpa_s->drv_flags & + WPA_DRIVER_FLAGS_DFS_OFFLOAD); + if (group_id >= 0) return p2p_ctrl_group_add_persistent(wpa_s, group_id, freq, freq2, ht40, vht, @@ -8428,7 +8447,7 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) #ifdef CONFIG_WPS_TESTING wps_version_number = 0x20; - wps_testing_dummy_cred = 0; + wps_testing_stub_cred = 0; wps_corrupt_pkhash = 0; wps_force_auth_types_in_use = 0; wps_force_encr_types_in_use = 0; @@ -8551,6 +8570,9 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) wpabuf_free(wpa_s->rsnxe_override_eapol); wpa_s->rsnxe_override_eapol = NULL; wpas_clear_driver_signal_override(wpa_s); + wpa_s->disable_scs_support = 0; + wpa_s->disable_mscs_support = 0; + wpa_s->enable_dscp_policy_capa = 0; wpa_s->oci_freq_override_eapol = 0; wpa_s->oci_freq_override_saquery_req = 0; wpa_s->oci_freq_override_saquery_resp = 0; @@ -10602,6 +10624,8 @@ static int wpas_ctrl_iface_pmksa_add(struct wpa_supplicant *wpa_s, if (sscanf(pos, "%d %d %d %d", &reauth_time, &expiration, &entry->akmp, &entry->opportunistic) != 4) goto fail; + if (reauth_time > expiration) + goto fail; for (i = 0; i < 4; i++) { pos = os_strchr(pos, ' '); if (!pos) { @@ -10887,6 +10911,581 @@ static int wpas_ctrl_iface_pasn_deauthenticate(struct wpa_supplicant *wpa_s, #endif /* CONFIG_PASN */ +static int set_type4_frame_classifier(const char *cmd, + struct type4_params *param) +{ + const char *pos, *end; + u8 classifier_mask = 0; + int ret; + char addr[INET6_ADDRSTRLEN]; + size_t alen; + + if (os_strstr(cmd, "ip_version=ipv4")) { + param->ip_version = IPV4; + } else if (os_strstr(cmd, "ip_version=ipv6")) { + param->ip_version = IPV6; + } else { + wpa_printf(MSG_ERROR, "IP version missing/invalid"); + return -1; + } + + classifier_mask |= BIT(0); + + pos = os_strstr(cmd, "src_ip="); + if (pos) { + pos += 7; + end = os_strchr(pos, ' '); + if (!end) + end = pos + os_strlen(pos); + + alen = end - pos; + if (alen >= INET6_ADDRSTRLEN) + return -1; + os_memcpy(addr, pos, alen); + addr[alen] = '\0'; + if (param->ip_version == IPV4) + ret = inet_pton(AF_INET, addr, + ¶m->ip_params.v4.src_ip); + else + ret = inet_pton(AF_INET6, addr, + ¶m->ip_params.v6.src_ip); + + if (ret != 1) { + wpa_printf(MSG_ERROR, + "Error converting src IP address to binary ret=%d", + ret); + return -1; + } + + classifier_mask |= BIT(1); + } + + pos = os_strstr(cmd, "dst_ip="); + if (pos) { + pos += 7; + end = os_strchr(pos, ' '); + if (!end) + end = pos + os_strlen(pos); + + alen = end - pos; + if (alen >= INET6_ADDRSTRLEN) + return -1; + os_memcpy(addr, pos, alen); + addr[alen] = '\0'; + if (param->ip_version == IPV4) + ret = inet_pton(AF_INET, addr, + ¶m->ip_params.v4.dst_ip); + else + ret = inet_pton(AF_INET6, addr, + ¶m->ip_params.v6.dst_ip); + + if (ret != 1) { + wpa_printf(MSG_ERROR, + "Error converting dst IP address to binary ret=%d", + ret); + return -1; + } + + classifier_mask |= BIT(2); + } + + pos = os_strstr(cmd, "src_port="); + if (pos && atoi(pos + 9) > 0) { + if (param->ip_version == IPV4) + param->ip_params.v4.src_port = atoi(pos + 9); + else + param->ip_params.v6.src_port = atoi(pos + 9); + classifier_mask |= BIT(3); + } + + pos = os_strstr(cmd, "dst_port="); + if (pos && atoi(pos + 9) > 0) { + if (param->ip_version == IPV4) + param->ip_params.v4.dst_port = atoi(pos + 9); + else + param->ip_params.v6.dst_port = atoi(pos + 9); + classifier_mask |= BIT(4); + } + + pos = os_strstr(cmd, "dscp="); + if (pos && atoi(pos + 5) > 0) { + if (param->ip_version == IPV4) + param->ip_params.v4.dscp = atoi(pos + 5); + else + param->ip_params.v6.dscp = atoi(pos + 5); + classifier_mask |= BIT(5); + } + + if (param->ip_version == IPV4) { + pos = os_strstr(cmd, "protocol="); + if (pos) { + if (os_strstr(pos, "udp")) { + param->ip_params.v4.protocol = 17; + } else if (os_strstr(pos, "tcp")) { + param->ip_params.v4.protocol = 6; + } else if (os_strstr(pos, "esp")) { + param->ip_params.v4.protocol = 50; + } else { + wpa_printf(MSG_ERROR, "Invalid protocol"); + return -1; + } + classifier_mask |= BIT(6); + } + } else { + pos = os_strstr(cmd, "next_header="); + if (pos) { + if (os_strstr(pos, "udp")) { + param->ip_params.v6.next_header = 17; + } else if (os_strstr(pos, "tcp")) { + param->ip_params.v6.next_header = 6; + } else if (os_strstr(pos, "esp")) { + param->ip_params.v6.next_header = 50; + } else { + wpa_printf(MSG_ERROR, "Invalid next header"); + return -1; + } + + classifier_mask |= BIT(6); + } + + pos = os_strstr(cmd, "flow_label="); + if (pos) { + pos += 11; + end = os_strchr(pos, ' '); + if (!end) + end = pos + os_strlen(pos); + + if (end - pos != 6 || + hexstr2bin(pos, param->ip_params.v6.flow_label, + 3) || + param->ip_params.v6.flow_label[0] > 0x0F) { + wpa_printf(MSG_ERROR, "Invalid flow label"); + return -1; + } + + classifier_mask |= BIT(7); + } + } + + param->classifier_mask = classifier_mask; + return 0; +} + + +static int set_type10_frame_classifier(const char *cmd, + struct type10_params *param) +{ + const char *pos, *end; + size_t filter_len; + + pos = os_strstr(cmd, "prot_instance="); + if (!pos) { + wpa_printf(MSG_ERROR, "Protocol instance missing"); + return -1; + } + param->prot_instance = atoi(pos + 14); + + pos = os_strstr(cmd, "prot_number="); + if (!pos) { + wpa_printf(MSG_ERROR, "Protocol number missing"); + return -1; + } + if (os_strstr(pos, "udp")) { + param->prot_number = 17; + } else if (os_strstr(pos, "tcp")) { + param->prot_number = 6; + } else if (os_strstr(pos, "esp")) { + param->prot_number = 50; + } else { + wpa_printf(MSG_ERROR, "Invalid protocol number"); + return -1; + } + + pos = os_strstr(cmd, "filter_value="); + if (!pos) { + wpa_printf(MSG_ERROR, + "Classifier parameter filter_value missing"); + return -1; + } + + pos += 13; + end = os_strchr(pos, ' '); + if (!end) + end = pos + os_strlen(pos); + + filter_len = (end - pos) / 2; + param->filter_value = os_malloc(filter_len); + if (!param->filter_value) + return -1; + + if (hexstr2bin(pos, param->filter_value, filter_len)) { + wpa_printf(MSG_ERROR, "Invalid filter_value %s", pos); + goto free; + } + + pos = os_strstr(cmd, "filter_mask="); + if (!pos) { + wpa_printf(MSG_ERROR, + "Classifier parameter filter_mask missing"); + goto free; + } + + pos += 12; + end = os_strchr(pos, ' '); + if (!end) + end = pos + os_strlen(pos); + + if (filter_len != (size_t) (end - pos) / 2) { + wpa_printf(MSG_ERROR, + "Filter mask length mismatch expected=%zu received=%zu", + filter_len, (size_t) (end - pos) / 2); + goto free; + } + + param->filter_mask = os_malloc(filter_len); + if (!param->filter_mask) + goto free; + + if (hexstr2bin(pos, param->filter_mask, filter_len)) { + wpa_printf(MSG_ERROR, "Invalid filter mask %s", pos); + os_free(param->filter_mask); + param->filter_mask = NULL; + goto free; + } + + param->filter_len = filter_len; + return 0; +free: + os_free(param->filter_value); + param->filter_value = NULL; + return -1; +} + + +static int scs_parse_type4(struct tclas_element *elem, const char *pos) +{ + struct type4_params type4_param = { 0 }; + + if (set_type4_frame_classifier(pos, &type4_param) == -1) { + wpa_printf(MSG_ERROR, "Failed to set frame_classifier 4"); + return -1; + } + + os_memcpy(&elem->frame_classifier.type4_param, + &type4_param, sizeof(struct type4_params)); + return 0; +} + + +static int scs_parse_type10(struct tclas_element *elem, const char *pos) +{ + struct type10_params type10_param = { 0 }; + + if (set_type10_frame_classifier(pos, &type10_param) == -1) { + wpa_printf(MSG_ERROR, "Failed to set frame_classifier 10"); + return -1; + } + + os_memcpy(&elem->frame_classifier.type10_param, + &type10_param, sizeof(struct type10_params)); + return 0; +} + + +static int wpas_ctrl_iface_configure_scs(struct wpa_supplicant *wpa_s, + char *cmd) +{ + char *pos1, *pos; + struct scs_robust_av_data *scs_data = &wpa_s->scs_robust_av_req; + struct scs_desc_elem desc_elem = { 0 }; + int val; + unsigned int num_scs_desc = 0; + + if (wpa_s->ongoing_scs_req) { + wpa_printf(MSG_ERROR, "%s: SCS Request already in queue", + __func__); + return -1; + } + + /** + * format: + * [scs_id=<decimal number>] <add|remove|change> [scs_up=<0-7>] + * [classifier_type=<4|10>] + * [classifier params based on classifier type] + * [tclas_processing=<0|1>] [scs_id=<decimal number>] ... + */ + pos1 = os_strstr(cmd, "scs_id="); + if (!pos1) { + wpa_printf(MSG_ERROR, "SCSID not present"); + return -1; + } + + free_up_scs_desc(scs_data); + + while (pos1) { + struct scs_desc_elem *n1; + struct active_scs_elem *active_scs_desc; + char *next_scs_desc; + unsigned int num_tclas_elem = 0; + bool scsid_active = false; + + desc_elem.scs_id = atoi(pos1 + 7); + pos1 += 7; + + next_scs_desc = os_strstr(pos1, "scs_id="); + if (next_scs_desc) { + char temp[20]; + + os_snprintf(temp, sizeof(temp), "scs_id=%d ", + desc_elem.scs_id); + if (os_strstr(next_scs_desc, temp)) { + wpa_printf(MSG_ERROR, + "Multiple SCS descriptors configured with same SCSID(=%d)", + desc_elem.scs_id); + goto free_scs_desc; + } + pos1[next_scs_desc - pos1 - 1] = '\0'; + } + + dl_list_for_each(active_scs_desc, &wpa_s->active_scs_ids, + struct active_scs_elem, list) { + if (desc_elem.scs_id == active_scs_desc->scs_id) { + scsid_active = true; + break; + } + } + + if (os_strstr(pos1, "add ")) { + desc_elem.request_type = SCS_REQ_ADD; + if (scsid_active) { + wpa_printf(MSG_ERROR, "SCSID %d already active", + desc_elem.scs_id); + return -1; + } + } else if (os_strstr(pos1, "remove")) { + desc_elem.request_type = SCS_REQ_REMOVE; + if (!scsid_active) { + wpa_printf(MSG_ERROR, "SCSID %d not active", + desc_elem.scs_id); + return -1; + } + goto scs_desc_end; + } else if (os_strstr(pos1, "change ")) { + desc_elem.request_type = SCS_REQ_CHANGE; + if (!scsid_active) { + wpa_printf(MSG_ERROR, "SCSID %d not active", + desc_elem.scs_id); + return -1; + } + } else { + wpa_printf(MSG_ERROR, "SCS Request type invalid"); + goto free_scs_desc; + } + + pos1 = os_strstr(pos1, "scs_up="); + if (!pos1) { + wpa_printf(MSG_ERROR, + "Intra-Access user priority not present"); + goto free_scs_desc; + } + + val = atoi(pos1 + 7); + if (val < 0 || val > 7) { + wpa_printf(MSG_ERROR, + "Intra-Access user priority invalid %d", + val); + goto free_scs_desc; + } + + desc_elem.intra_access_priority = val; + desc_elem.scs_up_avail = true; + + pos = os_strstr(pos1, "classifier_type="); + if (!pos) { + wpa_printf(MSG_ERROR, "classifier type empty"); + goto free_scs_desc; + } + + while (pos) { + struct tclas_element elem = { 0 }, *n; + char *next_tclas_elem; + + val = atoi(pos + 16); + if (val != 4 && val != 10) { + wpa_printf(MSG_ERROR, + "classifier type invalid %d", val); + goto free_scs_desc; + } + + elem.classifier_type = val; + pos += 16; + + next_tclas_elem = os_strstr(pos, "classifier_type="); + if (next_tclas_elem) { + pos1 = next_tclas_elem; + pos[next_tclas_elem - pos - 1] = '\0'; + } + + switch (val) { + case 4: + if (scs_parse_type4(&elem, pos) < 0) + goto free_scs_desc; + break; + case 10: + if (scs_parse_type10(&elem, pos) < 0) + goto free_scs_desc; + break; + } + + n = os_realloc(desc_elem.tclas_elems, + (num_tclas_elem + 1) * sizeof(elem)); + if (!n) + goto free_scs_desc; + + desc_elem.tclas_elems = n; + os_memcpy((u8 *) desc_elem.tclas_elems + + num_tclas_elem * sizeof(elem), + &elem, sizeof(elem)); + num_tclas_elem++; + desc_elem.num_tclas_elem = num_tclas_elem; + pos = next_tclas_elem; + } + + if (desc_elem.num_tclas_elem > 1) { + pos1 = os_strstr(pos1, "tclas_processing="); + if (!pos1) { + wpa_printf(MSG_ERROR, "tclas_processing empty"); + goto free_scs_desc; + } + + val = atoi(pos1 + 17); + if (val != 0 && val != 1) { + wpa_printf(MSG_ERROR, + "tclas_processing invalid"); + goto free_scs_desc; + } + + desc_elem.tclas_processing = val; + } + +scs_desc_end: + n1 = os_realloc(scs_data->scs_desc_elems, (num_scs_desc + 1) * + sizeof(struct scs_desc_elem)); + if (!n1) + goto free_scs_desc; + + scs_data->scs_desc_elems = n1; + os_memcpy((u8 *) scs_data->scs_desc_elems + num_scs_desc * + sizeof(desc_elem), &desc_elem, sizeof(desc_elem)); + num_scs_desc++; + scs_data->num_scs_desc = num_scs_desc; + pos1 = next_scs_desc; + os_memset(&desc_elem, 0, sizeof(desc_elem)); + } + + return wpas_send_scs_req(wpa_s); + +free_scs_desc: + free_up_tclas_elem(&desc_elem); + free_up_scs_desc(scs_data); + return -1; +} + + +static int wpas_ctrl_iface_send_dscp_resp(struct wpa_supplicant *wpa_s, + const char *cmd) +{ + char *pos; + struct dscp_policy_status *policy = NULL, *n; + int num_policies = 0, ret = -1; + struct dscp_resp_data resp_data; + + /* + * format: + * <[reset]>/<[solicited] [policy_id=1 status=0...]> [more] + */ + + os_memset(&resp_data, 0, sizeof(resp_data)); + + resp_data.more = os_strstr(cmd, "more") != NULL; + + if (os_strstr(cmd, "reset")) { + resp_data.reset = true; + resp_data.solicited = false; + goto send_resp; + } + + resp_data.solicited = os_strstr(cmd, "solicited") != NULL; + + pos = os_strstr(cmd, "policy_id="); + while (pos) { + n = os_realloc(policy, (num_policies + 1) * sizeof(*policy)); + if (!n) + goto fail; + + policy = n; + pos += 10; + policy[num_policies].id = atoi(pos); + if (policy[num_policies].id == 0) { + wpa_printf(MSG_ERROR, "DSCP: Invalid policy id"); + goto fail; + } + + pos = os_strstr(pos, "status="); + if (!pos) { + wpa_printf(MSG_ERROR, + "DSCP: Status is not found for a policy"); + goto fail; + } + + pos += 7; + policy[num_policies].status = atoi(pos); + num_policies++; + + pos = os_strstr(pos, "policy_id"); + } + + resp_data.policy = policy; + resp_data.num_policies = num_policies; +send_resp: + ret = wpas_send_dscp_response(wpa_s, &resp_data); + if (ret) + wpa_printf(MSG_ERROR, "DSCP: Failed to send DSCP response"); +fail: + os_free(policy); + return ret; +} + + +static int wpas_ctrl_iface_send_dscp_query(struct wpa_supplicant *wpa_s, + const char *cmd) +{ + char *pos; + + /* + * format: + * Wildcard DSCP query + * <wildcard> + * + * DSCP query with a domain name attribute: + * [domain_name=<string>] + */ + + if (os_strstr(cmd, "wildcard")) { + wpa_printf(MSG_DEBUG, "QM: Send wildcard DSCP policy query"); + return wpas_send_dscp_query(wpa_s, NULL, 0); + } + + pos = os_strstr(cmd, "domain_name="); + if (!pos || !os_strlen(pos + 12)) { + wpa_printf(MSG_ERROR, "QM: Domain name not preset"); + return -1; + } + + return wpas_send_dscp_query(wpa_s, pos + 12, os_strlen(pos + 12)); +} + + char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, char *buf, size_t *resp_len) { @@ -11458,6 +12057,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strcmp(buf, "STOP_AP") == 0) { if (wpas_ap_stop_ap(wpa_s)) reply_len = -1; + } else if (os_strcmp(buf, "UPDATE_BEACON") == 0) { + if (wpas_ap_update_beacon(wpa_s)) + reply_len = -1; #endif /* CONFIG_AP */ } else if (os_strcmp(buf, "SUSPEND") == 0) { wpas_notify_suspend(wpa_s->global); @@ -11812,6 +12414,15 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, if (wpas_ctrl_iface_pasn_deauthenticate(wpa_s, buf + 12) < 0) reply_len = -1; #endif /* CONFIG_PASN */ + } else if (os_strncmp(buf, "SCS ", 4) == 0) { + if (wpas_ctrl_iface_configure_scs(wpa_s, buf + 4)) + reply_len = -1; + } else if (os_strncmp(buf, "DSCP_RESP ", 10) == 0) { + if (wpas_ctrl_iface_send_dscp_resp(wpa_s, buf + 10)) + reply_len = -1; + } else if (os_strncmp(buf, "DSCP_QUERY ", 11) == 0) { + if (wpas_ctrl_iface_send_dscp_query(wpa_s, buf + 11)) + reply_len = -1; } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.sgml b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.sgml index 144654aac684..e4a83698393a 100644 --- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.sgml +++ b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.sgml @@ -46,7 +46,7 @@ can be used to improve the network security, but even that has inherited security issues due to the use of WEP for encryption. Wi-Fi Protected Access and IEEE 802.11i amendment to the wireless LAN standard introduce - a much improvement mechanism for securing wireless networks. IEEE 802.11i + a much improved mechanism for securing wireless networks. IEEE 802.11i enabled networks that are using CCMP (encryption mechanism based on strong cryptographic algorithm AES) can finally be called secure used for applications which require efficient protection against unauthorized diff --git a/contrib/wpa/wpa_supplicant/eapol_test.py b/contrib/wpa/wpa_supplicant/eapol_test.py index 734428d29e66..88c83f343597 100755 --- a/contrib/wpa/wpa_supplicant/eapol_test.py +++ b/contrib/wpa/wpa_supplicant/eapol_test.py @@ -72,7 +72,7 @@ class eapol_test: break return None -def run(ifname, count, no_fast_reauth, res): +def run(ifname, count, no_fast_reauth, res, conf): et = eapol_test(ifname) et.request("AP_SCAN 0") @@ -81,14 +81,20 @@ def run(ifname, count, no_fast_reauth, res): else: et.request("SET fast_reauth 1") id = et.add_network() - et.set_network(id, "key_mgmt", "IEEE8021X") - et.set_network(id, "eapol_flags", "0") - et.set_network(id, "eap", "TLS") - et.set_network_quoted(id, "identity", "user") - et.set_network_quoted(id, "ca_cert", 'ca.pem') - et.set_network_quoted(id, "client_cert", 'client.pem') - et.set_network_quoted(id, "private_key", 'client.key') - et.set_network_quoted(id, "private_key_passwd", 'whatever') + + if len(conf): + for item in conf: + et.set_network(id, item, conf[item]) + else: + et.set_network(id, "key_mgmt", "IEEE8021X") + et.set_network(id, "eapol_flags", "0") + et.set_network(id, "eap", "TLS") + et.set_network_quoted(id, "identity", "user") + et.set_network_quoted(id, "ca_cert", 'ca.pem') + et.set_network_quoted(id, "client_cert", 'client.pem') + et.set_network_quoted(id, "private_key", 'client.key') + et.set_network_quoted(id, "private_key_passwd", 'whatever') + et.set_network(id, "disabled", "0") fail = False @@ -114,6 +120,7 @@ def main(): parser.add_argument('--no-fast-reauth', action='store_true', dest='no_fast_reauth', help='disable TLS session resumption') + parser.add_argument('--conf', help='file of network conf items') args = parser.parse_args() num = int(args.num) @@ -122,12 +129,22 @@ def main(): global wpas_ctrl wpas_ctrl = args.ctrl + conf = {} + if args.conf: + f = open(args.conf, "r") + for line in f: + confitem = line.split("=") + if len(confitem) == 2: + conf[confitem[0].strip()] = confitem[1].strip() + f.close() + t = {} res = {} for i in range(num): res[i] = Queue.Queue() t[i] = threading.Thread(target=run, args=(str(i), iter, - args.no_fast_reauth, res[i])) + args.no_fast_reauth, res[i], + conf)) for i in range(num): t[i].start() for i in range(num): diff --git a/contrib/wpa/wpa_supplicant/events.c b/contrib/wpa/wpa_supplicant/events.c index a565e658f33d..5f5c50ba9754 100644 --- a/contrib/wpa/wpa_supplicant/events.c +++ b/contrib/wpa/wpa_supplicant/events.c @@ -356,9 +356,14 @@ static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s) struct wpa_ie_data ie; int pmksa_set = -1; size_t i; + struct rsn_pmksa_cache_entry *cur_pmksa; - /* Start with assumption of no PMKSA cache entry match */ - pmksa_cache_clear_current(wpa_s->wpa); + /* Start with assumption of no PMKSA cache entry match for cases other + * than SAE. In particular, this is needed to generate the PMKSA cache + * entries for Suite B cases with driver-based roaming indication. */ + cur_pmksa = pmksa_cache_get_current(wpa_s->wpa); + if (cur_pmksa && !wpa_key_mgmt_sae(cur_pmksa->akmp)) + pmksa_cache_clear_current(wpa_s->wpa); if (wpa_sm_parse_own_wpa_ie(wpa_s->wpa, &ie) < 0 || ie.pmkid == NULL) @@ -2680,6 +2685,205 @@ static int wpas_fst_update_mbie(struct wpa_supplicant *wpa_s, #endif /* CONFIG_FST */ +static int wpa_supplicant_use_own_rsne_params(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + int sel; + const u8 *p; + int l, len; + bool found = false; + struct wpa_ie_data ie; + struct wpa_ssid *ssid = wpa_s->current_ssid; + struct wpa_bss *bss = wpa_s->current_bss; + int pmf; + + if (!ssid) + return 0; + + p = data->assoc_info.req_ies; + l = data->assoc_info.req_ies_len; + + while (p && l >= 2) { + len = p[1] + 2; + if (len > l) { + wpa_hexdump(MSG_DEBUG, "Truncated IE in assoc_info", + p, l); + break; + } + if (((p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 && + (os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0)) || + (p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 4 && + (os_memcmp(&p[2], "\x50\x6F\x9A\x12", 4) == 0)) || + (p[0] == WLAN_EID_RSN && p[1] >= 2))) { + found = true; + break; + } + l -= len; + p += len; + } + + if (!found || wpa_parse_wpa_ie(p, len, &ie) < 0) + return 0; + + wpa_hexdump(MSG_DEBUG, + "WPA: Update cipher suite selection based on IEs in driver-generated WPA/RSNE in AssocReq", + p, l); + + /* Update proto from (Re)Association Request frame info */ + wpa_s->wpa_proto = ie.proto; + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PROTO, wpa_s->wpa_proto); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_ENABLED, + !!(wpa_s->wpa_proto & + (WPA_PROTO_RSN | WPA_PROTO_OSEN))); + + /* Update AKMP suite from (Re)Association Request frame info */ + sel = ie.key_mgmt; + if (ssid->key_mgmt) + sel &= ssid->key_mgmt; + + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: AP key_mgmt 0x%x network key_mgmt 0x%x; available key_mgmt 0x%x", + ie.key_mgmt, ssid->key_mgmt, sel); + if (ie.key_mgmt && !sel) { + wpa_supplicant_deauthenticate( + wpa_s, WLAN_REASON_AKMP_NOT_VALID); + return -1; + } + + wpa_s->key_mgmt = ie.key_mgmt; + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_KEY_MGMT, wpa_s->key_mgmt); + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT %s and proto %d", + wpa_key_mgmt_txt(wpa_s->key_mgmt, wpa_s->wpa_proto), + wpa_s->wpa_proto); + + /* Update pairwise cipher from (Re)Association Request frame info */ + sel = ie.pairwise_cipher; + if (ssid->pairwise_cipher) + sel &= ssid->pairwise_cipher; + + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: AP pairwise cipher 0x%x network pairwise cipher 0x%x; available pairwise cipher 0x%x", + ie.pairwise_cipher, ssid->pairwise_cipher, sel); + if (ie.pairwise_cipher && !sel) { + wpa_supplicant_deauthenticate( + wpa_s, WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID); + return -1; + } + + wpa_s->pairwise_cipher = ie.pairwise_cipher; + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PAIRWISE, + wpa_s->pairwise_cipher); + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK %s", + wpa_cipher_txt(wpa_s->pairwise_cipher)); + + /* Update other parameters based on AP's WPA IE/RSNE, if available */ + if (!bss) { + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: current_bss == NULL - skip AP IE check"); + return 0; + } + + /* Update GTK and IGTK from AP's RSNE */ + found = false; + + if (wpa_s->wpa_proto & (WPA_PROTO_RSN | WPA_PROTO_OSEN)) { + const u8 *bss_rsn; + + bss_rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); + if (bss_rsn) { + p = bss_rsn; + len = 2 + bss_rsn[1]; + found = true; + } + } else if (wpa_s->wpa_proto & WPA_PROTO_WPA) { + const u8 *bss_wpa; + + bss_wpa = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); + if (bss_wpa) { + p = bss_wpa; + len = 2 + bss_wpa[1]; + found = true; + } + } + + if (!found || wpa_parse_wpa_ie(p, len, &ie) < 0) + return 0; + + pmf = wpas_get_ssid_pmf(wpa_s, ssid); + if (!(ie.capabilities & WPA_CAPABILITY_MFPC) && + pmf == MGMT_FRAME_PROTECTION_REQUIRED) { + /* AP does not support MFP, local configuration requires it */ + wpa_supplicant_deauthenticate( + wpa_s, WLAN_REASON_INVALID_RSN_IE_CAPAB); + return -1; + } + if ((ie.capabilities & WPA_CAPABILITY_MFPR) && + pmf == NO_MGMT_FRAME_PROTECTION) { + /* AP requires MFP, local configuration disables it */ + wpa_supplicant_deauthenticate( + wpa_s, WLAN_REASON_INVALID_RSN_IE_CAPAB); + return -1; + } + + /* Update PMF from local configuration now that MFP validation was done + * above */ + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MFP, pmf); + + /* Update GTK from AP's RSNE */ + sel = ie.group_cipher; + if (ssid->group_cipher) + sel &= ssid->group_cipher; + + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: AP group cipher 0x%x network group cipher 0x%x; available group cipher 0x%x", + ie.group_cipher, ssid->group_cipher, sel); + if (ie.group_cipher && !sel) { + wpa_supplicant_deauthenticate( + wpa_s, WLAN_REASON_GROUP_CIPHER_NOT_VALID); + return -1; + } + + wpa_s->group_cipher = ie.group_cipher; + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_GROUP, wpa_s->group_cipher); + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK %s", + wpa_cipher_txt(wpa_s->group_cipher)); + + /* Update IGTK from AP RSN IE */ + sel = ie.mgmt_group_cipher; + if (ssid->group_mgmt_cipher) + sel &= ssid->group_mgmt_cipher; + + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: AP mgmt_group_cipher 0x%x network mgmt_group_cipher 0x%x; available mgmt_group_cipher 0x%x", + ie.mgmt_group_cipher, ssid->group_mgmt_cipher, sel); + + if (pmf == NO_MGMT_FRAME_PROTECTION || + !(ie.capabilities & WPA_CAPABILITY_MFPC)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: STA/AP is not MFP capable; AP RSNE caps 0x%x", + ie.capabilities); + ie.mgmt_group_cipher = 0; + } + + if (ie.mgmt_group_cipher && !sel) { + wpa_supplicant_deauthenticate( + wpa_s, WLAN_REASON_CIPHER_SUITE_REJECTED); + return -1; + } + + wpa_s->mgmt_group_cipher = ie.mgmt_group_cipher; + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP, + wpa_s->mgmt_group_cipher); + if (wpa_s->mgmt_group_cipher) + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher %s", + wpa_cipher_txt(wpa_s->mgmt_group_cipher)); + else + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: not using MGMT group cipher"); + + return 0; +} + + static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { @@ -2977,6 +3181,9 @@ no_pfs: wpa_s->assoc_freq = data->assoc_info.freq; + wpas_handle_assoc_resp_qos_mgmt(wpa_s, data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len); + return 0; } @@ -3121,6 +3328,10 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, } } + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && + data && wpa_supplicant_use_own_rsne_params(wpa_s, data) < 0) + return; + multi_ap_set_4addr_mode(wpa_s); if (wpa_s->conf->ap_scan == 1 && @@ -4260,12 +4471,26 @@ static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s, #endif /* CONFIG_DPP */ if (category == WLAN_ACTION_ROBUST_AV_STREAMING && + payload[0] == ROBUST_AV_SCS_RESP) { + wpas_handle_robust_av_scs_recv_action(wpa_s, mgmt->sa, + payload + 1, plen - 1); + return; + } + + if (category == WLAN_ACTION_ROBUST_AV_STREAMING && payload[0] == ROBUST_AV_MSCS_RESP) { wpas_handle_robust_av_recv_action(wpa_s, mgmt->sa, payload + 1, plen - 1); return; } + if (category == WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED && plen > 4 && + WPA_GET_BE32(payload) == QM_ACTION_VENDOR_TYPE) { + wpas_handle_qos_mgmt_recv_action(wpa_s, mgmt->sa, + payload + 4, plen - 4); + return; + } + wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid, category, payload, plen, freq); if (wpa_s->ifmsh) @@ -5238,13 +5463,21 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; case EVENT_INTERFACE_MAC_CHANGED: wpa_supplicant_update_mac_addr(wpa_s); + wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL); break; case EVENT_INTERFACE_ENABLED: wpa_dbg(wpa_s, MSG_DEBUG, "Interface was enabled"); if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { + u8 addr[ETH_ALEN]; + eloop_cancel_timeout(wpas_clear_disabled_interface, wpa_s, NULL); + os_memcpy(addr, wpa_s->own_addr, ETH_ALEN); wpa_supplicant_update_mac_addr(wpa_s); + if (os_memcmp(addr, wpa_s->own_addr, ETH_ALEN) != 0) + wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL); + else + wpa_sm_pmksa_cache_reconfig(wpa_s->wpa); wpa_supplicant_set_default_scan_ies(wpa_s); if (wpa_s->p2p_mgmt) { wpa_supplicant_set_state(wpa_s, diff --git a/contrib/wpa/wpa_supplicant/gas_query.c b/contrib/wpa/wpa_supplicant/gas_query.c index e60a8c1fe6aa..a6172d69233b 100644 --- a/contrib/wpa/wpa_supplicant/gas_query.c +++ b/contrib/wpa/wpa_supplicant/gas_query.c @@ -273,16 +273,6 @@ static void gas_query_tx_status(struct wpa_supplicant *wpa_s, } -int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr) -{ - if (wpa_s->current_ssid == NULL || - wpa_s->wpa_state < WPA_4WAY_HANDSHAKE || - os_memcmp(addr, wpa_s->bssid, ETH_ALEN) != 0) - return 0; - return wpa_sm_pmf_enabled(wpa_s->wpa); -} - - static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query, struct wpabuf *req, unsigned int wait_time) { diff --git a/contrib/wpa/wpa_supplicant/gas_query.h b/contrib/wpa/wpa_supplicant/gas_query.h index f9ce7b680fda..6ccecd4ddbe1 100644 --- a/contrib/wpa/wpa_supplicant/gas_query.h +++ b/contrib/wpa/wpa_supplicant/gas_query.h @@ -19,7 +19,6 @@ void gas_query_deinit(struct gas_query *gas); int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa, const u8 *bssid, u8 categ, const u8 *data, size_t len, int freq); -int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr); /** * enum gas_query_result - GAS query result diff --git a/contrib/wpa/wpa_supplicant/mesh.c b/contrib/wpa/wpa_supplicant/mesh.c index 901b49b4d257..7938b8b4903e 100644 --- a/contrib/wpa/wpa_supplicant/mesh.c +++ b/contrib/wpa/wpa_supplicant/mesh.c @@ -437,6 +437,37 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, if (!conf) goto out_free; + if (is_6ghz_freq(freq->freq)) { + /* + * IEEE Std 802.11ax-2021, 12.12.2: + * The STA shall use management frame protection (MFPR=1) when + * using RSN. + */ + ssid->ieee80211w = MGMT_FRAME_PROTECTION_REQUIRED; + + /* Set mandatory op_class parameter for setting up BSS */ + switch (freq->bandwidth) { + case 20: + if (freq->freq == 5935) + conf->op_class = 136; + else + conf->op_class = 131; + break; + case 40: + conf->op_class = 132; + break; + case 80: + conf->op_class = 133; + break; + case 160: + conf->op_class = 134; + break; + default: + conf->op_class = 131; + break; + } + } + bss->conf = *conf->bss; bss->conf->start_disabled = 1; bss->conf->mesh = MESH_ENABLED; diff --git a/contrib/wpa/wpa_supplicant/mesh_mpm.c b/contrib/wpa/wpa_supplicant/mesh_mpm.c index b6a5e8857db9..38f0d641a03b 100644 --- a/contrib/wpa/wpa_supplicant/mesh_mpm.c +++ b/contrib/wpa/wpa_supplicant/mesh_mpm.c @@ -251,6 +251,9 @@ static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s, HE_MAX_MCS_CAPAB_SIZE + HE_MAX_PPET_CAPAB_SIZE; buf_len += 3 + sizeof(struct ieee80211_he_operation); + if (is_6ghz_op_class(bss->iconf->op_class)) + buf_len += sizeof(struct ieee80211_he_6ghz_oper_info) + + 3 + sizeof(struct ieee80211_he_6ghz_band_cap); } #endif /* CONFIG_IEEE80211AX */ if (type != PLINK_CLOSE) @@ -375,11 +378,14 @@ static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s, HE_MAX_PHY_CAPAB_SIZE + HE_MAX_MCS_CAPAB_SIZE + HE_MAX_PPET_CAPAB_SIZE + - 3 + sizeof(struct ieee80211_he_operation)]; + 3 + sizeof(struct ieee80211_he_operation) + + sizeof(struct ieee80211_he_6ghz_oper_info) + + 3 + sizeof(struct ieee80211_he_6ghz_band_cap)]; pos = hostapd_eid_he_capab(bss, he_capa_oper, IEEE80211_MODE_MESH); pos = hostapd_eid_he_operation(bss, pos); + pos = hostapd_eid_he_6ghz_band_cap(bss, pos); wpabuf_put_data(buf, he_capa_oper, pos - he_capa_oper); } #endif /* CONFIG_IEEE80211AX */ @@ -749,6 +755,7 @@ static struct sta_info * mesh_mpm_add_peer(struct wpa_supplicant *wpa_s, #ifdef CONFIG_IEEE80211AX copy_sta_he_capab(data, sta, IEEE80211_MODE_MESH, elems->he_capabilities, elems->he_capabilities_len); + copy_sta_he_6ghz_capab(data, sta, elems->he_6ghz_band_cap); #endif /* CONFIG_IEEE80211AX */ if (hostapd_get_aid(data, sta) < 0) { @@ -770,6 +777,7 @@ static struct sta_info * mesh_mpm_add_peer(struct wpa_supplicant *wpa_s, params.vht_capabilities = sta->vht_capabilities; params.he_capab = sta->he_capab; params.he_capab_len = sta->he_capab_len; + params.he_6ghz_capab = sta->he_6ghz_capab; params.flags |= WPA_STA_WMM; params.flags_mask |= WPA_STA_AUTHENTICATED; if (conf->security == MESH_CONF_SEC_NONE) { diff --git a/contrib/wpa/wpa_supplicant/notify.c b/contrib/wpa/wpa_supplicant/notify.c index e0e7e5433d77..fe5e072c24c2 100644 --- a/contrib/wpa/wpa_supplicant/notify.c +++ b/contrib/wpa/wpa_supplicant/notify.c @@ -350,8 +350,11 @@ void wpas_notify_network_added(struct wpa_supplicant *wpa_s, * applications since these network objects won't behave like * regular ones. */ - if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s) + if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s) { wpas_dbus_register_network(wpa_s, ssid); + wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_NETWORK_ADDED "%d", + ssid->id); + } } @@ -381,8 +384,11 @@ void wpas_notify_network_removed(struct wpa_supplicant *wpa_s, if (wpa_s->wpa) wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid); if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s && - !wpa_s->p2p_mgmt) + !wpa_s->p2p_mgmt) { wpas_dbus_unregister_network(wpa_s, ssid->id); + wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_NETWORK_REMOVED "%d", + ssid->id); + } if (network_is_persistent_group(ssid)) wpas_notify_persistent_group_removed(wpa_s, ssid); diff --git a/contrib/wpa/wpa_supplicant/op_classes.c b/contrib/wpa/wpa_supplicant/op_classes.c index a0ad0c2ff572..bd53c5ceceaf 100644 --- a/contrib/wpa/wpa_supplicant/op_classes.c +++ b/contrib/wpa/wpa_supplicant/op_classes.c @@ -207,11 +207,15 @@ enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, u8 op_class, if (!(flag & HOSTAPD_CHAN_HT40MINUS)) return NOT_ALLOWED; res2 = allow_channel(mode, op_class, channel - 4, NULL); - } else if (bw == BW40PLUS || - (bw == BW40 && !(((channel - 1) / 4) % 2))) { + } else if (bw == BW40PLUS) { if (!(flag & HOSTAPD_CHAN_HT40PLUS)) return NOT_ALLOWED; res2 = allow_channel(mode, op_class, channel + 4, NULL); + } else if (is_6ghz_op_class(op_class) && bw == BW40) { + if (get_6ghz_sec_channel(channel) < 0) + res2 = allow_channel(mode, op_class, channel - 4, NULL); + else + res2 = allow_channel(mode, op_class, channel + 4, NULL); } else if (bw == BW80) { /* * channel is a center channel and as such, not necessarily a diff --git a/contrib/wpa/wpa_supplicant/p2p_supplicant.c b/contrib/wpa/wpa_supplicant/p2p_supplicant.c index 62c9a26a3490..ce44dfb9e053 100644 --- a/contrib/wpa/wpa_supplicant/p2p_supplicant.c +++ b/contrib/wpa/wpa_supplicant/p2p_supplicant.c @@ -164,6 +164,17 @@ wpas_p2p_consider_moving_gos(struct wpa_supplicant *wpa_s, static void wpas_p2p_reconsider_moving_go(void *eloop_ctx, void *timeout_ctx); +static int wpas_get_6ghz_he_chwidth_capab(struct hostapd_hw_modes *mode) +{ + int he_capab = 0; + + if (mode) + he_capab = mode->he_capab[WPAS_MODE_INFRA].phy_cap[ + HE_PHYCAP_CHANNEL_WIDTH_SET_IDX]; + return he_capab; +} + + /* * Get the number of concurrent channels that the HW can operate, but that are * currently not in use by any of the wpa_supplicant interfaces. @@ -1027,6 +1038,7 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, wpa_s->p2p_group_common_freqs = NULL; wpa_s->p2p_group_common_freqs_num = 0; wpa_s->p2p_go_do_acs = 0; + wpa_s->p2p_go_allow_dfs = 0; wpa_s->waiting_presence_resp = 0; @@ -2069,6 +2081,8 @@ static void wpas_start_wps_go(struct wpa_supplicant *wpa_s, is_p2p_6ghz_capable(wpa_s->global->p2p)) { ssid->auth_alg |= WPA_AUTH_ALG_SAE; ssid->key_mgmt = WPA_KEY_MGMT_SAE; + ssid->ieee80211w = MGMT_FRAME_PROTECTION_REQUIRED; + ssid->sae_pwe = 1; wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use SAE auth_alg and key_mgmt"); } else { p2p_set_6ghz_dev_capab(wpa_s->global->p2p, false); @@ -2157,6 +2171,8 @@ do { \ d->disassoc_low_ack = s->disassoc_low_ack; d->disable_scan_offload = s->disable_scan_offload; d->passive_scan = s->passive_scan; + d->pmf = s->pmf; + d->p2p_6ghz_disable = s->p2p_6ghz_disable; if (s->wps_nfc_dh_privkey && s->wps_nfc_dh_pubkey && !d->wps_nfc_pw_from_config) { @@ -3571,12 +3587,12 @@ static enum chan_allowed has_channel(struct wpa_global *global, if ((unsigned int) mode->channels[i].freq == freq) { if (flags) *flags = mode->channels[i].flag; - if (mode->channels[i].flag & - (HOSTAPD_CHAN_DISABLED | - HOSTAPD_CHAN_RADAR)) + if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED) return NOT_ALLOWED; if (mode->channels[i].flag & HOSTAPD_CHAN_NO_IR) return NO_IR; + if (mode->channels[i].flag & HOSTAPD_CHAN_RADAR) + return RADAR; return ALLOWED; } } @@ -3636,7 +3652,8 @@ static enum chan_allowed wpas_p2p_verify_80mhz(struct wpa_supplicant *wpa_s, chans, num_chans); if (!center_chan) return NOT_ALLOWED; - if (!is_6ghz && center_chan >= 58 && center_chan <= 138) + if (!wpa_s->p2p_go_allow_dfs && + !is_6ghz && center_chan >= 58 && center_chan <= 138) return NOT_ALLOWED; /* Do not allow DFS channels for P2P */ /* check all the channels are available */ @@ -3647,17 +3664,24 @@ static enum chan_allowed wpas_p2p_verify_80mhz(struct wpa_supplicant *wpa_s, &flags); if (res == NOT_ALLOWED) return NOT_ALLOWED; + if (res == RADAR) + ret = RADAR; if (res == NO_IR) ret = NO_IR; - - if (i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_70)) - return NOT_ALLOWED; - if (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_50)) - return NOT_ALLOWED; - if (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_30)) - return NOT_ALLOWED; - if (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_10)) + if (!is_6ghz) { + if (i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_70)) + return NOT_ALLOWED; + if (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_50)) + return NOT_ALLOWED; + if (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_30)) + return NOT_ALLOWED; + if (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_10)) + return NOT_ALLOWED; + } else if (is_6ghz && + (!(wpas_get_6ghz_he_chwidth_capab(mode) & + HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G))) { return NOT_ALLOWED; + } } return ret; @@ -3723,25 +3747,33 @@ static enum chan_allowed wpas_p2p_verify_160mhz(struct wpa_supplicant *wpa_s, if (res == NOT_ALLOWED) return NOT_ALLOWED; + if (res == RADAR) + ret = RADAR; if (res == NO_IR) ret = NO_IR; - if (i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_150)) - return NOT_ALLOWED; - if (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_130)) - return NOT_ALLOWED; - if (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_110)) - return NOT_ALLOWED; - if (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_90)) - return NOT_ALLOWED; - if (i == 4 && !(flags & HOSTAPD_CHAN_VHT_90_70)) - return NOT_ALLOWED; - if (i == 5 && !(flags & HOSTAPD_CHAN_VHT_110_50)) - return NOT_ALLOWED; - if (i == 6 && !(flags & HOSTAPD_CHAN_VHT_130_30)) - return NOT_ALLOWED; - if (i == 7 && !(flags & HOSTAPD_CHAN_VHT_150_10)) + if (!is_6ghz_op_class(op_class)) { + if (i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_150)) + return NOT_ALLOWED; + if (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_130)) + return NOT_ALLOWED; + if (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_110)) + return NOT_ALLOWED; + if (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_90)) + return NOT_ALLOWED; + if (i == 4 && !(flags & HOSTAPD_CHAN_VHT_90_70)) + return NOT_ALLOWED; + if (i == 5 && !(flags & HOSTAPD_CHAN_VHT_110_50)) + return NOT_ALLOWED; + if (i == 6 && !(flags & HOSTAPD_CHAN_VHT_130_30)) + return NOT_ALLOWED; + if (i == 7 && !(flags & HOSTAPD_CHAN_VHT_150_10)) + return NOT_ALLOWED; + } else if (is_6ghz_op_class(op_class) && + (!(wpas_get_6ghz_he_chwidth_capab(mode) & + HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G))) { return NOT_ALLOWED; + } } return ret; @@ -3780,6 +3812,15 @@ static enum chan_allowed wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s, return NOT_ALLOWED; res2 = has_channel(wpa_s->global, mode, op_class, channel + 4, NULL); + } else if (is_6ghz_op_class(op_class) && bw == BW40) { + if (mode->mode != HOSTAPD_MODE_IEEE80211A) + return NOT_ALLOWED; + if (get_6ghz_sec_channel(channel) < 0) + res2 = has_channel(wpa_s->global, mode, op_class, + channel - 4, NULL); + else + res2 = has_channel(wpa_s->global, mode, op_class, + channel + 4, NULL); } else if (bw == BW80) { res2 = wpas_p2p_verify_80mhz(wpa_s, mode, op_class, channel, bw); @@ -3794,6 +3835,8 @@ static enum chan_allowed wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s, return NOT_ALLOWED; if (res == NO_IR || res2 == NO_IR) return NO_IR; + if (res == RADAR || res2 == RADAR) + return RADAR; return res; } @@ -3817,7 +3860,7 @@ static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s, for (op = 0; global_op_class[op].op_class; op++) { const struct oper_class_map *o = &global_op_class[op]; - u8 ch; + unsigned int ch; struct p2p_reg_class *reg = NULL, *cli_reg = NULL; if (o->p2p == NO_P2P_SUPP || @@ -3890,30 +3933,50 @@ static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s, } -int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s, - struct hostapd_hw_modes *mode, u8 channel) +int wpas_p2p_get_sec_channel_offset_40mhz(struct wpa_supplicant *wpa_s, + struct hostapd_hw_modes *mode, + u8 channel) { int op; enum chan_allowed ret; for (op = 0; global_op_class[op].op_class; op++) { const struct oper_class_map *o = &global_op_class[op]; - u8 ch; - - if (o->p2p == NO_P2P_SUPP || + u16 ch; + int chan = channel; + + /* Allow DFS channels marked as NO_P2P_SUPP to be used with + * driver offloaded DFS. */ + if ((o->p2p == NO_P2P_SUPP && + (!is_dfs_global_op_class(o->op_class) || + !wpa_s->p2p_go_allow_dfs)) || (is_6ghz_op_class(o->op_class) && wpa_s->conf->p2p_6ghz_disable)) continue; + if (is_6ghz_op_class(o->op_class) && o->bw == BW40 && + get_6ghz_sec_channel(channel) < 0) + chan = channel - 4; + for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) { if (o->mode != HOSTAPD_MODE_IEEE80211A || - (o->bw != BW40PLUS && o->bw != BW40MINUS) || - ch != channel) + (o->bw != BW40PLUS && o->bw != BW40MINUS && + o->bw != BW40) || + ch != chan) continue; ret = wpas_p2p_verify_channel(wpa_s, mode, o->op_class, ch, o->bw); - if (ret == ALLOWED) + if (ret == ALLOWED) { + if (is_6ghz_op_class(o->op_class) && + o->bw == BW40) + return get_6ghz_sec_channel(channel); + return (o->bw == BW40MINUS) ? -1 : 1; + } + if (ret == RADAR && wpa_s->p2p_go_allow_dfs) { + /* Allow RADAR channels used for driver + * offloaded DFS */ return (o->bw == BW40MINUS) ? -1 : 1; + } } } return 0; @@ -3926,8 +3989,10 @@ int wpas_p2p_get_vht80_center(struct wpa_supplicant *wpa_s, { const u8 *chans; size_t num_chans; + enum chan_allowed ret; - if (!wpas_p2p_verify_channel(wpa_s, mode, op_class, channel, BW80)) + ret = wpas_p2p_verify_channel(wpa_s, mode, op_class, channel, BW80); + if (!(ret == ALLOWED || (ret == RADAR && wpa_s->p2p_go_allow_dfs))) return 0; if (is_6ghz_op_class(op_class)) { @@ -3948,8 +4013,10 @@ int wpas_p2p_get_vht160_center(struct wpa_supplicant *wpa_s, { const u8 *chans; size_t num_chans; + enum chan_allowed ret; - if (!wpas_p2p_verify_channel(wpa_s, mode, op_class, channel, BW160)) + ret = wpas_p2p_verify_channel(wpa_s, mode, op_class, channel, BW160); + if (!(ret == ALLOWED || (ret == RADAR && wpa_s->p2p_go_allow_dfs))) return 0; if (is_6ghz_op_class(op_class)) { chans = center_channels_6ghz_160mhz; @@ -6267,34 +6334,26 @@ static void wpas_p2p_select_go_freq_no_pref(struct wpa_supplicant *wpa_s, /* try all channels in operating class 115 */ for (i = 0; i < 4; i++) { params->freq = 5180 + i * 20; - if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) && - freq_included(wpa_s, channels, params->freq) && - p2p_supported_freq(wpa_s->global->p2p, params->freq)) + if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq)) goto out; } /* try all channels in operating class 124 */ for (i = 0; i < 4; i++) { params->freq = 5745 + i * 20; - if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) && - freq_included(wpa_s, channels, params->freq) && - p2p_supported_freq(wpa_s->global->p2p, params->freq)) + if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq)) goto out; } /* try social channel class 180 channel 2 */ params->freq = 58320 + 1 * 2160; - if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) && - freq_included(wpa_s, channels, params->freq) && - p2p_supported_freq(wpa_s->global->p2p, params->freq)) + if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq)) goto out; /* try all channels in reg. class 180 */ for (i = 0; i < 4; i++) { params->freq = 58320 + i * 2160; - if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) && - freq_included(wpa_s, channels, params->freq) && - p2p_supported_freq(wpa_s->global->p2p, params->freq)) + if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq)) goto out; } @@ -6729,6 +6788,11 @@ wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated, wpa_s->p2p_go_do_acs = 0; } + if (go && wpa_s->p2p_go_allow_dfs) { + group_wpa_s->p2p_go_allow_dfs = wpa_s->p2p_go_allow_dfs; + wpa_s->p2p_go_allow_dfs = 0; + } + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use separate group interface %s", group_wpa_s->ifname); group_wpa_s->p2p_first_connection_timeout = 0; diff --git a/contrib/wpa/wpa_supplicant/p2p_supplicant.h b/contrib/wpa/wpa_supplicant/p2p_supplicant.h index dee9c1bcc1e1..5a869e7309a3 100644 --- a/contrib/wpa/wpa_supplicant/p2p_supplicant.h +++ b/contrib/wpa/wpa_supplicant/p2p_supplicant.h @@ -146,8 +146,9 @@ struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s, void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s, const u8 *addr); int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s); -int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s, - struct hostapd_hw_modes *mode, u8 channel); +int wpas_p2p_get_sec_channel_offset_40mhz(struct wpa_supplicant *wpa_s, + struct hostapd_hw_modes *mode, + u8 channel); int wpas_p2p_get_vht80_center(struct wpa_supplicant *wpa_s, struct hostapd_hw_modes *mode, u8 channel, u8 op_class); diff --git a/contrib/wpa/wpa_supplicant/p2p_supplicant_sd.c b/contrib/wpa/wpa_supplicant/p2p_supplicant_sd.c index f8675e68bec4..b400cbacae61 100644 --- a/contrib/wpa/wpa_supplicant/p2p_supplicant_sd.c +++ b/contrib/wpa/wpa_supplicant/p2p_supplicant_sd.c @@ -826,7 +826,7 @@ static void wpas_sd_p2ps_serv_response(struct wpa_supplicant *wpa_s, size_t buf_len; u8 svc_len; - /* Sanity check fixed length+svc_str */ + /* Validity check fixed length+svc_str */ if (6 >= tlv_end - pos) break; svc_len = pos[6]; @@ -854,7 +854,7 @@ static void wpas_sd_p2ps_serv_response(struct wpa_supplicant *wpa_s, buf_len = WPA_GET_LE16(pos); pos += sizeof(u16); - /* Sanity check buffer length */ + /* Validity check buffer length */ if (buf_len > (unsigned int) (tlv_end - pos)) break; diff --git a/contrib/wpa/wpa_supplicant/preauth_test.c b/contrib/wpa/wpa_supplicant/preauth_test.c index 97c16fb80d27..31b55325f7f7 100644 --- a/contrib/wpa/wpa_supplicant/preauth_test.c +++ b/contrib/wpa/wpa_supplicant/preauth_test.c @@ -220,7 +220,7 @@ static void eapol_test_poll(void *eloop_ctx, void *timeout_ctx) } -static struct wpa_driver_ops dummy_driver; +static struct wpa_driver_ops stub_driver; static void wpa_init_conf(struct wpa_supplicant *wpa_s, const char *ifname) @@ -228,8 +228,8 @@ static void wpa_init_conf(struct wpa_supplicant *wpa_s, const char *ifname) struct l2_packet_data *l2; struct wpa_sm_ctx *ctx; - os_memset(&dummy_driver, 0, sizeof(dummy_driver)); - wpa_s->driver = &dummy_driver; + os_memset(&stub_driver, 0, sizeof(stub_driver)); + wpa_s->driver = &stub_driver; ctx = os_zalloc(sizeof(*ctx)); assert(ctx != NULL); diff --git a/contrib/wpa/wpa_supplicant/robust_av.c b/contrib/wpa/wpa_supplicant/robust_av.c index 39d282f0870d..770c8fcab189 100644 --- a/contrib/wpa/wpa_supplicant/robust_av.c +++ b/contrib/wpa/wpa_supplicant/robust_av.c @@ -8,6 +8,7 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/eloop.h" #include "common/wpa_ctrl.h" #include "common/ieee802_11_common.h" #include "wpa_supplicant_i.h" @@ -15,6 +16,10 @@ #include "bss.h" +#define SCS_RESP_TIMEOUT 1 +#define DSCP_REQ_TIMEOUT 5 + + void wpas_populate_mscs_descriptor_ie(struct robust_av_data *robust_av, struct wpabuf *buf) { @@ -45,6 +50,126 @@ void wpas_populate_mscs_descriptor_ie(struct robust_av_data *robust_av, } +static int wpas_populate_type4_classifier(struct type4_params *type4_param, + struct wpabuf *buf) +{ + /* classifier parameters */ + wpabuf_put_u8(buf, type4_param->classifier_mask); + if (type4_param->ip_version == IPV4) { + wpabuf_put_u8(buf, IPV4); /* IP version */ + wpabuf_put_data(buf, &type4_param->ip_params.v4.src_ip.s_addr, + 4); + wpabuf_put_data(buf, &type4_param->ip_params.v4.dst_ip.s_addr, + 4); + wpabuf_put_be16(buf, type4_param->ip_params.v4.src_port); + wpabuf_put_be16(buf, type4_param->ip_params.v4.dst_port); + wpabuf_put_u8(buf, type4_param->ip_params.v4.dscp); + wpabuf_put_u8(buf, type4_param->ip_params.v4.protocol); + wpabuf_put_u8(buf, 0); /* Reserved octet */ + } else { + wpabuf_put_u8(buf, IPV6); + wpabuf_put_data(buf, &type4_param->ip_params.v6.src_ip.s6_addr, + 16); + wpabuf_put_data(buf, &type4_param->ip_params.v6.dst_ip.s6_addr, + 16); + wpabuf_put_be16(buf, type4_param->ip_params.v6.src_port); + wpabuf_put_be16(buf, type4_param->ip_params.v6.dst_port); + wpabuf_put_u8(buf, type4_param->ip_params.v6.dscp); + wpabuf_put_u8(buf, type4_param->ip_params.v6.next_header); + wpabuf_put_data(buf, type4_param->ip_params.v6.flow_label, 3); + } + + return 0; +} + + +static int wpas_populate_type10_classifier(struct type10_params *type10_param, + struct wpabuf *buf) +{ + /* classifier parameters */ + wpabuf_put_u8(buf, type10_param->prot_instance); + wpabuf_put_u8(buf, type10_param->prot_number); + wpabuf_put_data(buf, type10_param->filter_value, + type10_param->filter_len); + wpabuf_put_data(buf, type10_param->filter_mask, + type10_param->filter_len); + return 0; +} + + +static int wpas_populate_scs_descriptor_ie(struct scs_desc_elem *desc_elem, + struct wpabuf *buf) +{ + u8 *len, *len1; + struct tclas_element *tclas_elem; + unsigned int i; + + /* SCS Descriptor element */ + wpabuf_put_u8(buf, WLAN_EID_SCS_DESCRIPTOR); + len = wpabuf_put(buf, 1); + wpabuf_put_u8(buf, desc_elem->scs_id); + wpabuf_put_u8(buf, desc_elem->request_type); + if (desc_elem->request_type == SCS_REQ_REMOVE) + goto end; + + if (desc_elem->intra_access_priority || desc_elem->scs_up_avail) { + wpabuf_put_u8(buf, WLAN_EID_INTRA_ACCESS_CATEGORY_PRIORITY); + wpabuf_put_u8(buf, 1); + wpabuf_put_u8(buf, desc_elem->intra_access_priority); + } + + tclas_elem = desc_elem->tclas_elems; + + if (!tclas_elem) + return -1; + + for (i = 0; i < desc_elem->num_tclas_elem; i++, tclas_elem++) { + int ret; + + /* TCLAS element */ + wpabuf_put_u8(buf, WLAN_EID_TCLAS); + len1 = wpabuf_put(buf, 1); + wpabuf_put_u8(buf, 255); /* User Priority: not compared */ + /* Frame Classifier */ + wpabuf_put_u8(buf, tclas_elem->classifier_type); + /* Frame classifier parameters */ + switch (tclas_elem->classifier_type) { + case 4: + ret = wpas_populate_type4_classifier( + &tclas_elem->frame_classifier.type4_param, + buf); + break; + case 10: + ret = wpas_populate_type10_classifier( + &tclas_elem->frame_classifier.type10_param, + buf); + break; + default: + return -1; + } + + if (ret == -1) { + wpa_printf(MSG_ERROR, + "Failed to populate frame classifier"); + return -1; + } + + *len1 = (u8 *) wpabuf_put(buf, 0) - len1 - 1; + } + + if (desc_elem->num_tclas_elem > 1) { + /* TCLAS Processing element */ + wpabuf_put_u8(buf, WLAN_EID_TCLAS_PROCESSING); + wpabuf_put_u8(buf, 1); + wpabuf_put_u8(buf, desc_elem->tclas_processing); + } + +end: + *len = (u8 *) wpabuf_put(buf, 0) - len - 1; + return 0; +} + + int wpas_send_mscs_req(struct wpa_supplicant *wpa_s) { struct wpabuf *buf; @@ -101,6 +226,277 @@ int wpas_send_mscs_req(struct wpa_supplicant *wpa_s) } +static size_t tclas_elem_len(const struct tclas_element *elem) +{ + size_t buf_len = 0; + + buf_len += 2 + /* TCLAS element header */ + 1 + /* User Priority */ + 1 ; /* Classifier Type */ + + if (elem->classifier_type == 4) { + enum ip_version ip_ver; + + buf_len += 1 + /* Classifier mask */ + 1 + /* IP version */ + 1 + /* user priority */ + 2 + /* src_port */ + 2 + /* dst_port */ + 1 ; /* dscp */ + ip_ver = elem->frame_classifier.type4_param.ip_version; + if (ip_ver == IPV4) { + buf_len += 4 + /* src_ip */ + 4 + /* dst_ip */ + 1 + /* protocol */ + 1 ; /* Reserved */ + } else if (ip_ver == IPV6) { + buf_len += 16 + /* src_ip */ + 16 + /* dst_ip */ + 1 + /* next_header */ + 3 ; /* flow_label */ + } else { + wpa_printf(MSG_ERROR, "%s: Incorrect IP version %d", + __func__, ip_ver); + return 0; + } + } else if (elem->classifier_type == 10) { + buf_len += 1 + /* protocol instance */ + 1 + /* protocol number */ + 2 * elem->frame_classifier.type10_param.filter_len; + } else { + wpa_printf(MSG_ERROR, "%s: Incorrect classifier type %u", + __func__, elem->classifier_type); + return 0; + } + + return buf_len; +} + + +static struct wpabuf * allocate_scs_buf(struct scs_desc_elem *desc_elem, + unsigned int num_scs_desc) +{ + struct wpabuf *buf; + size_t buf_len = 0; + unsigned int i, j; + + buf_len = 3; /* Action frame header */ + + for (i = 0; i < num_scs_desc; i++, desc_elem++) { + struct tclas_element *tclas_elem; + + buf_len += 2 + /* SCS descriptor IE header */ + 1 + /* SCSID */ + 1 ; /* Request type */ + + if (desc_elem->request_type == SCS_REQ_REMOVE) + continue; + + if (desc_elem->intra_access_priority || desc_elem->scs_up_avail) + buf_len += 3; + + tclas_elem = desc_elem->tclas_elems; + if (!tclas_elem) { + wpa_printf(MSG_ERROR, "%s: TCLAS element null", + __func__); + return NULL; + } + + for (j = 0; j < desc_elem->num_tclas_elem; j++, tclas_elem++) { + size_t elen; + + elen = tclas_elem_len(tclas_elem); + if (elen == 0) + return NULL; + buf_len += elen; + } + + if (desc_elem->num_tclas_elem > 1) { + buf_len += 1 + /* TCLAS Processing eid */ + 1 + /* length */ + 1 ; /* processing */ + } + } + + buf = wpabuf_alloc(buf_len); + if (!buf) { + wpa_printf(MSG_ERROR, "Failed to allocate SCS req"); + return NULL; + } + + return buf; +} + + +static void scs_request_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct active_scs_elem *scs_desc, *prev; + + if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid) + return; + + /* Once timeout is over, remove all SCS descriptors with no response */ + dl_list_for_each_safe(scs_desc, prev, &wpa_s->active_scs_ids, + struct active_scs_elem, list) { + u8 bssid[ETH_ALEN] = { 0 }; + const u8 *src; + + if (scs_desc->status == SCS_DESC_SUCCESS) + continue; + + if (wpa_s->current_bss) + src = wpa_s->current_bss->bssid; + else + src = bssid; + + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCS_RESULT "bssid=" MACSTR + " SCSID=%u status_code=timedout", MAC2STR(src), + scs_desc->scs_id); + + dl_list_del(&scs_desc->list); + wpa_printf(MSG_INFO, "%s: SCSID %d removed after timeout", + __func__, scs_desc->scs_id); + os_free(scs_desc); + } + + eloop_cancel_timeout(scs_request_timer, wpa_s, NULL); + wpa_s->ongoing_scs_req = false; +} + + +int wpas_send_scs_req(struct wpa_supplicant *wpa_s) +{ + struct wpabuf *buf = NULL; + struct scs_desc_elem *desc_elem = NULL; + int ret = -1; + unsigned int i; + + if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid) + return -1; + + if (!wpa_bss_ext_capab(wpa_s->current_bss, WLAN_EXT_CAPAB_SCS)) { + wpa_dbg(wpa_s, MSG_INFO, + "AP does not support SCS - could not send SCS Request"); + return -1; + } + + desc_elem = wpa_s->scs_robust_av_req.scs_desc_elems; + if (!desc_elem) + return -1; + + buf = allocate_scs_buf(desc_elem, + wpa_s->scs_robust_av_req.num_scs_desc); + if (!buf) + return -1; + + wpabuf_put_u8(buf, WLAN_ACTION_ROBUST_AV_STREAMING); + wpabuf_put_u8(buf, ROBUST_AV_SCS_REQ); + wpa_s->scs_dialog_token++; + if (wpa_s->scs_dialog_token == 0) + wpa_s->scs_dialog_token++; + wpabuf_put_u8(buf, wpa_s->scs_dialog_token); + + for (i = 0; i < wpa_s->scs_robust_av_req.num_scs_desc; + i++, desc_elem++) { + /* SCS Descriptor element */ + if (wpas_populate_scs_descriptor_ie(desc_elem, buf) < 0) + goto end; + } + + wpa_hexdump_buf(MSG_DEBUG, "SCS Request", buf); + ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head(buf), wpabuf_len(buf), 0); + if (ret < 0) { + wpa_dbg(wpa_s, MSG_ERROR, "SCS: Failed to send SCS Request"); + wpa_s->scs_dialog_token--; + goto end; + } + + desc_elem = wpa_s->scs_robust_av_req.scs_desc_elems; + for (i = 0; i < wpa_s->scs_robust_av_req.num_scs_desc; + i++, desc_elem++) { + struct active_scs_elem *active_scs_elem; + + if (desc_elem->request_type != SCS_REQ_ADD) + continue; + + active_scs_elem = os_malloc(sizeof(struct active_scs_elem)); + if (!active_scs_elem) + break; + active_scs_elem->scs_id = desc_elem->scs_id; + active_scs_elem->status = SCS_DESC_SENT; + dl_list_add(&wpa_s->active_scs_ids, &active_scs_elem->list); + } + + /* + * Register a timeout after which this request will be removed from + * the cache. + */ + eloop_register_timeout(SCS_RESP_TIMEOUT, 0, scs_request_timer, wpa_s, + NULL); + wpa_s->ongoing_scs_req = true; + +end: + wpabuf_free(buf); + free_up_scs_desc(&wpa_s->scs_robust_av_req); + + return ret; +} + + +void free_up_tclas_elem(struct scs_desc_elem *elem) +{ + struct tclas_element *tclas_elems = elem->tclas_elems; + unsigned int num_tclas_elem = elem->num_tclas_elem; + struct tclas_element *tclas_data; + unsigned int j; + + elem->tclas_elems = NULL; + elem->num_tclas_elem = 0; + + if (!tclas_elems) + return; + + tclas_data = tclas_elems; + for (j = 0; j < num_tclas_elem; j++, tclas_data++) { + if (tclas_data->classifier_type != 10) + continue; + + os_free(tclas_data->frame_classifier.type10_param.filter_value); + os_free(tclas_data->frame_classifier.type10_param.filter_mask); + } + + os_free(tclas_elems); +} + + +void free_up_scs_desc(struct scs_robust_av_data *data) +{ + struct scs_desc_elem *desc_elems = data->scs_desc_elems; + unsigned int num_scs_desc = data->num_scs_desc; + struct scs_desc_elem *desc_data; + unsigned int i; + + data->scs_desc_elems = NULL; + data->num_scs_desc = 0; + + if (!desc_elems) + return; + + desc_data = desc_elems; + for (i = 0; i < num_scs_desc; i++, desc_data++) { + if (desc_data->request_type == SCS_REQ_REMOVE || + !desc_data->tclas_elems) + continue; + + free_up_tclas_elem(desc_data); + } + os_free(desc_elems); +} + + void wpas_handle_robust_av_recv_action(struct wpa_supplicant *wpa_s, const u8 *src, const u8 *buf, size_t len) { @@ -153,3 +549,939 @@ void wpas_handle_assoc_resp_mscs(struct wpa_supplicant *wpa_s, const u8 *bssid, " status_code=%u", MAC2STR(bssid), status); wpa_s->mscs_setup_done = status == WLAN_STATUS_SUCCESS; } + + +static void wpas_wait_for_dscp_req_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + /* Once timeout is over, reset wait flag and allow sending DSCP query */ + wpa_printf(MSG_DEBUG, + "QM: Wait time over for sending DSCP request - allow DSCP query"); + wpa_s->wait_for_dscp_req = 0; + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY "request_wait end"); +} + + +void wpas_handle_assoc_resp_qos_mgmt(struct wpa_supplicant *wpa_s, + const u8 *ies, size_t ies_len) +{ + const u8 *wfa_capa; + + wpa_s->connection_dscp = 0; + if (wpa_s->wait_for_dscp_req) + eloop_cancel_timeout(wpas_wait_for_dscp_req_timer, wpa_s, NULL); + + if (!ies || ies_len == 0 || !wpa_s->enable_dscp_policy_capa) + return; + + wfa_capa = get_vendor_ie(ies, ies_len, WFA_CAPA_IE_VENDOR_TYPE); + if (!wfa_capa || wfa_capa[1] < 6 || wfa_capa[6] < 1 || + !(wfa_capa[7] & WFA_CAPA_QM_DSCP_POLICY)) + return; /* AP does not enable QM DSCP Policy */ + + wpa_s->connection_dscp = 1; + wpa_s->wait_for_dscp_req = !!(wfa_capa[7] & + WFA_CAPA_QM_UNSOLIC_DSCP); + if (!wpa_s->wait_for_dscp_req) + return; + + /* Register a timeout after which dscp query can be sent to AP. */ + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY "request_wait start"); + eloop_register_timeout(DSCP_REQ_TIMEOUT, 0, + wpas_wait_for_dscp_req_timer, wpa_s, NULL); +} + + +void wpas_handle_robust_av_scs_recv_action(struct wpa_supplicant *wpa_s, + const u8 *src, const u8 *buf, + size_t len) +{ + u8 dialog_token; + unsigned int i, count; + struct active_scs_elem *scs_desc, *prev; + + if (len < 2) + return; + if (!wpa_s->ongoing_scs_req) { + wpa_printf(MSG_INFO, + "SCS: Drop received response due to no ongoing request"); + return; + } + + dialog_token = *buf++; + len--; + if (dialog_token != wpa_s->scs_dialog_token) { + wpa_printf(MSG_INFO, + "SCS: Drop received frame due to dialog token mismatch: received:%u expected:%u", + dialog_token, wpa_s->scs_dialog_token); + return; + } + + /* This Count field does not exist in the IEEE Std 802.11-2020 + * definition of the SCS Response frame. However, it was accepted to + * be added into REVme per REVme/D0.0 CC35 CID 49 (edits in document + * 11-21-0688-07). */ + count = *buf++; + len--; + if (count == 0 || count * 3 > len) { + wpa_printf(MSG_INFO, + "SCS: Drop received frame due to invalid count: %u (remaining %zu octets)", + count, len); + return; + } + + for (i = 0; i < count; i++) { + u8 id; + u16 status; + bool scs_desc_found = false; + + id = *buf++; + status = WPA_GET_LE16(buf); + buf += 2; + len -= 3; + + dl_list_for_each(scs_desc, &wpa_s->active_scs_ids, + struct active_scs_elem, list) { + if (id == scs_desc->scs_id) { + scs_desc_found = true; + break; + } + } + + if (!scs_desc_found) { + wpa_printf(MSG_INFO, "SCS: SCS ID invalid %u", id); + continue; + } + + if (status != WLAN_STATUS_SUCCESS) { + dl_list_del(&scs_desc->list); + os_free(scs_desc); + } else if (status == WLAN_STATUS_SUCCESS) { + scs_desc->status = SCS_DESC_SUCCESS; + } + + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCS_RESULT "bssid=" MACSTR + " SCSID=%u status_code=%u", MAC2STR(src), id, status); + } + + eloop_cancel_timeout(scs_request_timer, wpa_s, NULL); + wpa_s->ongoing_scs_req = false; + + dl_list_for_each_safe(scs_desc, prev, &wpa_s->active_scs_ids, + struct active_scs_elem, list) { + if (scs_desc->status != SCS_DESC_SUCCESS) { + wpa_msg(wpa_s, MSG_INFO, + WPA_EVENT_SCS_RESULT "bssid=" MACSTR + " SCSID=%u status_code=response_not_received", + MAC2STR(src), scs_desc->scs_id); + dl_list_del(&scs_desc->list); + os_free(scs_desc); + } + } +} + + +static void wpas_clear_active_scs_ids(struct wpa_supplicant *wpa_s) +{ + struct active_scs_elem *scs_elem; + + while ((scs_elem = dl_list_first(&wpa_s->active_scs_ids, + struct active_scs_elem, list))) { + dl_list_del(&scs_elem->list); + os_free(scs_elem); + } +} + + +void wpas_scs_deinit(struct wpa_supplicant *wpa_s) +{ + free_up_scs_desc(&wpa_s->scs_robust_av_req); + wpa_s->scs_dialog_token = 0; + wpas_clear_active_scs_ids(wpa_s); + eloop_cancel_timeout(scs_request_timer, wpa_s, NULL); + wpa_s->ongoing_scs_req = false; +} + + +static int write_ipv4_info(char *pos, int total_len, + const struct ipv4_params *v4) +{ + int res, rem_len; + char addr[INET_ADDRSTRLEN]; + + rem_len = total_len; + + if (v4->param_mask & BIT(1)) { + if (!inet_ntop(AF_INET, &v4->src_ip, addr, INET_ADDRSTRLEN)) { + wpa_printf(MSG_ERROR, + "QM: Failed to set IPv4 source address"); + return -1; + } + + res = os_snprintf(pos, rem_len, " src_ip=%s", addr); + if (os_snprintf_error(rem_len, res)) + return -1; + + pos += res; + rem_len -= res; + } + + if (v4->param_mask & BIT(2)) { + if (!inet_ntop(AF_INET, &v4->dst_ip, addr, INET_ADDRSTRLEN)) { + wpa_printf(MSG_ERROR, + "QM: Failed to set IPv4 destination address"); + return -1; + } + + res = os_snprintf(pos, rem_len, " dst_ip=%s", addr); + if (os_snprintf_error(rem_len, res)) + return -1; + + pos += res; + rem_len -= res; + } + + if (v4->param_mask & BIT(3)) { + res = os_snprintf(pos, rem_len, " src_port=%d", v4->src_port); + if (os_snprintf_error(rem_len, res)) + return -1; + + pos += res; + rem_len -= res; + } + + if (v4->param_mask & BIT(4)) { + res = os_snprintf(pos, rem_len, " dst_port=%d", v4->dst_port); + if (os_snprintf_error(rem_len, res)) + return -1; + + pos += res; + rem_len -= res; + } + + if (v4->param_mask & BIT(6)) { + res = os_snprintf(pos, rem_len, " protocol=%d", v4->protocol); + if (os_snprintf_error(rem_len, res)) + return -1; + + pos += res; + rem_len -= res; + } + + return total_len - rem_len; +} + + +static int write_ipv6_info(char *pos, int total_len, + const struct ipv6_params *v6) +{ + int res, rem_len; + char addr[INET6_ADDRSTRLEN]; + + rem_len = total_len; + + if (v6->param_mask & BIT(1)) { + if (!inet_ntop(AF_INET6, &v6->src_ip, addr, INET6_ADDRSTRLEN)) { + wpa_printf(MSG_ERROR, + "QM: Failed to set IPv6 source addr"); + return -1; + } + + res = os_snprintf(pos, rem_len, " src_ip=%s", addr); + if (os_snprintf_error(rem_len, res)) + return -1; + + pos += res; + rem_len -= res; + } + + if (v6->param_mask & BIT(2)) { + if (!inet_ntop(AF_INET6, &v6->dst_ip, addr, INET6_ADDRSTRLEN)) { + wpa_printf(MSG_ERROR, + "QM: Failed to set IPv6 destination addr"); + return -1; + } + + res = os_snprintf(pos, rem_len, " dst_ip=%s", addr); + if (os_snprintf_error(rem_len, res)) + return -1; + + pos += res; + rem_len -= res; + } + + if (v6->param_mask & BIT(3)) { + res = os_snprintf(pos, rem_len, " src_port=%d", v6->src_port); + if (os_snprintf_error(rem_len, res)) + return -1; + + pos += res; + rem_len -= res; + } + + if (v6->param_mask & BIT(4)) { + res = os_snprintf(pos, rem_len, " dst_port=%d", v6->dst_port); + if (os_snprintf_error(rem_len, res)) + return -1; + + pos += res; + rem_len -= res; + } + + if (v6->param_mask & BIT(6)) { + res = os_snprintf(pos, rem_len, " protocol=%d", + v6->next_header); + if (os_snprintf_error(rem_len, res)) + return -1; + + pos += res; + rem_len -= res; + } + + return total_len - rem_len; +} + + +struct dscp_policy_data { + u8 policy_id; + u8 req_type; + u8 dscp; + bool dscp_info; + const u8 *frame_classifier; + u8 frame_classifier_len; + struct type4_params type4_param; + const u8 *domain_name; + u8 domain_name_len; + u16 start_port; + u16 end_port; + bool port_range_info; +}; + + +static int set_frame_classifier_type4_ipv4(struct dscp_policy_data *policy) +{ + u8 classifier_mask; + const u8 *frame_classifier = policy->frame_classifier; + struct type4_params *type4_param = &policy->type4_param; + + if (policy->frame_classifier_len < 18) { + wpa_printf(MSG_ERROR, + "QM: Received IPv4 frame classifier with insufficient length %d", + policy->frame_classifier_len); + return -1; + } + + classifier_mask = frame_classifier[1]; + + /* Classifier Mask - bit 1 = Source IP Address */ + if (classifier_mask & BIT(1)) { + type4_param->ip_params.v4.param_mask |= BIT(1); + os_memcpy(&type4_param->ip_params.v4.src_ip, + &frame_classifier[3], 4); + } + + /* Classifier Mask - bit 2 = Destination IP Address */ + if (classifier_mask & BIT(2)) { + if (policy->domain_name) { + wpa_printf(MSG_ERROR, + "QM: IPv4: Both domain name and destination IP address not expected"); + return -1; + } + + type4_param->ip_params.v4.param_mask |= BIT(2); + os_memcpy(&type4_param->ip_params.v4.dst_ip, + &frame_classifier[7], 4); + } + + /* Classifier Mask - bit 3 = Source Port */ + if (classifier_mask & BIT(3)) { + type4_param->ip_params.v4.param_mask |= BIT(3); + type4_param->ip_params.v4.src_port = + WPA_GET_BE16(&frame_classifier[11]); + } + + /* Classifier Mask - bit 4 = Destination Port */ + if (classifier_mask & BIT(4)) { + if (policy->port_range_info) { + wpa_printf(MSG_ERROR, + "QM: IPv4: Both port range and destination port not expected"); + return -1; + } + + type4_param->ip_params.v4.param_mask |= BIT(4); + type4_param->ip_params.v4.dst_port = + WPA_GET_BE16(&frame_classifier[13]); + } + + /* Classifier Mask - bit 5 = DSCP (ignored) */ + + /* Classifier Mask - bit 6 = Protocol */ + if (classifier_mask & BIT(6)) { + type4_param->ip_params.v4.param_mask |= BIT(6); + type4_param->ip_params.v4.protocol = frame_classifier[16]; + } + + return 0; +} + + +static int set_frame_classifier_type4_ipv6(struct dscp_policy_data *policy) +{ + u8 classifier_mask; + const u8 *frame_classifier = policy->frame_classifier; + struct type4_params *type4_param = &policy->type4_param; + + if (policy->frame_classifier_len < 44) { + wpa_printf(MSG_ERROR, + "QM: Received IPv6 frame classifier with insufficient length %d", + policy->frame_classifier_len); + return -1; + } + + classifier_mask = frame_classifier[1]; + + /* Classifier Mask - bit 1 = Source IP Address */ + if (classifier_mask & BIT(1)) { + type4_param->ip_params.v6.param_mask |= BIT(1); + os_memcpy(&type4_param->ip_params.v6.src_ip, + &frame_classifier[3], 16); + } + + /* Classifier Mask - bit 2 = Destination IP Address */ + if (classifier_mask & BIT(2)) { + if (policy->domain_name) { + wpa_printf(MSG_ERROR, + "QM: IPv6: Both domain name and destination IP address not expected"); + return -1; + } + type4_param->ip_params.v6.param_mask |= BIT(2); + os_memcpy(&type4_param->ip_params.v6.dst_ip, + &frame_classifier[19], 16); + } + + /* Classifier Mask - bit 3 = Source Port */ + if (classifier_mask & BIT(3)) { + type4_param->ip_params.v6.param_mask |= BIT(3); + type4_param->ip_params.v6.src_port = + WPA_GET_BE16(&frame_classifier[35]); + } + + /* Classifier Mask - bit 4 = Destination Port */ + if (classifier_mask & BIT(4)) { + if (policy->port_range_info) { + wpa_printf(MSG_ERROR, + "IPv6: Both port range and destination port not expected"); + return -1; + } + + type4_param->ip_params.v6.param_mask |= BIT(4); + type4_param->ip_params.v6.dst_port = + WPA_GET_BE16(&frame_classifier[37]); + } + + /* Classifier Mask - bit 5 = DSCP (ignored) */ + + /* Classifier Mask - bit 6 = Next Header */ + if (classifier_mask & BIT(6)) { + type4_param->ip_params.v6.param_mask |= BIT(6); + type4_param->ip_params.v6.next_header = frame_classifier[40]; + } + + return 0; +} + + +static int wpas_set_frame_classifier_params(struct dscp_policy_data *policy) +{ + const u8 *frame_classifier = policy->frame_classifier; + u8 frame_classifier_len = policy->frame_classifier_len; + + if (frame_classifier_len < 3) { + wpa_printf(MSG_ERROR, + "QM: Received frame classifier with insufficient length %d", + frame_classifier_len); + return -1; + } + + /* Only allowed Classifier Type: IP and higher layer parameters (4) */ + if (frame_classifier[0] != 4) { + wpa_printf(MSG_ERROR, + "QM: Received frame classifier with invalid classifier type %d", + frame_classifier[0]); + return -1; + } + + /* Classifier Mask - bit 0 = Version */ + if (!(frame_classifier[1] & BIT(0))) { + wpa_printf(MSG_ERROR, + "QM: Received frame classifier without IP version"); + return -1; + } + + /* Version (4 or 6) */ + if (frame_classifier[2] == 4) { + if (set_frame_classifier_type4_ipv4(policy)) { + wpa_printf(MSG_ERROR, + "QM: Failed to set IPv4 parameters"); + return -1; + } + + policy->type4_param.ip_version = IPV4; + } else if (frame_classifier[2] == 6) { + if (set_frame_classifier_type4_ipv6(policy)) { + wpa_printf(MSG_ERROR, + "QM: Failed to set IPv6 parameters"); + return -1; + } + + policy->type4_param.ip_version = IPV6; + } else { + wpa_printf(MSG_ERROR, + "QM: Received unknown IP version %d", + frame_classifier[2]); + return -1; + } + + return 0; +} + + +static bool dscp_valid_domain_name(const char *str) +{ + if (!str[0]) + return false; + + while (*str) { + if (is_ctrl_char(*str) || *str == ' ' || *str == '=') + return false; + str++; + } + + return true; +} + + +static void wpas_add_dscp_policy(struct wpa_supplicant *wpa_s, + struct dscp_policy_data *policy) +{ + int ip_ver = 0, res; + char policy_str[1000], *pos; + int len; + + if (!policy->frame_classifier && !policy->domain_name && + !policy->port_range_info) { + wpa_printf(MSG_ERROR, + "QM: Invalid DSCP policy - no attributes present"); + goto fail; + } + + policy_str[0] = '\0'; + pos = policy_str; + len = sizeof(policy_str); + + if (policy->frame_classifier) { + struct type4_params *type4 = &policy->type4_param; + + if (wpas_set_frame_classifier_params(policy)) { + wpa_printf(MSG_ERROR, + "QM: Failed to set frame classifier parameters"); + goto fail; + } + + if (type4->ip_version == IPV4) + res = write_ipv4_info(pos, len, &type4->ip_params.v4); + else + res = write_ipv6_info(pos, len, &type4->ip_params.v6); + + if (res <= 0) { + wpa_printf(MSG_ERROR, + "QM: Failed to write IP parameters"); + goto fail; + } + + ip_ver = type4->ip_version; + + pos += res; + len -= res; + } + + if (policy->port_range_info) { + res = os_snprintf(pos, len, " start_port=%u end_port=%u", + policy->start_port, policy->end_port); + if (os_snprintf_error(len, res)) { + wpa_printf(MSG_ERROR, + "QM: Failed to write port range attributes for policy id = %d", + policy->policy_id); + goto fail; + } + + pos += res; + len -= res; + } + + if (policy->domain_name) { + char domain_name_str[250]; + + if (policy->domain_name_len >= sizeof(domain_name_str)) { + wpa_printf(MSG_ERROR, + "QM: Domain name length higher than max expected"); + goto fail; + } + os_memcpy(domain_name_str, policy->domain_name, + policy->domain_name_len); + domain_name_str[policy->domain_name_len] = '\0'; + if (!dscp_valid_domain_name(domain_name_str)) { + wpa_printf(MSG_ERROR, "QM: Invalid domain name string"); + goto fail; + } + res = os_snprintf(pos, len, " domain_name=%s", domain_name_str); + if (os_snprintf_error(len, res)) { + wpa_printf(MSG_ERROR, + "QM: Failed to write domain name attribute for policy id = %d", + policy->policy_id); + goto fail; + } + } + + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY + "add policy_id=%u dscp=%u ip_version=%d%s", + policy->policy_id, policy->dscp, ip_ver, policy_str); + return; +fail: + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY "reject policy_id=%u", + policy->policy_id); +} + + +void wpas_dscp_deinit(struct wpa_supplicant *wpa_s) +{ + wpa_printf(MSG_DEBUG, "QM: Clear all active DSCP policies"); + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY "clear_all"); + wpa_s->dscp_req_dialog_token = 0; + wpa_s->dscp_query_dialog_token = 0; + wpa_s->connection_dscp = 0; + if (wpa_s->wait_for_dscp_req) { + wpa_s->wait_for_dscp_req = 0; + eloop_cancel_timeout(wpas_wait_for_dscp_req_timer, wpa_s, NULL); + } +} + + +static void wpas_fill_dscp_policy(struct dscp_policy_data *policy, u8 attr_id, + u8 attr_len, const u8 *attr_data) +{ + switch (attr_id) { + case QM_ATTR_PORT_RANGE: + if (attr_len < 4) { + wpa_printf(MSG_ERROR, + "QM: Received Port Range attribute with insufficient length %d", + attr_len); + break; + } + policy->start_port = WPA_GET_BE16(attr_data); + policy->end_port = WPA_GET_BE16(attr_data + 2); + policy->port_range_info = true; + break; + case QM_ATTR_DSCP_POLICY: + if (attr_len < 3) { + wpa_printf(MSG_ERROR, + "QM: Received DSCP Policy attribute with insufficient length %d", + attr_len); + return; + } + policy->policy_id = attr_data[0]; + policy->req_type = attr_data[1]; + policy->dscp = attr_data[2]; + policy->dscp_info = true; + break; + case QM_ATTR_TCLAS: + if (attr_len < 1) { + wpa_printf(MSG_ERROR, + "QM: Received TCLAS attribute with insufficient length %d", + attr_len); + return; + } + policy->frame_classifier = attr_data; + policy->frame_classifier_len = attr_len; + break; + case QM_ATTR_DOMAIN_NAME: + if (attr_len < 1) { + wpa_printf(MSG_ERROR, + "QM: Received domain name attribute with insufficient length %d", + attr_len); + return; + } + policy->domain_name = attr_data; + policy->domain_name_len = attr_len; + break; + default: + wpa_printf(MSG_ERROR, "QM: Received invalid QoS attribute %d", + attr_id); + break; + } +} + + +void wpas_handle_qos_mgmt_recv_action(struct wpa_supplicant *wpa_s, + const u8 *src, + const u8 *buf, size_t len) +{ + int rem_len; + const u8 *qos_ie, *attr; + int more, reset; + + if (!wpa_s->enable_dscp_policy_capa) { + wpa_printf(MSG_ERROR, + "QM: Ignore DSCP Policy frame since the capability is not enabled"); + return; + } + + if (!pmf_in_use(wpa_s, src)) { + wpa_printf(MSG_ERROR, + "QM: Ignore DSCP Policy frame since PMF is not in use"); + return; + } + + if (!wpa_s->connection_dscp) { + wpa_printf(MSG_DEBUG, + "QM: DSCP Policy capability not enabled for the current association - ignore QoS Management Action frames"); + return; + } + + if (len < 1) + return; + + /* Handle only DSCP Policy Request frame */ + if (buf[0] != QM_DSCP_POLICY_REQ) { + wpa_printf(MSG_ERROR, "QM: Received unexpected QoS action frame %d", + buf[0]); + return; + } + + if (len < 3) { + wpa_printf(MSG_ERROR, + "Received QoS Management DSCP Policy Request frame with invalid length %zu", + len); + return; + } + + /* Clear wait_for_dscp_req on receiving first DSCP request from AP */ + if (wpa_s->wait_for_dscp_req) { + wpa_s->wait_for_dscp_req = 0; + eloop_cancel_timeout(wpas_wait_for_dscp_req_timer, wpa_s, NULL); + } + + wpa_s->dscp_req_dialog_token = buf[1]; + more = buf[2] & DSCP_POLICY_CTRL_MORE; + reset = buf[2] & DSCP_POLICY_CTRL_RESET; + + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY "request_start%s%s", + reset ? " clear_all" : "", more ? " more" : ""); + + qos_ie = buf + 3; + rem_len = len - 3; + while (rem_len > 2) { + struct dscp_policy_data policy; + int rem_attrs_len, ie_len; + + ie_len = 2 + qos_ie[1]; + if (rem_len < ie_len) + break; + + if (rem_len < 6 || qos_ie[0] != WLAN_EID_VENDOR_SPECIFIC || + qos_ie[1] < 4 || + WPA_GET_BE32(&qos_ie[2]) != QM_IE_VENDOR_TYPE) { + rem_len -= ie_len; + qos_ie += ie_len; + continue; + } + + os_memset(&policy, 0, sizeof(struct dscp_policy_data)); + attr = qos_ie + 6; + rem_attrs_len = qos_ie[1] - 4; + + while (rem_attrs_len > 2 && rem_attrs_len >= 2 + attr[1]) { + wpas_fill_dscp_policy(&policy, attr[0], attr[1], + &attr[2]); + rem_attrs_len -= 2 + attr[1]; + attr += 2 + attr[1]; + } + + rem_len -= ie_len; + qos_ie += ie_len; + + if (!policy.dscp_info) { + wpa_printf(MSG_ERROR, + "QM: Received QoS IE without DSCP Policy attribute"); + continue; + } + + if (policy.req_type == DSCP_POLICY_REQ_ADD) + wpas_add_dscp_policy(wpa_s, &policy); + else if (policy.req_type == DSCP_POLICY_REQ_REMOVE) + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY + "remove policy_id=%u", policy.policy_id); + else + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY + "reject policy_id=%u", policy.policy_id); + } + + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY "request_end"); +} + + +int wpas_send_dscp_response(struct wpa_supplicant *wpa_s, + struct dscp_resp_data *resp_data) +{ + struct wpabuf *buf = NULL; + size_t buf_len; + int ret = -1, i; + u8 resp_control = 0; + + if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid) { + wpa_printf(MSG_ERROR, + "QM: Failed to send DSCP response - not connected to AP"); + return -1; + } + + if (resp_data->solicited && !wpa_s->dscp_req_dialog_token) { + wpa_printf(MSG_ERROR, "QM: No ongoing DSCP request"); + return -1; + } + + if (!wpa_s->connection_dscp) { + wpa_printf(MSG_ERROR, + "QM: Failed to send DSCP response - DSCP capability not enabled for the current association"); + return -1; + + } + + buf_len = 1 + /* Category */ + 3 + /* OUI */ + 1 + /* OUI Type */ + 1 + /* OUI Subtype */ + 1 + /* Dialog Token */ + 1 + /* Response Control */ + 1 + /* Count */ + 2 * resp_data->num_policies; /* Status list */ + buf = wpabuf_alloc(buf_len); + if (!buf) { + wpa_printf(MSG_ERROR, + "QM: Failed to allocate DSCP policy response"); + return -1; + } + + wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, QM_ACTION_OUI_TYPE); + wpabuf_put_u8(buf, QM_DSCP_POLICY_RESP); + + wpabuf_put_u8(buf, resp_data->solicited ? + wpa_s->dscp_req_dialog_token : 0); + + if (resp_data->more) + resp_control |= DSCP_POLICY_CTRL_MORE; + if (resp_data->reset) + resp_control |= DSCP_POLICY_CTRL_RESET; + wpabuf_put_u8(buf, resp_control); + + wpabuf_put_u8(buf, resp_data->num_policies); + for (i = 0; i < resp_data->num_policies; i++) { + wpabuf_put_u8(buf, resp_data->policy[i].id); + wpabuf_put_u8(buf, resp_data->policy[i].status); + } + + wpa_hexdump_buf(MSG_MSGDUMP, "DSCP response frame: ", buf); + ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head(buf), wpabuf_len(buf), 0); + if (ret < 0) { + wpa_msg(wpa_s, MSG_INFO, "QM: Failed to send DSCP response"); + goto fail; + } + + /* + * Mark DSCP request complete whether response sent is solicited or + * unsolicited + */ + wpa_s->dscp_req_dialog_token = 0; + +fail: + wpabuf_free(buf); + return ret; +} + + +int wpas_send_dscp_query(struct wpa_supplicant *wpa_s, const char *domain_name, + size_t domain_name_length) +{ + struct wpabuf *buf = NULL; + int ret, dscp_query_size; + + if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid) + return -1; + + if (!wpa_s->connection_dscp) { + wpa_printf(MSG_ERROR, + "QM: Failed to send DSCP query - DSCP capability not enabled for the current association"); + return -1; + } + + if (wpa_s->wait_for_dscp_req) { + wpa_printf(MSG_INFO, "QM: Wait until AP sends a DSCP request"); + return -1; + } + +#define DOMAIN_NAME_OFFSET (4 /* OUI */ + 1 /* Attr Id */ + 1 /* Attr len */) + + if (domain_name_length > 255 - DOMAIN_NAME_OFFSET) { + wpa_printf(MSG_ERROR, "QM: Too long domain name"); + return -1; + } + + dscp_query_size = 1 + /* Category */ + 4 + /* OUI Type */ + 1 + /* OUI subtype */ + 1; /* Dialog Token */ + if (domain_name && domain_name_length) + dscp_query_size += 1 + /* Element ID */ + 1 + /* IE Length */ + DOMAIN_NAME_OFFSET + domain_name_length; + + buf = wpabuf_alloc(dscp_query_size); + if (!buf) { + wpa_printf(MSG_ERROR, "QM: Failed to allocate DSCP query"); + return -1; + } + + wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED); + wpabuf_put_be32(buf, QM_ACTION_VENDOR_TYPE); + wpabuf_put_u8(buf, QM_DSCP_POLICY_QUERY); + wpa_s->dscp_query_dialog_token++; + if (wpa_s->dscp_query_dialog_token == 0) + wpa_s->dscp_query_dialog_token++; + wpabuf_put_u8(buf, wpa_s->dscp_query_dialog_token); + + if (domain_name && domain_name_length) { + /* Domain Name attribute */ + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(buf, DOMAIN_NAME_OFFSET + domain_name_length); + wpabuf_put_be32(buf, QM_IE_VENDOR_TYPE); + wpabuf_put_u8(buf, QM_ATTR_DOMAIN_NAME); + wpabuf_put_u8(buf, domain_name_length); + wpabuf_put_data(buf, domain_name, domain_name_length); + } +#undef DOMAIN_NAME_OFFSET + + ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head(buf), wpabuf_len(buf), 0); + if (ret < 0) { + wpa_dbg(wpa_s, MSG_ERROR, "QM: Failed to send DSCP query"); + wpa_s->dscp_query_dialog_token--; + } + + wpabuf_free(buf); + return ret; +} diff --git a/contrib/wpa/wpa_supplicant/scan.c b/contrib/wpa/wpa_supplicant/scan.c index 97a8d9a638d6..b0094ca6ca5b 100644 --- a/contrib/wpa/wpa_supplicant/scan.c +++ b/contrib/wpa/wpa_supplicant/scan.c @@ -392,6 +392,29 @@ wpa_supplicant_build_filter_ssids(struct wpa_config *conf, size_t *num_ssids) } +#ifdef CONFIG_P2P +static bool is_6ghz_supported(struct wpa_supplicant *wpa_s) +{ + struct hostapd_channel_data *chnl; + int i, j; + + for (i = 0; i < wpa_s->hw.num_modes; i++) { + if (wpa_s->hw.modes[i].mode == HOSTAPD_MODE_IEEE80211A) { + chnl = wpa_s->hw.modes[i].channels; + for (j = 0; j < wpa_s->hw.modes[i].num_channels; j++) { + if (chnl[j].flag & HOSTAPD_CHAN_DISABLED) + continue; + if (is_6ghz_freq(chnl[j].freq)) + return true; + } + } + } + + return false; +} +#endif /* CONFIG_P2P */ + + static void wpa_supplicant_optimize_freqs( struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params) { @@ -729,13 +752,13 @@ static void wpa_setband_scan_freqs(struct wpa_supplicant *wpa_s, if (wpa_s->setband_mask & WPA_SETBAND_5G) wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, params, - 0); + false); if (wpa_s->setband_mask & WPA_SETBAND_2G) wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, params, - 0); + false); if (wpa_s->setband_mask & WPA_SETBAND_6G) wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, params, - 1); + true); } @@ -1337,6 +1360,34 @@ scan: } } } + + if (!params.freqs && + (wpa_s->p2p_in_invitation || wpa_s->p2p_in_provisioning) && + !is_p2p_allow_6ghz(wpa_s->global->p2p) && + is_6ghz_supported(wpa_s)) { + int i; + + /* Exclude 5 GHz channels from the full scan for P2P connection + * since the 6 GHz band is disabled for P2P uses. */ + wpa_printf(MSG_DEBUG, + "P2P: 6 GHz disabled - update the scan frequency list"); + for (i = 0; i < wpa_s->hw.num_modes; i++) { + if (wpa_s->hw.modes[i].num_channels == 0) + continue; + if (wpa_s->hw.modes[i].mode == HOSTAPD_MODE_IEEE80211G) + wpa_add_scan_freqs_list( + wpa_s, HOSTAPD_MODE_IEEE80211G, + ¶ms, false); + if (wpa_s->hw.modes[i].mode == HOSTAPD_MODE_IEEE80211A) + wpa_add_scan_freqs_list( + wpa_s, HOSTAPD_MODE_IEEE80211A, + ¶ms, false); + if (wpa_s->hw.modes[i].mode == HOSTAPD_MODE_IEEE80211AD) + wpa_add_scan_freqs_list( + wpa_s, HOSTAPD_MODE_IEEE80211AD, + ¶ms, false); + } + } #endif /* CONFIG_P2P */ ret = wpa_supplicant_trigger_scan(wpa_s, scan_params); diff --git a/contrib/wpa/wpa_supplicant/systemd/wpa_supplicant.service.in b/contrib/wpa/wpa_supplicant/systemd/wpa_supplicant.service.in index 75a37a8cdbd3..58a622887cd9 100644 --- a/contrib/wpa/wpa_supplicant/systemd/wpa_supplicant.service.in +++ b/contrib/wpa/wpa_supplicant/systemd/wpa_supplicant.service.in @@ -1,6 +1,7 @@ [Unit] Description=WPA supplicant Before=network.target +After=dbus.service Wants=network.target [Service] diff --git a/contrib/wpa/wpa_supplicant/wpa_cli.c b/contrib/wpa/wpa_supplicant/wpa_cli.c index 215ac4975cb5..075d587be2f1 100644 --- a/contrib/wpa/wpa_supplicant/wpa_cli.c +++ b/contrib/wpa/wpa_supplicant/wpa_cli.c @@ -499,6 +499,7 @@ static char ** wpa_cli_complete_set(const char *str, int pos) "p2p_search_delay", "mac_addr", "rand_addr_lifetime", "preassoc_mac_addr", "key_mgmt_offload", "passive_scan", "reassoc_same_bss_optim", "wps_priority", + "ap_assocresp_elements", #ifdef CONFIG_TESTING_OPTIONS "ignore_auth_resp", #endif /* CONFIG_TESTING_OPTIONS */ @@ -2037,6 +2038,13 @@ static int wpa_cli_cmd_chanswitch(struct wpa_ctrl *ctrl, int argc, return wpa_cli_cmd(ctrl, "CHAN_SWITCH", 2, argc, argv); } + +static int wpa_cli_cmd_update_beacon(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "UPDATE_BEACON"); +} + #endif /* CONFIG_AP */ @@ -3220,6 +3228,30 @@ static int wpa_cli_cmd_pasn_deauth(struct wpa_ctrl *ctrl, int argc, #endif /* CONFIG_PASN */ +static int wpa_cli_cmd_mscs(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "MSCS", 1, argc, argv); +} + + +static int wpa_cli_cmd_scs(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "SCS", 2, argc, argv); +} + + +static int wpa_cli_cmd_dscp_resp(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "DSCP_RESP", 1, argc, argv); +} + + +static int wpa_cli_cmd_dscp_query(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "DSCP_QUERY", 1, argc, argv); +} + + enum wpa_cli_cmd_flags { cli_cmd_flag_none = 0x00, cli_cmd_flag_sensitive = 0x01 @@ -3563,6 +3595,9 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { "<cs_count> <freq> [sec_channel_offset=] [center_freq1=]" " [center_freq2=] [bandwidth=] [blocktx] [ht|vht]" " = CSA parameters" }, + { "update_beacon", wpa_cli_cmd_update_beacon, NULL, + cli_cmd_flag_none, + "= update Beacon frame contents"}, #endif /* CONFIG_AP */ { "suspend", wpa_cli_cmd_suspend, NULL, cli_cmd_flag_none, "= notification of suspend/hibernate" }, @@ -3924,6 +3959,18 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { cli_cmd_flag_none, "bssid=<BSSID> = Remove PASN PTKSA state" }, #endif /* CONFIG_PASN */ + { "mscs", wpa_cli_cmd_mscs, NULL, + cli_cmd_flag_none, + "<add|remove|change> [up_bitmap=<hex byte>] [up_limit=<integer>] [stream_timeout=<in TUs>] [frame_classifier=<hex bytes>] = Configure MSCS request" }, + { "scs", wpa_cli_cmd_scs, NULL, + cli_cmd_flag_none, + "[scs_id=<decimal number>] <add|remove|change> [scs_up=<0-7>] [classifier_type=<4|10>] [classifier params based on classifier type] [tclas_processing=<0|1>] [scs_id=<decimal number>] ... = Send SCS request" }, + { "dscp_resp", wpa_cli_cmd_dscp_resp, NULL, + cli_cmd_flag_none, + "<[reset]>/<[solicited] [policy_id=1 status=0...]> [more] = Send DSCP response" }, + { "dscp_query", wpa_cli_cmd_dscp_query, NULL, + cli_cmd_flag_none, + "wildcard/domain_name=<string> = Send DSCP Query" }, { NULL, NULL, NULL, cli_cmd_flag_none, NULL } }; diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant.c b/contrib/wpa/wpa_supplicant/wpa_supplicant.c index 47b542e91dd7..71bf95f951a1 100644 --- a/contrib/wpa/wpa_supplicant/wpa_supplicant.c +++ b/contrib/wpa/wpa_supplicant/wpa_supplicant.c @@ -743,6 +743,8 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) #ifdef CONFIG_PASN wpas_pasn_auth_stop(wpa_s); #endif /* CONFIG_PASN */ + wpas_scs_deinit(wpa_s); + wpas_dscp_deinit(wpa_s); } @@ -1295,6 +1297,47 @@ static int matching_ciphers(struct wpa_ssid *ssid, struct wpa_ie_data *ie, } +void wpas_set_mgmt_group_cipher(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, struct wpa_ie_data *ie) +{ + int sel; + + sel = ie->mgmt_group_cipher; + if (ssid->group_mgmt_cipher) + sel &= ssid->group_mgmt_cipher; + if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION || + !(ie->capabilities & WPA_CAPABILITY_MFPC)) + sel = 0; + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: AP mgmt_group_cipher 0x%x network profile mgmt_group_cipher 0x%x; available mgmt_group_cipher 0x%x", + ie->mgmt_group_cipher, ssid->group_mgmt_cipher, sel); + if (sel & WPA_CIPHER_AES_128_CMAC) { + wpa_s->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC; + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: using MGMT group cipher AES-128-CMAC"); + } else if (sel & WPA_CIPHER_BIP_GMAC_128) { + wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_GMAC_128; + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: using MGMT group cipher BIP-GMAC-128"); + } else if (sel & WPA_CIPHER_BIP_GMAC_256) { + wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_GMAC_256; + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: using MGMT group cipher BIP-GMAC-256"); + } else if (sel & WPA_CIPHER_BIP_CMAC_256) { + wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_CMAC_256; + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: using MGMT group cipher BIP-CMAC-256"); + } else { + wpa_s->mgmt_group_cipher = 0; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: not using MGMT group cipher"); + } + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP, + wpa_s->mgmt_group_cipher); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MFP, + wpas_get_ssid_pmf(wpa_s, ssid)); +} + + /** * wpa_supplicant_set_suites - Set authentication and encryption parameters * @wpa_s: Pointer to wpa_supplicant data @@ -1628,39 +1671,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, return -1; } - sel = ie.mgmt_group_cipher; - if (ssid->group_mgmt_cipher) - sel &= ssid->group_mgmt_cipher; - if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION || - !(ie.capabilities & WPA_CAPABILITY_MFPC)) - sel = 0; - wpa_dbg(wpa_s, MSG_DEBUG, - "WPA: AP mgmt_group_cipher 0x%x network profile mgmt_group_cipher 0x%x; available mgmt_group_cipher 0x%x", - ie.mgmt_group_cipher, ssid->group_mgmt_cipher, sel); - if (sel & WPA_CIPHER_AES_128_CMAC) { - wpa_s->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC; - wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher " - "AES-128-CMAC"); - } else if (sel & WPA_CIPHER_BIP_GMAC_128) { - wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_GMAC_128; - wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher " - "BIP-GMAC-128"); - } else if (sel & WPA_CIPHER_BIP_GMAC_256) { - wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_GMAC_256; - wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher " - "BIP-GMAC-256"); - } else if (sel & WPA_CIPHER_BIP_CMAC_256) { - wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_CMAC_256; - wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher " - "BIP-CMAC-256"); - } else { - wpa_s->mgmt_group_cipher = 0; - wpa_dbg(wpa_s, MSG_DEBUG, "WPA: not using MGMT group cipher"); - } - wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP, - wpa_s->mgmt_group_cipher); - wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MFP, - wpas_get_ssid_pmf(wpa_s, ssid)); + wpas_set_mgmt_group_cipher(wpa_s, ssid, &ie); #ifdef CONFIG_OCV if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) || (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_OCV)) @@ -1872,6 +1883,8 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx) { + bool scs = true, mscs = true; + *pos = 0x00; switch (idx) { @@ -1915,6 +1928,12 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx) #endif /* CONFIG_MBO */ break; case 6: /* Bits 48-55 */ +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->disable_scs_support) + scs = false; +#endif /* CONFIG_TESTING_OPTIONS */ + if (scs) + *pos |= 0x40; /* Bit 54 - SCS */ break; case 7: /* Bits 56-63 */ break; @@ -1931,7 +1950,12 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx) #endif /* CONFIG_FILS */ break; case 10: /* Bits 80-87 */ - *pos |= 0x20; /* Bit 85 - Mirrored SCS */ +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->disable_mscs_support) + mscs = false; +#endif /* CONFIG_TESTING_OPTIONS */ + if (mscs) + *pos |= 0x20; /* Bit 85 - Mirrored SCS */ break; } } @@ -2220,9 +2244,11 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, } else { #ifdef CONFIG_SAE wpa_s_clear_sae_rejected(wpa_s); - wpa_s_setup_sae_pt(wpa_s->conf, ssid); #endif /* CONFIG_SAE */ } +#ifdef CONFIG_SAE + wpa_s_setup_sae_pt(wpa_s->conf, ssid); +#endif /* CONFIG_SAE */ if (rand_style > 0 && !wpa_s->reassoc_same_ess) { if (wpas_update_random_addr(wpa_s, rand_style) < 0) @@ -2383,6 +2409,23 @@ static int drv_supports_vht(struct wpa_supplicant *wpa_s, } +static bool ibss_mesh_is_80mhz_avail(int channel, struct hostapd_hw_modes *mode) +{ + int i; + + for (i = channel; i < channel + 16; i += 4) { + struct hostapd_channel_data *chan; + + chan = hw_get_channel_chan(mode, i, NULL); + if (!chan || + chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR)) + return false; + } + + return true; +} + + void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, const struct wpa_ssid *ssid, struct hostapd_freq_params *freq) @@ -2392,7 +2435,10 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, struct hostapd_hw_modes *mode = NULL; int ht40plus[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, 184, 192 }; - int vht80[] = { 36, 52, 100, 116, 132, 149 }; + int bw80[] = { 5180, 5260, 5500, 5580, 5660, 5745, 5955, + 6035, 6115, 6195, 6275, 6355, 6435, 6515, + 6595, 6675, 6755, 6835, 6915, 6995 }; + int bw160[] = { 5955, 6115, 6275, 6435, 6595, 6755, 6915 }; struct hostapd_channel_data *pri_chan = NULL, *sec_chan = NULL; u8 channel; int i, chan_idx, ht40 = -1, res, obss_scan = 1; @@ -2400,7 +2446,7 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, struct hostapd_freq_params vht_freq; int chwidth, seg0, seg1; u32 vht_caps = 0; - int is_24ghz; + bool is_24ghz, is_6ghz; freq->freq = ssid->frequency; @@ -2457,6 +2503,13 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G || hw_mode == HOSTAPD_MODE_IEEE80211B; + /* HT/VHT and corresponding overrides are not applicable to 6 GHz. + * However, HE is mandatory for 6 GHz. + */ + is_6ghz = is_6ghz_freq(freq->freq); + if (is_6ghz) + goto skip_to_6ghz; + #ifdef CONFIG_HT_OVERRIDES if (ssid->disable_ht) { freq->ht_enabled = 0; @@ -2584,8 +2637,6 @@ skip_ht40: !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_VHT_IBSS)) return; - vht_freq = *freq; - #ifdef CONFIG_VHT_OVERRIDES if (ssid->disable_vht) { freq->vht_enabled = 0; @@ -2593,46 +2644,67 @@ skip_ht40: } #endif /* CONFIG_VHT_OVERRIDES */ +skip_to_6ghz: + vht_freq = *freq; + + /* 6 GHz does not have VHT enabled, so allow that exception here. */ vht_freq.vht_enabled = vht_supported(mode); - if (!vht_freq.vht_enabled) + if (!vht_freq.vht_enabled && !is_6ghz) return; /* Enable HE with VHT for 5 GHz */ freq->he_enabled = mode->he_capab[ieee80211_mode].he_supported; /* setup center_freq1, bandwidth */ - for (j = 0; j < ARRAY_SIZE(vht80); j++) { - if (freq->channel >= vht80[j] && - freq->channel < vht80[j] + 16) + for (j = 0; j < ARRAY_SIZE(bw80); j++) { + if (freq->freq >= bw80[j] && + freq->freq < bw80[j] + 80) break; } - if (j == ARRAY_SIZE(vht80)) + if (j == ARRAY_SIZE(bw80) || + ieee80211_freq_to_chan(bw80[j], &channel) == NUM_HOSTAPD_MODES) return; - for (i = vht80[j]; i < vht80[j] + 16; i += 4) { - struct hostapd_channel_data *chan; + /* Back to HT configuration if channel not usable */ + if (!ibss_mesh_is_80mhz_avail(channel, mode)) + return; - chan = hw_get_channel_chan(mode, i, NULL); - if (!chan) - return; + chwidth = CHANWIDTH_80MHZ; + seg0 = channel + 6; + seg1 = 0; - /* Back to HT configuration if channel not usable */ - if (chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR)) + if ((mode->he_capab[ieee80211_mode].phy_cap[ + HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] & + HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G) && is_6ghz) { + /* In 160 MHz, the initial four 20 MHz channels were validated + * above; check the remaining four 20 MHz channels for the total + * of 160 MHz bandwidth. + */ + if (!ibss_mesh_is_80mhz_avail(channel + 16, mode)) return; - } - chwidth = CHANWIDTH_80MHZ; - seg0 = vht80[j] + 6; - seg1 = 0; + for (j = 0; j < ARRAY_SIZE(bw160); j++) { + if (freq->freq == bw160[j]) { + chwidth = CHANWIDTH_160MHZ; + seg0 = channel + 14; + break; + } + } + } if (ssid->max_oper_chwidth == CHANWIDTH_80P80MHZ) { /* setup center_freq2, bandwidth */ - for (k = 0; k < ARRAY_SIZE(vht80); k++) { + for (k = 0; k < ARRAY_SIZE(bw80); k++) { /* Only accept 80 MHz segments separated by a gap */ - if (j == k || abs(vht80[j] - vht80[k]) == 16) + if (j == k || abs(bw80[j] - bw80[k]) == 80) continue; - for (i = vht80[k]; i < vht80[k] + 16; i += 4) { + + if (ieee80211_freq_to_chan(bw80[k], &channel) == + NUM_HOSTAPD_MODES) + return; + + for (i = channel; i < channel + 16; i += 4) { struct hostapd_channel_data *chan; chan = hw_get_channel_chan(mode, i, NULL); @@ -2646,9 +2718,10 @@ skip_ht40: /* Found a suitable second segment for 80+80 */ chwidth = CHANWIDTH_80P80MHZ; - vht_caps |= - VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; - seg1 = vht80[k] + 6; + if (!is_6ghz) + vht_caps |= + VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; + seg1 = channel + 6; } if (chwidth == CHANWIDTH_80P80MHZ) @@ -2666,7 +2739,7 @@ skip_ht40: } } else if (ssid->max_oper_chwidth == CHANWIDTH_USE_HT) { chwidth = CHANWIDTH_USE_HT; - seg0 = vht80[j] + 2; + seg0 = channel + 2; #ifdef CONFIG_HT_OVERRIDES if (ssid->disable_ht40) seg0 = 0; @@ -2786,6 +2859,54 @@ int wpa_is_fils_sk_pfs_supported(struct wpa_supplicant *wpa_s) #endif /* CONFIG_FILS */ +static int wpas_populate_wfa_capa(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, + u8 *wpa_ie, size_t wpa_ie_len, + size_t max_wpa_ie_len) +{ + struct wpabuf *wfa_ie = NULL; + u8 wfa_capa[1]; + size_t wfa_ie_len, buf_len; + + os_memset(wfa_capa, 0, sizeof(wfa_capa)); + if (wpa_s->enable_dscp_policy_capa) + wfa_capa[0] |= WFA_CAPA_QM_DSCP_POLICY; + + if (!wfa_capa[0]) + return wpa_ie_len; + + /* Wi-Fi Alliance element */ + buf_len = 1 + /* Element ID */ + 1 + /* Length */ + 3 + /* OUI */ + 1 + /* OUI Type */ + 1 + /* Capabilities Length */ + sizeof(wfa_capa); /* Capabilities */ + wfa_ie = wpabuf_alloc(buf_len); + if (!wfa_ie) + return wpa_ie_len; + + wpabuf_put_u8(wfa_ie, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(wfa_ie, buf_len - 2); + wpabuf_put_be24(wfa_ie, OUI_WFA); + wpabuf_put_u8(wfa_ie, WFA_CAPA_OUI_TYPE); + wpabuf_put_u8(wfa_ie, sizeof(wfa_capa)); + wpabuf_put_data(wfa_ie, wfa_capa, sizeof(wfa_capa)); + + wfa_ie_len = wpabuf_len(wfa_ie); + if (wpa_ie_len + wfa_ie_len <= max_wpa_ie_len) { + wpa_hexdump_buf(MSG_MSGDUMP, "WFA Capabilities element", + wfa_ie); + os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(wfa_ie), + wfa_ie_len); + wpa_ie_len += wfa_ie_len; + } + + wpabuf_free(wfa_ie); + return wpa_ie_len; +} + + static u8 * wpas_populate_assoc_ies( struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_ssid *ssid, @@ -3228,6 +3349,10 @@ pfs_fail: wpa_ie_len += wpa_s->rsnxe_len; } +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->disable_mscs_support) + goto mscs_end; +#endif /* CONFIG_TESTING_OPTIONS */ if (wpa_bss_ext_capab(bss, WLAN_EXT_CAPAB_MSCS) && wpa_s->robust_av.valid_config) { struct wpabuf *mscs_ie; @@ -3243,7 +3368,7 @@ pfs_fail: if (!mscs_ie) { wpa_printf(MSG_INFO, "MSCS: Failed to allocate MSCS IE"); - goto mscs_fail; + goto mscs_end; } wpas_populate_mscs_descriptor_ie(&wpa_s->robust_av, mscs_ie); @@ -3257,7 +3382,10 @@ pfs_fail: wpabuf_free(mscs_ie); } -mscs_fail: +mscs_end: + + wpa_ie_len = wpas_populate_wfa_capa(wpa_s, bss, wpa_ie, wpa_ie_len, + max_wpa_ie_len); if (ssid->multi_ap_backhaul_sta) { size_t multi_ap_ie_len; @@ -3868,7 +3996,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) if (ret < 0) { wpa_msg(wpa_s, MSG_INFO, "Association request to the driver " "failed"); - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SANE_ERROR_CODES) { + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_VALID_ERROR_CODES) { /* * The driver is known to mean what is saying, so we * can stop right here; the association will not @@ -3961,6 +4089,9 @@ static void wpa_supplicant_clear_connection(struct wpa_supplicant *wpa_s, eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); if (old_ssid != wpa_s->current_ssid) wpas_notify_network_changed(wpa_s); + + wpas_scs_deinit(wpa_s); + wpas_dscp_deinit(wpa_s); eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); } @@ -4728,8 +4859,13 @@ static int wpa_supplicant_set_driver(struct wpa_supplicant *wpa_s, } if (name == NULL) { - /* default to first driver in the list */ - return select_driver(wpa_s, 0); + /* Default to first successful driver in the list */ + for (i = 0; wpa_drivers[i]; i++) { + if (select_driver(wpa_s, i) == 0) + return 0; + } + /* Drivers have each reported failure, so no wpa_msg() here. */ + return -1; } do { @@ -5159,6 +5295,7 @@ wpa_supplicant_alloc(struct wpa_supplicant *parent) #ifdef CONFIG_TESTING_OPTIONS dl_list_init(&wpa_s->drv_signal_override); #endif /* CONFIG_TESTING_OPTIONS */ + dl_list_init(&wpa_s->active_scs_ids); return wpa_s; } @@ -7782,6 +7919,16 @@ int wpas_get_ssid_pmf(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) } +int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr) +{ + if (wpa_s->current_ssid == NULL || + wpa_s->wpa_state < WPA_4WAY_HANDSHAKE || + os_memcmp(addr, wpa_s->bssid, ETH_ALEN) != 0) + return 0; + return wpa_sm_pmf_enabled(wpa_s->wpa); +} + + int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s) { if (wpa_s->global->conc_pref == WPA_CONC_PREF_P2P) @@ -8139,6 +8286,9 @@ struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes, { u16 i; + if (!modes) + return NULL; + for (i = 0; i < num_modes; i++) { if (modes[i].mode != mode || !modes[i].num_channels || !modes[i].channels) diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant.conf b/contrib/wpa/wpa_supplicant/wpa_supplicant.conf index e3ae77114680..fa257f3dec1b 100644 --- a/contrib/wpa/wpa_supplicant/wpa_supplicant.conf +++ b/contrib/wpa/wpa_supplicant/wpa_supplicant.conf @@ -988,7 +988,7 @@ fast_reauth=1 # WPA3-Personal-only mode: ieee80211w=2 and key_mgmt=SAE # # ocv: whether operating channel validation is enabled -# This is a countermeasure against multi-channel man-in-the-middle attacks. +# This is a countermeasure against multi-channel on-path attacks. # Enabling this automatically also enables ieee80211w, if not yet enabled. # 0 = disabled (default) # 1 = enabled if wpa_supplicant's SME in use. Otherwise enabled only when the diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h b/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h index 60acb53c5c38..cbc955159bbe 100644 --- a/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h +++ b/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h @@ -524,6 +524,19 @@ struct robust_av_data { bool valid_config; }; +struct dscp_policy_status { + u8 id; + u8 status; +}; + +struct dscp_resp_data { + bool more; + bool reset; + bool solicited; + struct dscp_policy_status *policy; + int num_policies; +}; + #ifdef CONFIG_PASN struct pasn_fils { @@ -576,6 +589,95 @@ struct wpas_pasn { }; #endif /* CONFIG_PASN */ + +enum ip_version { + IPV4 = 4, + IPV6 = 6, +}; + + +struct ipv4_params { + struct in_addr src_ip; + struct in_addr dst_ip; + u16 src_port; + u16 dst_port; + u8 dscp; + u8 protocol; + u8 param_mask; +}; + + +struct ipv6_params { + struct in6_addr src_ip; + struct in6_addr dst_ip; + u16 src_port; + u16 dst_port; + u8 dscp; + u8 next_header; + u8 flow_label[3]; + u8 param_mask; +}; + + +struct type4_params { + u8 classifier_mask; + enum ip_version ip_version; + union { + struct ipv4_params v4; + struct ipv6_params v6; + } ip_params; +}; + + +struct type10_params { + u8 prot_instance; + u8 prot_number; + u8 *filter_value; + u8 *filter_mask; + size_t filter_len; +}; + + +struct tclas_element { + u8 user_priority; + u8 classifier_type; + union { + struct type4_params type4_param; + struct type10_params type10_param; + } frame_classifier; +}; + + +struct scs_desc_elem { + u8 scs_id; + enum scs_request_type request_type; + u8 intra_access_priority; + bool scs_up_avail; + struct tclas_element *tclas_elems; + unsigned int num_tclas_elem; + u8 tclas_processing; +}; + + +struct scs_robust_av_data { + struct scs_desc_elem *scs_desc_elems; + unsigned int num_scs_desc; +}; + + +enum scs_response_status { + SCS_DESC_SENT = 0, + SCS_DESC_SUCCESS = 1, +}; + + +struct active_scs_elem { + struct dl_list list; + u8 scs_id; + enum scs_response_status status; +}; + + /** * struct wpa_supplicant - Internal data for wpa_supplicant interface * @@ -1038,6 +1140,7 @@ struct wpa_supplicant { unsigned int p2p_disable_ip_addr_req:1; unsigned int p2ps_method_config_any:1; unsigned int p2p_cli_probe:1; + unsigned int p2p_go_allow_dfs:1; enum hostapd_hw_mode p2p_go_acs_band; int p2p_persistent_go_freq; int p2p_persistent_id; @@ -1402,6 +1505,19 @@ struct wpa_supplicant { struct wpas_pasn pasn; struct wpa_radio_work *pasn_auth_work; #endif /* CONFIG_PASN */ + struct scs_robust_av_data scs_robust_av_req; + u8 scs_dialog_token; +#ifdef CONFIG_TESTING_OPTIONS + unsigned int disable_scs_support:1; + unsigned int disable_mscs_support:1; +#endif /* CONFIG_TESTING_OPTIONS */ + struct dl_list active_scs_ids; + bool ongoing_scs_req; + u8 dscp_req_dialog_token; + u8 dscp_query_dialog_token; + unsigned int enable_dscp_policy_capa:1; + unsigned int connection_dscp:1; + unsigned int wait_for_dscp_req:1; }; @@ -1427,6 +1543,8 @@ int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s); int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s); int wpa_supplicant_update_bridge_ifname(struct wpa_supplicant *wpa_s, const char *bridge_ifname); +void wpas_set_mgmt_group_cipher(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, struct wpa_ie_data *ie); int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_ssid *ssid, u8 *wpa_ie, size_t *wpa_ie_len); @@ -1580,7 +1698,7 @@ void wpas_update_mbo_connect_params(struct wpa_supplicant *wpa_s); /* op_classes.c */ enum chan_allowed { - NOT_ALLOWED, NO_IR, ALLOWED + NOT_ALLOWED, NO_IR, RADAR, ALLOWED }; enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, u8 op_class, @@ -1667,6 +1785,7 @@ static inline int wpas_mode_to_ieee80211_mode(enum wpas_mode mode) int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); int wpas_get_ssid_pmf(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); +int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr); int wpas_init_ext_pw(struct wpa_supplicant *wpa_s); @@ -1734,6 +1853,23 @@ void wpas_handle_robust_av_recv_action(struct wpa_supplicant *wpa_s, size_t len); void wpas_handle_assoc_resp_mscs(struct wpa_supplicant *wpa_s, const u8 *bssid, const u8 *ies, size_t ies_len); +int wpas_send_scs_req(struct wpa_supplicant *wpa_s); +void free_up_tclas_elem(struct scs_desc_elem *elem); +void free_up_scs_desc(struct scs_robust_av_data *data); +void wpas_handle_robust_av_scs_recv_action(struct wpa_supplicant *wpa_s, + const u8 *src, const u8 *buf, + size_t len); +void wpas_scs_deinit(struct wpa_supplicant *wpa_s); +void wpas_handle_qos_mgmt_recv_action(struct wpa_supplicant *wpa_s, + const u8 *src, + const u8 *buf, size_t len); +void wpas_dscp_deinit(struct wpa_supplicant *wpa_s); +int wpas_send_dscp_response(struct wpa_supplicant *wpa_s, + struct dscp_resp_data *resp_data); +void wpas_handle_assoc_resp_qos_mgmt(struct wpa_supplicant *wpa_s, + const u8 *ies, size_t ies_len); +int wpas_send_dscp_query(struct wpa_supplicant *wpa_s, const char *domain_name, + size_t domain_name_length); int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s, const u8 *bssid, int akmp, int cipher, diff --git a/contrib/wpa/wpa_supplicant/wpas_glue.c b/contrib/wpa/wpa_supplicant/wpas_glue.c index 96818697882f..17fc05bcbdab 100644 --- a/contrib/wpa/wpa_supplicant/wpas_glue.c +++ b/contrib/wpa/wpa_supplicant/wpas_glue.c @@ -780,6 +780,7 @@ static int wpa_supplicant_tdls_peer_addset( const struct ieee80211_vht_capabilities *vht_capab, const struct ieee80211_he_capabilities *he_capab, size_t he_capab_len, + const struct ieee80211_he_6ghz_band_cap *he_6ghz_he_capab, u8 qosinfo, int wmm, const u8 *ext_capab, size_t ext_capab_len, const u8 *supp_channels, size_t supp_channels_len, const u8 *supp_oper_classes, size_t supp_oper_classes_len) @@ -805,6 +806,7 @@ static int wpa_supplicant_tdls_peer_addset( params.vht_capabilities = vht_capab; params.he_capab = he_capab; params.he_capab_len = he_capab_len; + params.he_6ghz_capab = he_6ghz_he_capab; params.qosinfo = qosinfo; params.listen_interval = 0; params.supp_rates = supp_rates; |